评论

在小程序里面播放背景音乐要怎么实现呢

简单记录如何优雅使用wx.createWebAudioContext

之前分享了一篇放烟花的源码文章,里面有一段关于音频播放的代码,看起来感觉挺优雅,于是在自己的答题小程序开发游戏音效的时候也借鉴了一下,在这里分享出来,希望可以对大家有所帮助和启发

wx.createWebAudioContext官方的文档链接在这里 https://developers.weixin.qq.com/miniprogram/dev/api/media/audio/wx.createWebAudioContext.html

首先是封装一个音频处理器soundManager.js,这里面声明webAudio上下文、一些会使用到的音频资源(sources)和一些操作音频的方法,比如加载(preload)、播放(playSound)、暂停(pauseSound)

因为游戏音效一般是允许用户自己开关,因此还定义了静音类型,比如关闭游戏音乐是不播放背景音乐,关闭游戏音效则是不播放答对、答错、获胜、战败等音效

具体音频处理器的代码如下:

const api = require('api.js');

const soundManager = {
    //静音缓存
    muteCacheKey: 'SOUND.MUTE.',
    //音频云储存文件基础路径
    baseURL: 'https://7072-prod-xxxxxxx.tcb.qcloud.la/static/earn/sound/',
    //WebAudio上下文
    ctx: wx.createWebAudioContext(),
    //音频资源
    sources: {
        bg: {
            //音频文件名-背景音乐
            filename: "bg.mp3",
            //音量大小
            volume: 1,
            //音频的播放倍速,数值越大速度越快,默认速度1.0,有效范围为 0 < playbackRate <= 2.0(可读可写)
            playbackRate: 1,
            //是否循环播放
            loop: true,
        },
        correct: {
            //答对音效
            filename: "correct.mp3",
            volume: 1,
            playbackRate: 1,
            loop: false
        },
        wrong: {
            //答错音效
            filename: "wrong.mp3",
            volume: 1,
            playbackRate: 1,
            loop: false
        },
        victory: {
            //获胜音效
            filename: "victory.mp3",
            volume: 1,
            playbackRate: 1,
            loop: false
        },
        defeat: {
            //战败音效
            filename: "defeat.mp3",
            volume: 1,
            playbackRate: 1,
            loop: false
        }
    },

    /**
     * 可静音声音类型
     */
    muteTypes: {
        background: ["bg"],
        effect: ["correct", "wrong", "victory", "defeat"]
    },

    /**
     * 预加载音频
     * @returns {Promise[]>}
     */
    preload() {
        const allFilePromises = [];
        console.warn("预加载音频");

        /**
         * 获取素材
         * @param url
         * @returns {Promise}
         */
        function fetchMedia(url) {
            return new Promise((resolve, reject) => {
                wx.request({
                    url,
                    responseType: 'arraybuffer',
                    success: res => {
                        console.warn("获取素材成功:", url);
                        resolve(res);
                    },
                    fail: res => {
                        console.error('request fail', res)
                        reject();
                    }
                })
            })
        }

        /**
         * 检查状态
         * @param response
         * @returns {*}
         */
        function checkStatus(response) {
            if (response.statusCode >= 200 && response.statusCode < 300) {
                return response;
            }
            const customError = new Error(JSON.stringify(response));
            customError.response = response;
            throw customError;
        }

        const types = Object.keys(this.sources);
        types.forEach(type => {
            const source = this.sources[type];
            const {filename} = source;
            const fileURL = this.baseURL + filename;
            // Promise will resolve with decoded audio buffer.
            const promise = new Promise((resolve, reject) => {
                fetchMedia(fileURL)
                    .then(checkStatus)
                    .then(res => {
                        //异步解码一段资源为AudioBuffer
                        this.ctx.decodeAudioData(res.data, buffer => {
                            source.buffer = buffer;
                            resolve(buffer);
                        }, res => {
                            console.warn("======>decodeAudioData fail:", res);
                            reject(res);
                        });
                    });
            });
            allFilePromises.push(promise);
        });

        return Promise.all(allFilePromises);
    },


    /**
     * 播放
     * @param type
     * @param scale
     */
    playSound(type, scale = 1) {
        try {
            console.warn("playSound:", type);
            const source = this.sources[type];

            if (!source) {
                throw new Error(`Sound of type "${type}" doesn't exist.`);
            }

            if (this.muted(type)) {
                console.warn("muteSound:", type);
                return;
            }


            const initialVolume = source.volume || 1;
            const initialPlaybackRate = source.playbackRate || 1;

            // Volume descreases with scale.
            const scaledVolume = initialVolume * scale;
            // Playback rate increases with scale. For this, we map the scale of 0-1 to a scale of 2-1.
            // So at a scale of 1, sound plays normally, but as scale approaches 0 speed approaches double.
            const scaledPlaybackRate = initialPlaybackRate * (2 - scale);

            //创建一个GainNode
            const gainNode = this.ctx.createGain();
            gainNode.gain.value = scaledVolume;

            const {buffer} = source;
            const bufferSource = this.ctx.createBufferSource();
            bufferSource.playbackRate.value = scaledPlaybackRate;
            bufferSource.buffer = buffer;
            bufferSource.loop = source.loop;
            bufferSource.connect(gainNode);
            gainNode.connect(this.ctx.destination);
            bufferSource.start(0);

            source.bufferSource = bufferSource;
        } catch (e) {
            console.error("playSound error:", type);
        }
    },

    /**
     * 暂停播放
     * @param type
     */
    pauseSound(type) {
        console.warn("pauseSound:", type);
        const source = this.sources[type];

        if (!source) {
            throw new Error(`Sound of type "${type}" doesn't exist.`);
        }
        if (!source.bufferSource) {
            console.warn("还未播放过");
            return;
        }
        source.bufferSource.stop();
    },

    /**
     * 修改静音状态
     * @param muteType
     * @param status true/false
     */
    changeMuteStatus(muteType, status) {
        const cacheKey = this.muteCacheKey + muteType;
        if (status) {
            api.setStorage(cacheKey, new Date().getTime());
        } else {
            api.removeStorage(cacheKey);
        }
    },

    /**
     * 获取静音状态
     * @param muteType
     * @returns {any|boolean}
     */
    getMuteStatus(muteType) {
        const result = api.getStorageSync(this.muteCacheKey + muteType);
        return result && result !== "";
    },

    /**
     * 是否处于静音
     * @param type
     * @returns {any|boolean}
     */
    muted(type) {
        for (let muteType in this.muteTypes) {
            const muteTypeList = this.muteTypes[muteType];
            const match = muteTypeList.findIndex(item => item === type) >= 0;
            if (!match) {
                continue;
            }
            return this.getMuteStatus(muteType);
        }

        return false;
    }
}

module.exports = soundManager;


然后在app.js中的onLaunch加载音频资源

//预加载音频资源
soundManager.preload().then(res => console.log("音频资源预加载成功,在这里你可以之间播放比如背景音乐"));


这样需要播放音频的时候只需要这样,简简单单

const soundManager = require("utils/soundManager.js");
//播放背景音乐
soundManager.playSound("bg");
//播放答对音效
soundManager.playSound("correct");

//暂停背景音乐
soundManager.pauseSound("bg");



最后一次编辑于  01-25  
点赞 1
收藏
评论
登录 后发表内容