|
|
|
|
@@ -131,10 +131,6 @@ 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;
|
|
|
|
|
@@ -161,7 +157,6 @@ class AudioManager {
|
|
|
|
|
const begin = () => {
|
|
|
|
|
if (this.isInterrupted) return;
|
|
|
|
|
this.isInterrupted = true;
|
|
|
|
|
this.interruptedAt = Date.now();
|
|
|
|
|
this.stopAll();
|
|
|
|
|
this.isSequenceRunning = false;
|
|
|
|
|
this.sequenceQueue = [];
|
|
|
|
|
@@ -173,7 +168,6 @@ class AudioManager {
|
|
|
|
|
const end = () => {
|
|
|
|
|
if (!this.isInterrupted) return;
|
|
|
|
|
this.isInterrupted = false;
|
|
|
|
|
this.interruptedAt = 0;
|
|
|
|
|
uni.$emit(AUDIO_INTERRUPTION_END_EVENT);
|
|
|
|
|
void this.reloadAll();
|
|
|
|
|
};
|
|
|
|
|
@@ -356,14 +350,9 @@ 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);
|
|
|
|
|
|
|
|
|
|
@@ -397,13 +386,7 @@ class AudioManager {
|
|
|
|
|
}
|
|
|
|
|
this.recordLoadFailure(key);
|
|
|
|
|
this.audioMap.delete(key);
|
|
|
|
|
try {
|
|
|
|
|
audio.destroy();
|
|
|
|
|
} catch (_) {}
|
|
|
|
|
this.finishPlayback(key, {
|
|
|
|
|
advanceSequence: true,
|
|
|
|
|
emitEnded: true,
|
|
|
|
|
});
|
|
|
|
|
audio.destroy();
|
|
|
|
|
if (this.readyMap.get(key)) {
|
|
|
|
|
// 这里不要去除,不然检查进度的时候由于没有重新加载而进度卡住,等播放失败的时候会重新加载
|
|
|
|
|
// this.readyMap.set(key, false);
|
|
|
|
|
@@ -413,14 +396,19 @@ class AudioManager {
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
audio.onEnded(() => {
|
|
|
|
|
this.finishPlayback(key, {
|
|
|
|
|
advanceSequence: true,
|
|
|
|
|
emitEnded: true,
|
|
|
|
|
});
|
|
|
|
|
if (this.currentPlayingKey === key) {
|
|
|
|
|
this.currentPlayingKey = null;
|
|
|
|
|
}
|
|
|
|
|
this.allowPlayMap.set(key, false);
|
|
|
|
|
this.onAudioEnded(key);
|
|
|
|
|
uni.$emit('audioEnded', key);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
audio.onStop(() => {
|
|
|
|
|
this.finishPlayback(key);
|
|
|
|
|
if (this.currentPlayingKey === key) {
|
|
|
|
|
this.currentPlayingKey = null;
|
|
|
|
|
}
|
|
|
|
|
this.allowPlayMap.set(key, false);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
this.audioMap.set(key, audio);
|
|
|
|
|
@@ -458,19 +446,11 @@ 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();
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
@@ -507,137 +487,15 @@ 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) {
|
|
|
|
|
try {
|
|
|
|
|
oldAudio.destroy();
|
|
|
|
|
} catch (_) {}
|
|
|
|
|
}
|
|
|
|
|
if (oldAudio) oldAudio.destroy();
|
|
|
|
|
this.createAudio(key);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 播放指定音频或音频数组(数组则按顺序连续播放)
|
|
|
|
|
play(input, interrupt = true) {
|
|
|
|
|
if (this.isInterrupted) {
|
|
|
|
|
this.recoverFromInterruptionIfStale();
|
|
|
|
|
}
|
|
|
|
|
if (this.isInterrupted) {
|
|
|
|
|
debugLog("音频处理中断状态,忽略播放请求");
|
|
|
|
|
return;
|
|
|
|
|
@@ -695,9 +553,6 @@ class AudioManager {
|
|
|
|
|
|
|
|
|
|
// 内部方法:播放单个 key
|
|
|
|
|
_playSingle(key, forceStopAll = false) {
|
|
|
|
|
if (this.isInterrupted) {
|
|
|
|
|
this.recoverFromInterruptionIfStale();
|
|
|
|
|
}
|
|
|
|
|
if (this.isInterrupted) {
|
|
|
|
|
debugLog(`音频处理中断状态,跳过播放: ${key}`);
|
|
|
|
|
return;
|
|
|
|
|
@@ -706,11 +561,6 @@ 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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -753,34 +603,21 @@ class AudioManager {
|
|
|
|
|
try {
|
|
|
|
|
audio.play();
|
|
|
|
|
} catch (err) {
|
|
|
|
|
this.finishPlayback(key, {
|
|
|
|
|
advanceSequence: true,
|
|
|
|
|
emitEnded: true,
|
|
|
|
|
force: true,
|
|
|
|
|
});
|
|
|
|
|
this.allowPlayMap.set(key, false);
|
|
|
|
|
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) {
|
|
|
|
|
cleanup();
|
|
|
|
|
try {
|
|
|
|
|
uni.$off("audioLoaded", handler);
|
|
|
|
|
} catch (_) {}
|
|
|
|
|
// 再次校验是否存在且就绪
|
|
|
|
|
const a = this.audioMap.get(key);
|
|
|
|
|
if (a && this.readyMap.get(key)) {
|
|
|
|
|
@@ -791,7 +628,6 @@ class AudioManager {
|
|
|
|
|
try {
|
|
|
|
|
uni.$on("audioLoaded", handler);
|
|
|
|
|
} catch (_) {}
|
|
|
|
|
loadWaitTimer = setTimeout(cleanup, 12000);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -817,7 +653,6 @@ class AudioManager {
|
|
|
|
|
// 停止指定音频
|
|
|
|
|
stop(key) {
|
|
|
|
|
const audio = this.audioMap.get(key);
|
|
|
|
|
this.clearPlayWatchdog(key);
|
|
|
|
|
if (audio) {
|
|
|
|
|
audio.stop();
|
|
|
|
|
this.allowPlayMap.set(key, false);
|
|
|
|
|
@@ -829,7 +664,6 @@ class AudioManager {
|
|
|
|
|
|
|
|
|
|
// 停止所有音频
|
|
|
|
|
stopAll() {
|
|
|
|
|
this.clearAllPlayWatchdogs();
|
|
|
|
|
for (const [k, audio] of this.audioMap.entries()) {
|
|
|
|
|
try {
|
|
|
|
|
audio.stop();
|
|
|
|
|
@@ -903,7 +737,6 @@ class AudioManager {
|
|
|
|
|
this.readyMap.clear();
|
|
|
|
|
this.failedLoadKeys.clear();
|
|
|
|
|
this.allowPlayMap.clear();
|
|
|
|
|
this.clearAllPlayWatchdogs();
|
|
|
|
|
this.currentPlayingKey = null;
|
|
|
|
|
this.sequenceQueue = [];
|
|
|
|
|
this.sequenceIndex = 0;
|
|
|
|
|
|