import { CallStatus, NAME, CallRole } from '../const/index'; import { IBellParams } from '../interface/index'; import { isUndefined } from '../utils/common-utils'; import DEFAULT_CALLER_BELL_FILEPATH from '../assets/phone_dialing.mp3'; import DEFAULT_CALLEE_BELL_FILEPATH from '../assets/phone_ringing.mp3'; export class BellContext { private _bellContext: any = null; private _isMuteBell: boolean = false; private _calleeBellFilePath: string = DEFAULT_CALLEE_BELL_FILEPATH; private _callRole: string = CallRole.UNKNOWN; private _callStatus: string = CallStatus.IDLE; private _isPlaying: boolean = false; constructor() { // @ts-ignore this._bellContext = wx.createInnerAudioContext({ useWebAudioImplement: false }); this._addListenBellContextEvent(); this._bellContext.loop = true; } setBellSrc() { // @ts-ignore const fs = wx.getFileSystemManager(); try { let playBellFilePath = DEFAULT_CALLER_BELL_FILEPATH; if (this._callRole === CallRole.CALLEE) { playBellFilePath = this._calleeBellFilePath || DEFAULT_CALLEE_BELL_FILEPATH; } fs.readFileSync(playBellFilePath, 'utf8', 0); this._bellContext.src = playBellFilePath; } catch (error) { console.warn(`${NAME.PREFIX}Failed to setBellSrc, ${error}`); } } setBellProperties(bellParams: IBellParams) { this._callRole = bellParams.callRole || this._callRole; this._callStatus = bellParams.callStatus || this._callStatus; this._calleeBellFilePath = bellParams.calleeBellFilePath || this._calleeBellFilePath; // undefined/false || isMuteBell => isMuteBell (不符合预期) this._isMuteBell = isUndefined(bellParams.isMuteBell) ? this._isMuteBell : bellParams.isMuteBell; } async play() { try { if (this._callStatus !== CallStatus.CALLING) { return; } // iOS 优化:增加更严格的状态检查 if (this._callStatus !== CallStatus.CALLING || this._isPlaying) { console.warn(`${NAME.PREFIX}play skipped, callStatus: ${this._callStatus}, isPlaying: ${this._isPlaying}`); return; } this._isPlaying = true; this.setBellSrc(); if (this._callRole === CallRole.CALLEE && !this._isMuteBell) { // 再次检查状态,避免在设置过程中状态已变更 if (!this._isPlaying || this._callStatus !== CallStatus.CALLING) return; await this._bellContext.play(); return; } if (this._callRole === CallRole.CALLER) { // 再次检查状态,避免在设置过程中状态已变更 if (!this._isPlaying || this._callStatus !== CallStatus.CALLING) return; await this._bellContext.play(); return; } } catch (error) { console.warn(`${NAME.PREFIX}Failed to play audio file, ${error}`); } } async stop() { try { this._isPlaying = false; // iOS 小程序音频停止优化:先暂停再停止,并添加延时确保操作完成 if (this._bellContext) { // 先暂停音频 this._bellContext.pause(); // iOS 需要短暂延时确保暂停操作完成 await this._delay(100); // 再停止音频 this._bellContext.stop(); // 重置音频源,确保彻底停止 this._bellContext.src = ''; // 再次延时确保所有操作完成 await this._delay(100); } } catch (error) { console.warn(`${NAME.PREFIX}Failed to stop audio file, ${error}`); } } async setBellMute(enable: boolean) { if (this._callStatus !== CallStatus.CALLING && this._callRole !== CallRole.CALLEE) { return; } if (enable) { await this.stop(); } else { await this.play(); } } destroy() { try { this._isMuteBell = false; this._calleeBellFilePath = ''; this._callRole = CallRole.UNKNOWN; this._callStatus = CallStatus.IDLE; this._isPlaying = false; this?._removeListenBellContextEvent(); this._bellContext.destroy(); this._bellContext = null; } catch (error) { console.warn(`${NAME.PREFIX}Failed to destroy, ${error}`); } } private _delay(ms: number): any { return new Promise((resolve: any) => setTimeout(resolve, ms)); } private _delayCallback(callback: () => void, ms: number) { setTimeout(callback, ms); } private _handleAudioInterruptionBegin = async () => { await this.stop(); }; private _handleAudioInterruptionEnd = async () => { if (this._callStatus !== CallStatus.CALLING) { await this.stop(); } else { // 音频中断结束后,延时一下再重新播放,确保系统音频资源释放完成 this._delayCallback(async () => { if (this._callStatus === CallStatus.CALLING && !this._isPlaying) { await this.play(); } }, 100); } }; private _addListenBellContextEvent() { this._bellContext.onPlay(() => { if (!this._isPlaying) { this._bellContext?.pause(); this._bellContext?.stop(); } }); // @ts-ignore wx.onAudioInterruptionBegin(this._handleAudioInterruptionBegin); // @ts-ignore wx.onAudioInterruptionEnd(this._handleAudioInterruptionEnd); } private _removeListenBellContextEvent() { // @ts-ignore wx.offAudioInterruptionBegin(this._handleAudioInterruptionBegin); // @ts-ignore wx.offAudioInterruptionEnd(this._handleAudioInterruptionEnd); } }