From 045827cb33f66512b2427ea50c9b8784f562fa02 Mon Sep 17 00:00:00 2001 From: zhangyi <690096405@qq.com> Date: Mon, 15 Jun 2026 15:35:04 +0800 Subject: [PATCH] =?UTF-8?q?update:=E4=BC=98=E5=8C=96=E8=AF=AD=E9=9F=B3?= =?UTF-8?q?=E6=92=AD=E6=8A=A5=E6=B5=81=E7=A8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/audioManager.js | 199 +++++++++++++++++++++++++++++--- src/pages/team-battle/index.vue | 5 +- 2 files changed, 187 insertions(+), 17 deletions(-) diff --git a/src/audioManager.js b/src/audioManager.js index a0f55bb..242373d 100644 --- a/src/audioManager.js +++ b/src/audioManager.js @@ -131,6 +131,10 @@ class AudioManager { this.lastPlayKey = null; this.lastPlayAt = 0; this.isInterrupted = false; + this.interruptedAt = 0; + this.interruptionFallbackMs = 5000; + this.playWatchdogMs = 8000; + this.playWatchdogTimers = new Map(); // 静音开关 this.isMuted = false; @@ -157,6 +161,7 @@ class AudioManager { const begin = () => { if (this.isInterrupted) return; this.isInterrupted = true; + this.interruptedAt = Date.now(); this.stopAll(); this.isSequenceRunning = false; this.sequenceQueue = []; @@ -168,6 +173,7 @@ class AudioManager { const end = () => { if (!this.isInterrupted) return; this.isInterrupted = false; + this.interruptedAt = 0; uni.$emit(AUDIO_INTERRUPTION_END_EVENT); void this.reloadAll(); }; @@ -350,9 +356,14 @@ class AudioManager { const loadTimeout = setTimeout(() => { debugLog(`音频 ${key} 加载超时`); this.recordLoadFailure(key); + this.audioMap.delete(key); try { audio.destroy(); } catch (_) {} + this.finishPlayback(key, { + advanceSequence: true, + emitEnded: true, + }); if (callback) callback(); }, 10000); @@ -386,7 +397,13 @@ class AudioManager { } this.recordLoadFailure(key); this.audioMap.delete(key); - audio.destroy(); + try { + audio.destroy(); + } catch (_) {} + this.finishPlayback(key, { + advanceSequence: true, + emitEnded: true, + }); if (this.readyMap.get(key)) { // 这里不要去除,不然检查进度的时候由于没有重新加载而进度卡住,等播放失败的时候会重新加载 // this.readyMap.set(key, false); @@ -396,19 +413,14 @@ class AudioManager { }); audio.onEnded(() => { - if (this.currentPlayingKey === key) { - this.currentPlayingKey = null; - } - this.allowPlayMap.set(key, false); - this.onAudioEnded(key); - uni.$emit('audioEnded', key); + this.finishPlayback(key, { + advanceSequence: true, + emitEnded: true, + }); }); audio.onStop(() => { - if (this.currentPlayingKey === key) { - this.currentPlayingKey = null; - } - this.allowPlayMap.set(key, false); + this.finishPlayback(key); }); this.audioMap.set(key, audio); @@ -446,11 +458,19 @@ class AudioManager { }); } else { this.recordLoadFailure(key); + this.finishPlayback(key, { + advanceSequence: true, + emitEnded: true, + }); if (callback) callback(); } }, fail: () => { this.recordLoadFailure(key); + this.finishPlayback(key, { + advanceSequence: true, + emitEnded: true, + }); if (callback) callback(); }, }); @@ -487,15 +507,137 @@ class AudioManager { this.failedLoadKeys.add(key); } + clearPlayWatchdog(key) { + const timer = this.playWatchdogTimers.get(key); + if (timer) { + clearTimeout(timer); + this.playWatchdogTimers.delete(key); + } + } + + clearAllPlayWatchdogs() { + for (const timer of this.playWatchdogTimers.values()) { + clearTimeout(timer); + } + this.playWatchdogTimers.clear(); + } + + startPlayWatchdog(key) { + this.clearPlayWatchdog(key); + const timer = setTimeout(() => { + if (this.currentPlayingKey !== key) return; + debugLog(`音频 ${key} 播放超时,跳过当前音频并继续队列`); + this.finishPlayback(key, { + advanceSequence: true, + emitEnded: true, + force: true, + }); + this.reloadAudioKey(key); + }, this.playWatchdogMs); + this.playWatchdogTimers.set(key, timer); + } + + finishPlayback(key, { advanceSequence = false, emitEnded = false, force = false } = {}) { + const wasCurrent = this.currentPlayingKey === key; + const isSequenceCurrent = + this.isSequenceRunning && this.sequenceQueue[this.sequenceIndex] === key; + + this.clearPlayWatchdog(key); + this.allowPlayMap.set(key, false); + + if (!force && !wasCurrent && !isSequenceCurrent) return false; + + if (wasCurrent) { + this.currentPlayingKey = null; + } + + if (advanceSequence && isSequenceCurrent) { + this.onAudioEnded(key); + } + + if (emitEnded) { + uni.$emit("audioEnded", key); + } + + return true; + } + + recoverFromInterruptionIfStale(force = false) { + if (!this.isInterrupted) return false; + const interruptedFor = Date.now() - (this.interruptedAt || Date.now()); + if (!force && interruptedFor < this.interruptionFallbackMs) return false; + + debugLog("音频中断状态超时,执行兜底恢复"); + this.isInterrupted = false; + this.interruptedAt = 0; + uni.$emit(AUDIO_INTERRUPTION_END_EVENT); + void this.reloadAll(); + return true; + } + + recoverIfStale(expectedKey) { + if (this.recoverFromInterruptionIfStale(true)) return; + + const key = + expectedKey || this.currentPlayingKey || this.sequenceQueue[this.sequenceIndex]; + if (!key) { + if (this.isSequenceRunning) { + this.sequenceQueue = []; + this.sequenceIndex = 0; + this.isSequenceRunning = false; + } + return; + } + + const isStaleCurrent = + this.currentPlayingKey === key || + (this.isSequenceRunning && this.sequenceQueue[this.sequenceIndex] === key); + if (!isStaleCurrent) return; + + debugLog(`音频 ${key} 等待超时,执行轻量恢复`); + const audio = this.audioMap.get(key); + if (audio) { + try { + audio.stop(); + } catch (_) {} + } + this.finishPlayback(key, { + advanceSequence: true, + emitEnded: true, + force: true, + }); + this.reloadAudioKey(key); + } + + reloadAudioKey(key) { + const audio = this.audioMap.get(key); + if (audio) { + try { + audio.destroy(); + } catch (_) {} + this.audioMap.delete(key); + } + this.readyMap.set(key, false); + this.retryLoadAudio(key); + } + // 重新加载音频 retryLoadAudio(key) { + this.clearPlayWatchdog(key); const oldAudio = this.audioMap.get(key); - if (oldAudio) oldAudio.destroy(); + if (oldAudio) { + try { + oldAudio.destroy(); + } catch (_) {} + } this.createAudio(key); } // 播放指定音频或音频数组(数组则按顺序连续播放) play(input, interrupt = true) { + if (this.isInterrupted) { + this.recoverFromInterruptionIfStale(); + } if (this.isInterrupted) { debugLog("音频处理中断状态,忽略播放请求"); return; @@ -553,6 +695,9 @@ class AudioManager { // 内部方法:播放单个 key _playSingle(key, forceStopAll = false) { + if (this.isInterrupted) { + this.recoverFromInterruptionIfStale(); + } if (this.isInterrupted) { debugLog(`音频处理中断状态,跳过播放: ${key}`); return; @@ -561,6 +706,11 @@ class AudioManager { const now = Date.now(); if (this.lastPlayKey === key && now - this.lastPlayAt < 250) { debugLog(`忽略快速重复播放: ${key}`); + this.finishPlayback(key, { + advanceSequence: true, + emitEnded: true, + force: true, + }); return; } @@ -603,21 +753,34 @@ class AudioManager { try { audio.play(); } catch (err) { - this.allowPlayMap.set(key, false); + this.finishPlayback(key, { + advanceSequence: true, + emitEnded: true, + force: true, + }); debugLog(`音频 ${key} 播放调用失败`, err?.errMsg || err); return; } this.currentPlayingKey = key; this.lastPlayKey = key; this.lastPlayAt = Date.now(); + this.startPlayWatchdog(key); } else { debugLog(`音频 ${key} 不存在,尝试重新加载...`); this.retryLoadAudio(key); + let loadWaitTimer = null; + const cleanup = () => { + try { + uni.$off("audioLoaded", handler); + } catch (_) {} + if (loadWaitTimer) { + clearTimeout(loadWaitTimer); + loadWaitTimer = null; + } + }; const handler = (loadedKey) => { if (loadedKey === key) { - try { - uni.$off("audioLoaded", handler); - } catch (_) {} + cleanup(); // 再次校验是否存在且就绪 const a = this.audioMap.get(key); if (a && this.readyMap.get(key)) { @@ -628,6 +791,7 @@ class AudioManager { try { uni.$on("audioLoaded", handler); } catch (_) {} + loadWaitTimer = setTimeout(cleanup, 12000); } } @@ -653,6 +817,7 @@ class AudioManager { // 停止指定音频 stop(key) { const audio = this.audioMap.get(key); + this.clearPlayWatchdog(key); if (audio) { audio.stop(); this.allowPlayMap.set(key, false); @@ -664,6 +829,7 @@ class AudioManager { // 停止所有音频 stopAll() { + this.clearAllPlayWatchdogs(); for (const [k, audio] of this.audioMap.entries()) { try { audio.stop(); @@ -737,6 +903,7 @@ class AudioManager { this.readyMap.clear(); this.failedLoadKeys.clear(); this.allowPlayMap.clear(); + this.clearAllPlayWatchdogs(); this.currentPlayingKey = null; this.sequenceQueue = []; this.sequenceIndex = 0; diff --git a/src/pages/team-battle/index.vue b/src/pages/team-battle/index.vue index cc50c45..5ffa5b1 100644 --- a/src/pages/team-battle/index.vue +++ b/src/pages/team-battle/index.vue @@ -432,7 +432,10 @@ function playAudioKeys(keys, { interrupt = false, timeout } = {}) { resolve(); }, }; - const timer = setTimeout(waiter.done, waitTime); + const timer = setTimeout(() => { + audioManager.recoverIfStale(expectedKey); + waiter.done(); + }, waitTime); audioWaiters.add(waiter); audioManager.play(audioKeys, interrupt); });