H5 微信小游戏 —— 音频管理器

前言

原本使用的是 egret 的 egret.SoundChannelegret.Sound 来管理音频,但不知为何在重复将游戏切换到前后台后,很容易出现背景音播放不全、播放重复和无法播放的问题,懒得再去排查原因干脆使用小游戏提供的音频播放 API 重写了一个音频管理工具。

API 相关

参考官方文档 教程/音频播放 ,大致了解音频播放是利用微信接口 wx.createInnerAudioContext() 创建 InnerAudioContext 对象作为唯一实现方式,此外,同时播放的音频实例在 Android 平台上有 10 个的上限限制,对于不再使用的音频实例可以通过 InnerAudioContext.destroy() 接口销毁。

大致思路

管理器可以设计为单例模式,其中包含:

  • 一个播放音效的音频实例池,并设置池子的容量为 9 (留一个给背景音乐播放使用);

  • 一个单独用于播放背景音乐的音频实例(不放入池内管理)

创建管理器时可以预创建少量实例在实例池中,每次播放音效的步骤:

  • 从池内获取可用的(状态 paused == true)播放实例;

  • 当实例用完但还未达到池子容量时,可创建新的实例并放在池内;

  • 当无可用且池子已满时,停掉最早创建的音频实例,用于播放新的音频(视音效的重要性的选择,以新音效为重)。

为了防止每次都重新下载音频,需要结合资源缓存机制,即播放一个新的音频时,先将音频下载到本地,再将本地文件地址赋值给 .src 属性,再播放音频。

实现代码

class WeChatSoundManager {
    private static _instance: WeChatSoundManager;
    public static get instance() {
        if (!this._instance) {
            this._instance = new WeChatSoundManager();
        }
        return this._instance;
    }
    private _bgmCtx = null;
    private _bgmPath = "";

    private soundCtxPool = new Array<any>();
    // 预创建实例
    private PreBuildNum = 3;
    private MaxSoundNum = 10;
    // 实例使用指针
    private _index = 0;

    public constructor() {
        let soundCtx = null;
        for (let i = 0; i++; i < this.PreBuildNum) {
            soundCtx = wx.createInnerAudioContext();
            this.soundCtxPool.push(soundCtx);
        }
    }

    public clean() {
        this.soundCtxPool = new Array<any>();
        this._index = 0;
        this._bgmCtx = null;
        this._bgmPath = "";
    }

    // 播放背景音乐
    public playBGM(bgmPath: string) {
        //if (gGame.chanelType != ChanelType.WXMini) return;
        gLog('背景音乐播放地址:' + bgmPath);
        if (this._bgmPath == bgmPath && this._bgmCtx != null) {
            this._bgmCtx.play();
        } else {
            this._bgmPath = bgmPath;
            if (!this._bgmCtx) {
                this._bgmCtx = wx.createInnerAudioContext();
            }
            this._bgmCtx.src = bgmPath;
            this._bgmCtx.loop = true;
            this._bgmCtx.autoplay = true;
        }
    }

    // 背景音乐是否正在播放
    public isBGMPlaying() {
        if (this._bgmCtx) {
            return !this._bgmCtx.paused;
        }
        return false;
    }

    // 暂停背景音乐
    public pauseBGM() {
        if (this._bgmCtx) {
            this._bgmCtx.pause();
        }
    }

    // 恢复背景音乐
    public resumeBGM() {
        if (this._bgmCtx) {
            this._bgmCtx.play();
        } else {
            if (this._bgmPath) {
                this.playBGM(this._bgmPath);
            }
        }
    }

    // 停止背景音乐
    public stopBGM() {
        if (this._bgmCtx) {
            this._bgmCtx.stop();
            this._bgmCtx.destroy();
            this._bgmCtx = null;
        }
    }

    // 播放音频
    public playSound(soundPath: string) {
        //if (gGame.chanelType != ChanelType.WXMini) return;
        gLog('音频播放地址:' + soundPath);
        let soundCtx = this.getSoundCtx();
        if (soundCtx) {
            soundCtx.src = soundPath;
            soundCtx.stop();
            soundCtx.play();
        }
    }

    public stopAllSound() {
        this.soundCtxPool.forEach(soundCtx => {
            // 暂停或停止了
            if (!soundCtx.paused) {
                soundCtx.stop();
            }
        });
    }

    // 获取一个音频实例
    private getSoundCtx() {
        let soundCtx = null;
        for (let i = 0; i++; i < this.soundCtxPool.length) {
            soundCtx = this.soundCtxPool[i];
            // 暂停或停止了
            if (soundCtx.paused) {
                return soundCtx;
            }
        }
        if (!soundCtx) {
            // 留一个实例用于播放背景音乐
            if (this.soundCtxPool.length < this.MaxSoundNum - 1) {
                soundCtx = wx.createInnerAudioContext();
            } else {
                soundCtx = this.soundCtxPool[this._index];
                this._index++;
                if (this._index > this.MaxSoundNum) {
                    this._index = 0;
                }
                soundCtx.stop();
            }
        }
        return soundCtx;
    }
}

调用方式:


WeChatSoundManager.instance.playBGM(url);   // 播放背景音乐
WeChatSoundManager.instance.playSound(url); // 播放普通音效

其他

在手机有外部事件需要暂停音频和事件结束恢复音频播放,分别通过游戏中全局监听 wx.onAudioInterruptionBeginwx.onAudioInterruptionEnd 两个事件来实现,当然为了方便和防止重复创建,可以将其写在音频管理器的构建方法中,如下:

    public constructor() {
        let soundCtx = null;
        for (let i = 0; i++; i < this.PreBuildNum) {
            soundCtx = wx.createInnerAudioContext();
            this.soundCtxPool.push(soundCtx);
        }
        // 中断事件开始
        wx.onAudioInterruptionBegin(() => {
            if (this.musicEnabled) {
                gLog('------------ 暂停背景音乐');
                this.pauseBGM();
            }
        });
        // 中断事件结束
        wx.onAudioInterruptionEnd(() => {
            if (this.musicEnabled) {
                gLog('------------ 恢复背景音乐');
                this.resumeBGM();
            }
        });
    }

参考

猜你喜欢

转载自blog.csdn.net/linshuhe1/article/details/80985964
今日推荐