2025-11-06 10:52:08 +08:00
|
|
|
|
export const audioFils = {
|
2025-10-29 18:07:42 +08:00
|
|
|
|
激光已校准:
|
|
|
|
|
|
"https://static.shelingxingqiu.com/attachment/2025-10-29/ddupaur1vdkyhzaqdc.mp3",
|
2025-09-18 09:28:04 +08:00
|
|
|
|
胜利: "https://static.shelingxingqiu.com/attachment/2025-09-17/dcuo9yjp0kt5msvmvd.mp3",
|
|
|
|
|
|
失败: "https://static.shelingxingqiu.com/attachment/2025-09-17/dcuo9yht2sdwhuqygy.mp3",
|
2025-09-15 11:23:22 +08:00
|
|
|
|
请射箭测试距离:
|
2025-09-18 09:28:04 +08:00
|
|
|
|
"https://static.shelingxingqiu.com/attachment/2025-09-17/dcuvj8avzqyw4hpq7t.mp3",
|
2025-09-15 11:23:22 +08:00
|
|
|
|
距离合格:
|
2025-09-18 09:28:04 +08:00
|
|
|
|
"https://static.shelingxingqiu.com/attachment/2025-09-17/dcutwrda0amn5kqr4j.mp3",
|
2025-09-15 11:23:22 +08:00
|
|
|
|
距离不足:
|
2025-09-18 09:28:04 +08:00
|
|
|
|
"https://static.shelingxingqiu.com/attachment/2025-09-17/dcutya57qurnsj6pg4.mp3",
|
2025-09-15 11:23:22 +08:00
|
|
|
|
轮到你了:
|
2025-09-18 09:28:04 +08:00
|
|
|
|
"https://static.shelingxingqiu.com/attachment/2025-09-17/dcutzdrn4lxcpv8aqr.mp3",
|
2025-07-14 16:37:56 +08:00
|
|
|
|
第一轮:
|
2025-09-18 09:28:04 +08:00
|
|
|
|
"https://static.shelingxingqiu.com/attachment/2025-09-17/dcutyyr9a7m1vz2w13.mp3",
|
2025-07-14 16:37:56 +08:00
|
|
|
|
第二轮:
|
2025-09-18 09:28:04 +08:00
|
|
|
|
"https://static.shelingxingqiu.com/attachment/2025-09-17/dcutyyr9ldnfexjxtw.mp3",
|
2025-07-14 16:37:56 +08:00
|
|
|
|
第三轮:
|
2025-09-18 09:28:04 +08:00
|
|
|
|
"https://static.shelingxingqiu.com/attachment/2025-09-17/dcutyyr97m4ipxaze4.mp3",
|
2025-07-14 16:37:56 +08:00
|
|
|
|
第四轮:
|
2025-09-18 09:28:04 +08:00
|
|
|
|
"https://static.shelingxingqiu.com/attachment/2025-09-17/dcutyyr9x5addohlzf.mp3",
|
2025-07-14 16:37:56 +08:00
|
|
|
|
第五轮:
|
2025-09-18 09:28:04 +08:00
|
|
|
|
"https://static.shelingxingqiu.com/attachment/2025-09-17/dcutyyr9d7lw2gebpv.mp3",
|
2025-07-14 16:37:56 +08:00
|
|
|
|
决金箭轮:
|
2025-09-18 09:28:04 +08:00
|
|
|
|
"https://static.shelingxingqiu.com/attachment/2025-09-17/dcutwrd9zs4oi2kujv.mp3",
|
2025-08-28 19:34:24 +08:00
|
|
|
|
请蓝方射箭:
|
2025-09-18 09:28:04 +08:00
|
|
|
|
"https://static.shelingxingqiu.com/attachment/2025-09-17/dcutzdrxcbe5ll46as.mp3",
|
2025-08-28 19:34:24 +08:00
|
|
|
|
请红方射箭:
|
2025-09-18 09:28:04 +08:00
|
|
|
|
"https://static.shelingxingqiu.com/attachment/2025-09-17/dcutzdrl3re3dhlfjd.mp3",
|
2025-07-14 16:37:56 +08:00
|
|
|
|
中场休息:
|
2025-09-18 09:28:04 +08:00
|
|
|
|
"https://static.shelingxingqiu.com/attachment/2025-09-17/dcutwrd9zdk1xyolst.mp3",
|
2025-07-14 16:37:56 +08:00
|
|
|
|
比赛结束:
|
2025-09-18 09:28:04 +08:00
|
|
|
|
"https://static.shelingxingqiu.com/attachment/2025-09-17/dcutya59b6pu0ur4um.mp3",
|
2025-07-14 16:37:56 +08:00
|
|
|
|
比赛开始:
|
2025-09-18 09:28:04 +08:00
|
|
|
|
"https://static.shelingxingqiu.com/attachment/2025-09-17/dcuu5z3a3lumkutske.mp3",
|
2025-07-14 16:37:56 +08:00
|
|
|
|
请开始射击:
|
2025-09-18 09:28:04 +08:00
|
|
|
|
"https://static.shelingxingqiu.com/attachment/2025-09-17/dcutzdrl5u0iromqhf.mp3",
|
2025-09-15 11:23:22 +08:00
|
|
|
|
射击无效:
|
2025-09-18 09:28:04 +08:00
|
|
|
|
"https://static.shelingxingqiu.com/attachment/2025-09-17/dcutya55ufiiw8oo55.mp3",
|
2025-07-14 16:37:56 +08:00
|
|
|
|
未上靶:
|
2025-09-18 09:28:04 +08:00
|
|
|
|
"https://static.shelingxingqiu.com/attachment/2025-09-17/dcuuznjc78ljhzuw1o.mp3",
|
2025-07-14 16:37:56 +08:00
|
|
|
|
"1环":
|
2025-09-18 09:28:04 +08:00
|
|
|
|
"https://static.shelingxingqiu.com/attachment/2025-09-17/dcutxin1aq7gxjih5l.mp3",
|
2025-07-14 16:37:56 +08:00
|
|
|
|
"2环":
|
2025-09-18 09:28:04 +08:00
|
|
|
|
"https://static.shelingxingqiu.com/attachment/2025-09-17/dcutxin64tdgx2s4at.mp3",
|
2025-07-14 16:37:56 +08:00
|
|
|
|
"3环":
|
2025-09-18 09:28:04 +08:00
|
|
|
|
"https://static.shelingxingqiu.com/attachment/2025-09-17/dcutxinlmf87vt8z65.mp3",
|
2025-07-14 16:37:56 +08:00
|
|
|
|
"4环":
|
2025-09-18 09:28:04 +08:00
|
|
|
|
"https://static.shelingxingqiu.com/attachment/2025-09-17/dcutxinniv97sx0q9u.mp3",
|
2025-07-14 16:37:56 +08:00
|
|
|
|
"5环":
|
2025-09-18 09:28:04 +08:00
|
|
|
|
"https://static.shelingxingqiu.com/attachment/2025-09-17/dcutxin7j01kknpb7k.mp3",
|
2025-07-14 16:37:56 +08:00
|
|
|
|
"6环":
|
2025-09-18 09:28:04 +08:00
|
|
|
|
"https://static.shelingxingqiu.com/attachment/2025-09-17/dcutxin4syy1015rtq.mp3",
|
2025-07-14 16:37:56 +08:00
|
|
|
|
"7环":
|
2025-09-18 09:28:04 +08:00
|
|
|
|
"https://static.shelingxingqiu.com/attachment/2025-09-17/dcutxin3iz3dvmjdai.mp3",
|
2025-07-14 16:37:56 +08:00
|
|
|
|
"8环":
|
2025-09-18 09:28:04 +08:00
|
|
|
|
"https://static.shelingxingqiu.com/attachment/2025-09-17/dcutxinnjd42lhpfiw.mp3",
|
2025-07-14 16:37:56 +08:00
|
|
|
|
"9环":
|
2025-09-18 09:28:04 +08:00
|
|
|
|
"https://static.shelingxingqiu.com/attachment/2025-09-17/dcutxin69nj1xh7yfz.mp3",
|
2025-07-14 16:37:56 +08:00
|
|
|
|
"10环":
|
2025-09-18 09:28:04 +08:00
|
|
|
|
"https://static.shelingxingqiu.com/attachment/2025-09-17/dcutxinnvsx0tt7ksa.mp3",
|
2025-07-14 16:37:56 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
2025-09-18 10:01:10 +08:00
|
|
|
|
// 版本控制日志函数
|
|
|
|
|
|
function debugLog(...args) {
|
|
|
|
|
|
// 获取当前环境信息
|
|
|
|
|
|
const accountInfo = uni.getAccountInfoSync();
|
|
|
|
|
|
const envVersion = accountInfo.miniProgram.envVersion;
|
2025-10-29 18:07:42 +08:00
|
|
|
|
|
2025-09-18 10:01:10 +08:00
|
|
|
|
// 只在体验版打印日志,正式版(release)和开发版(develop)不打印
|
2025-10-29 18:07:42 +08:00
|
|
|
|
if (envVersion === "trial") {
|
2025-09-18 10:01:10 +08:00
|
|
|
|
console.log(...args);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-14 16:37:56 +08:00
|
|
|
|
class AudioManager {
|
|
|
|
|
|
constructor() {
|
|
|
|
|
|
this.audioMap = new Map();
|
2025-09-18 09:28:04 +08:00
|
|
|
|
this.currentPlayingKey = null;
|
|
|
|
|
|
this.retryCount = new Map();
|
|
|
|
|
|
this.maxRetries = 3;
|
2025-11-05 18:30:33 +08:00
|
|
|
|
// 显式授权播放标记,防止 iOS 在设置 src 后误播
|
|
|
|
|
|
this.allowPlayMap = new Map();
|
2025-10-29 18:07:42 +08:00
|
|
|
|
|
2025-09-18 09:28:04 +08:00
|
|
|
|
// 串行加载相关属性
|
|
|
|
|
|
this.audioKeys = [];
|
|
|
|
|
|
this.currentLoadingIndex = 0;
|
|
|
|
|
|
this.isLoading = false;
|
|
|
|
|
|
this.loadingPromise = null;
|
2025-10-29 18:07:42 +08:00
|
|
|
|
|
2025-11-12 11:39:17 +08:00
|
|
|
|
// 连续播放队列相关属性
|
|
|
|
|
|
this.sequenceQueue = [];
|
|
|
|
|
|
this.sequenceIndex = 0;
|
|
|
|
|
|
this.isSequenceRunning = false;
|
|
|
|
|
|
|
2025-11-06 17:38:34 +08:00
|
|
|
|
// 网络状态相关
|
|
|
|
|
|
this.networkOnline = true;
|
|
|
|
|
|
this.pendingPlayKey = null;
|
|
|
|
|
|
try {
|
|
|
|
|
|
uni.onNetworkStatusChange(({ isConnected }) => {
|
|
|
|
|
|
this.networkOnline = !!isConnected;
|
|
|
|
|
|
if (this.networkOnline) {
|
|
|
|
|
|
this.onNetworkRestored();
|
|
|
|
|
|
} else {
|
|
|
|
|
|
this.onNetworkLost();
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
} catch (_) {}
|
|
|
|
|
|
|
2025-08-29 10:41:25 +08:00
|
|
|
|
this.initAudios();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 初始化音频
|
|
|
|
|
|
initAudios() {
|
2025-09-18 09:28:04 +08:00
|
|
|
|
if (this.isLoading) {
|
2025-09-18 10:01:10 +08:00
|
|
|
|
debugLog("音频正在加载中,跳过重复初始化");
|
2025-09-18 09:28:04 +08:00
|
|
|
|
return this.loadingPromise;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-18 10:01:10 +08:00
|
|
|
|
debugLog("开始串行加载音频...");
|
2025-09-18 09:28:04 +08:00
|
|
|
|
this.isLoading = true;
|
|
|
|
|
|
this.audioKeys = Object.keys(audioFils);
|
|
|
|
|
|
this.currentLoadingIndex = 0;
|
2025-10-29 18:07:42 +08:00
|
|
|
|
|
2025-09-18 09:28:04 +08:00
|
|
|
|
this.loadingPromise = new Promise((resolve) => {
|
|
|
|
|
|
this.loadNextAudio(resolve);
|
|
|
|
|
|
});
|
2025-10-29 18:07:42 +08:00
|
|
|
|
|
2025-09-18 09:28:04 +08:00
|
|
|
|
return this.loadingPromise;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 串行加载下一个音频
|
|
|
|
|
|
loadNextAudio(onComplete) {
|
|
|
|
|
|
if (this.currentLoadingIndex >= this.audioKeys.length) {
|
2025-09-18 10:01:10 +08:00
|
|
|
|
debugLog("所有音频加载完成");
|
2025-09-18 09:28:04 +08:00
|
|
|
|
this.isLoading = false;
|
|
|
|
|
|
if (onComplete) onComplete();
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const key = this.audioKeys[this.currentLoadingIndex];
|
2025-10-29 18:07:42 +08:00
|
|
|
|
debugLog(
|
|
|
|
|
|
`开始加载音频 ${this.currentLoadingIndex + 1}/${
|
|
|
|
|
|
this.audioKeys.length
|
|
|
|
|
|
}: ${key}`
|
|
|
|
|
|
);
|
|
|
|
|
|
|
2025-09-18 09:28:04 +08:00
|
|
|
|
this.createAudio(key, () => {
|
|
|
|
|
|
this.currentLoadingIndex++;
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
|
this.loadNextAudio(onComplete);
|
|
|
|
|
|
}, 100);
|
2025-07-14 16:37:56 +08:00
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-08-29 10:41:25 +08:00
|
|
|
|
// 创建单个音频实例
|
2025-09-18 09:28:04 +08:00
|
|
|
|
createAudio(key, callback) {
|
|
|
|
|
|
const src = audioFils[key];
|
2025-08-29 10:41:25 +08:00
|
|
|
|
const audio = uni.createInnerAudioContext();
|
|
|
|
|
|
audio.autoplay = false;
|
2025-11-05 18:30:33 +08:00
|
|
|
|
audio.src = src;
|
|
|
|
|
|
|
|
|
|
|
|
// 初始化为不允许播放,只有显式 play() 才允许
|
|
|
|
|
|
this.allowPlayMap.set(key, false);
|
|
|
|
|
|
|
|
|
|
|
|
// 防止 iOS 误播:非授权播放立刻停止
|
|
|
|
|
|
audio.onPlay(() => {
|
|
|
|
|
|
if (!this.allowPlayMap.get(key)) {
|
|
|
|
|
|
try {
|
|
|
|
|
|
audio.stop();
|
|
|
|
|
|
} catch (_) {}
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
2025-08-29 10:41:25 +08:00
|
|
|
|
|
2025-09-18 09:28:04 +08:00
|
|
|
|
// 设置加载超时
|
|
|
|
|
|
const loadTimeout = setTimeout(() => {
|
2025-09-18 10:01:10 +08:00
|
|
|
|
debugLog(`音频 ${key} 加载超时`);
|
2025-09-18 09:28:04 +08:00
|
|
|
|
audio.destroy();
|
|
|
|
|
|
if (callback) callback();
|
|
|
|
|
|
}, 10000);
|
|
|
|
|
|
|
2025-08-29 10:41:25 +08:00
|
|
|
|
// 监听加载状态
|
|
|
|
|
|
audio.onCanplay(() => {
|
2025-11-06 14:06:37 +08:00
|
|
|
|
// 预加载阶段:仅在未授权情况下暂停,避免用户刚点击播放被打断
|
|
|
|
|
|
if (!this.allowPlayMap.get(key)) {
|
|
|
|
|
|
try {
|
|
|
|
|
|
audio.pause();
|
|
|
|
|
|
} catch (_) {}
|
|
|
|
|
|
}
|
2025-09-18 09:28:04 +08:00
|
|
|
|
clearTimeout(loadTimeout);
|
2025-09-18 10:01:10 +08:00
|
|
|
|
debugLog(`音频 ${key} 已加载完成`);
|
2025-11-06 10:52:08 +08:00
|
|
|
|
uni.$emit("audioLoaded", key);
|
|
|
|
|
|
const loadedAudioKeys = uni.getStorageSync("loadedAudioKeys") || {};
|
|
|
|
|
|
loadedAudioKeys[key] = true;
|
|
|
|
|
|
uni.setStorageSync("loadedAudioKeys", loadedAudioKeys);
|
2025-08-29 10:41:25 +08:00
|
|
|
|
this.retryCount.set(key, 0);
|
2025-09-18 09:28:04 +08:00
|
|
|
|
if (callback) callback();
|
2025-08-29 10:41:25 +08:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
audio.onError((res) => {
|
2025-09-18 09:28:04 +08:00
|
|
|
|
clearTimeout(loadTimeout);
|
2025-09-18 10:01:10 +08:00
|
|
|
|
debugLog(`音频 ${key} 加载失败:`, res.errMsg);
|
2025-11-05 18:30:33 +08:00
|
|
|
|
this.allowPlayMap.set(key, false);
|
2025-08-29 10:41:25 +08:00
|
|
|
|
this.handleAudioError(key);
|
2025-09-18 09:28:04 +08:00
|
|
|
|
if (callback) callback();
|
2025-08-29 10:41:25 +08:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 监听播放结束事件
|
|
|
|
|
|
audio.onEnded(() => {
|
|
|
|
|
|
if (this.currentPlayingKey === key) {
|
|
|
|
|
|
this.currentPlayingKey = null;
|
|
|
|
|
|
}
|
2025-11-05 18:30:33 +08:00
|
|
|
|
this.allowPlayMap.set(key, false);
|
2025-11-12 11:39:17 +08:00
|
|
|
|
// 触发连续播放队列的衔接
|
|
|
|
|
|
this.onAudioEnded(key);
|
2025-08-29 10:41:25 +08:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 监听播放停止事件
|
|
|
|
|
|
audio.onStop(() => {
|
|
|
|
|
|
if (this.currentPlayingKey === key) {
|
|
|
|
|
|
this.currentPlayingKey = null;
|
|
|
|
|
|
}
|
2025-11-05 18:30:33 +08:00
|
|
|
|
this.allowPlayMap.set(key, false);
|
2025-08-29 10:41:25 +08:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
this.audioMap.set(key, audio);
|
|
|
|
|
|
if (!this.retryCount.has(key)) {
|
|
|
|
|
|
this.retryCount.set(key, 0);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 处理音频加载错误
|
|
|
|
|
|
handleAudioError(key) {
|
2025-11-06 17:38:34 +08:00
|
|
|
|
// 网络不可用时不重试,等重连后统一重建
|
|
|
|
|
|
if (!this.networkOnline) {
|
|
|
|
|
|
debugLog(`网络不可用,暂不重试音频: ${key}`);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2025-08-29 10:41:25 +08:00
|
|
|
|
const currentRetries = this.retryCount.get(key) || 0;
|
2025-09-15 11:23:22 +08:00
|
|
|
|
|
2025-08-29 10:41:25 +08:00
|
|
|
|
if (currentRetries < this.maxRetries) {
|
|
|
|
|
|
this.retryCount.set(key, currentRetries + 1);
|
2025-09-18 10:01:10 +08:00
|
|
|
|
debugLog(`音频 ${key} 开始第 ${currentRetries + 1} 次重试...`);
|
2025-10-29 18:07:42 +08:00
|
|
|
|
|
2025-08-29 10:41:25 +08:00
|
|
|
|
setTimeout(() => {
|
|
|
|
|
|
this.retryLoadAudio(key);
|
2025-09-18 09:28:04 +08:00
|
|
|
|
}, 1000);
|
2025-08-29 10:41:25 +08:00
|
|
|
|
} else {
|
2025-10-29 18:07:42 +08:00
|
|
|
|
console.error(
|
|
|
|
|
|
`音频 ${key} 重试 ${this.maxRetries} 次后仍然失败,停止重试`
|
|
|
|
|
|
);
|
2025-09-18 09:28:04 +08:00
|
|
|
|
const failedAudio = this.audioMap.get(key);
|
|
|
|
|
|
if (failedAudio) {
|
|
|
|
|
|
failedAudio.destroy();
|
|
|
|
|
|
this.audioMap.delete(key);
|
|
|
|
|
|
}
|
2025-08-29 10:41:25 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 重新加载音频
|
|
|
|
|
|
retryLoadAudio(key) {
|
2025-11-06 17:38:34 +08:00
|
|
|
|
if (!this.networkOnline) {
|
|
|
|
|
|
debugLog(`网络不可用,稍后再重载音频: ${key}`);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2025-08-29 10:41:25 +08:00
|
|
|
|
const oldAudio = this.audioMap.get(key);
|
|
|
|
|
|
if (oldAudio) {
|
|
|
|
|
|
oldAudio.destroy();
|
|
|
|
|
|
}
|
|
|
|
|
|
this.createAudio(key);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-12 11:39:17 +08:00
|
|
|
|
// 播放指定音频或音频数组(数组则按顺序连续播放)
|
|
|
|
|
|
play(input) {
|
|
|
|
|
|
// 离线:缓存播放意图(可为字符串或数组),待网络恢复后自动播放
|
|
|
|
|
|
if (!this.networkOnline) {
|
|
|
|
|
|
this.pendingPlayKey = input;
|
|
|
|
|
|
debugLog(`网络不可用,记录播放意图: ${Array.isArray(input) ? input.join(',') : input}`);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 再次调用 play:打断前面所有声音与队列
|
|
|
|
|
|
this.stopAll();
|
|
|
|
|
|
this.isSequenceRunning = false;
|
|
|
|
|
|
this.sequenceQueue = [];
|
|
|
|
|
|
this.sequenceIndex = 0;
|
|
|
|
|
|
|
|
|
|
|
|
if (Array.isArray(input)) {
|
|
|
|
|
|
// 过滤可播放的 key
|
|
|
|
|
|
const queue = input.filter((k) => !!audioFils[k]);
|
|
|
|
|
|
if (queue.length === 0) {
|
|
|
|
|
|
debugLog("连续播放队列为空或无效");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
this.sequenceQueue = queue;
|
|
|
|
|
|
this.sequenceIndex = 0;
|
|
|
|
|
|
this.isSequenceRunning = true;
|
|
|
|
|
|
// 开始播放队列的第一个
|
|
|
|
|
|
this._playSingle(queue[0], false);
|
|
|
|
|
|
} else if (typeof input === "string") {
|
|
|
|
|
|
this._playSingle(input, false);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
debugLog("play 参数类型无效,仅支持字符串或字符串数组");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 内部方法:播放单个 key
|
|
|
|
|
|
_playSingle(key, forceStopAll = false) {
|
2025-11-06 17:38:34 +08:00
|
|
|
|
if (!this.networkOnline) {
|
|
|
|
|
|
this.pendingPlayKey = key;
|
|
|
|
|
|
debugLog(`网络不可用,记录播放意图: ${key}`);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2025-11-12 11:39:17 +08:00
|
|
|
|
|
|
|
|
|
|
if (forceStopAll) {
|
|
|
|
|
|
this.stopAll();
|
|
|
|
|
|
} else if (this.currentPlayingKey && this.currentPlayingKey !== key) {
|
2025-08-27 18:23:59 +08:00
|
|
|
|
this.stop(this.currentPlayingKey);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-14 16:37:56 +08:00
|
|
|
|
const audio = this.audioMap.get(key);
|
2025-08-27 18:23:59 +08:00
|
|
|
|
if (audio) {
|
2025-11-06 11:29:08 +08:00
|
|
|
|
// 同一音频:避免 stop() 触发 onStop 清除授权,使用 pause()+seek(0)
|
|
|
|
|
|
try {
|
|
|
|
|
|
audio.pause();
|
|
|
|
|
|
} catch (_) {}
|
|
|
|
|
|
try {
|
|
|
|
|
|
if (typeof audio.seek === "function") {
|
|
|
|
|
|
audio.seek(0);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
audio.startTime = 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (_) {
|
|
|
|
|
|
audio.startTime = 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-05 18:30:33 +08:00
|
|
|
|
// 显式授权播放并立即播放
|
|
|
|
|
|
this.allowPlayMap.set(key, true);
|
2025-11-06 11:29:08 +08:00
|
|
|
|
|
2025-09-18 09:28:04 +08:00
|
|
|
|
audio.play();
|
|
|
|
|
|
this.currentPlayingKey = key;
|
|
|
|
|
|
} else {
|
2025-09-18 10:01:10 +08:00
|
|
|
|
debugLog(`音频 ${key} 不存在,尝试重新加载...`);
|
2025-09-18 09:28:04 +08:00
|
|
|
|
this.reloadAudio(key);
|
2025-08-27 18:23:59 +08:00
|
|
|
|
}
|
2025-07-14 16:37:56 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-12 11:39:17 +08:00
|
|
|
|
// 连续播放:在某个音频结束后,若处于队列播放状态则继续下一个
|
|
|
|
|
|
onAudioEnded(key) {
|
|
|
|
|
|
if (!this.isSequenceRunning) return;
|
|
|
|
|
|
const currentKey = this.sequenceQueue[this.sequenceIndex];
|
|
|
|
|
|
if (currentKey !== key) return;
|
|
|
|
|
|
|
|
|
|
|
|
const nextIndex = this.sequenceIndex + 1;
|
|
|
|
|
|
if (nextIndex < this.sequenceQueue.length) {
|
|
|
|
|
|
this.sequenceIndex = nextIndex;
|
|
|
|
|
|
const nextKey = this.sequenceQueue[nextIndex];
|
|
|
|
|
|
this._playSingle(nextKey, false);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// 队列播放完成
|
|
|
|
|
|
this.isSequenceRunning = false;
|
|
|
|
|
|
this.sequenceQueue = [];
|
|
|
|
|
|
this.sequenceIndex = 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-14 16:37:56 +08:00
|
|
|
|
// 停止指定音频
|
|
|
|
|
|
stop(key) {
|
|
|
|
|
|
const audio = this.audioMap.get(key);
|
2025-08-27 18:23:59 +08:00
|
|
|
|
if (audio) {
|
|
|
|
|
|
audio.stop();
|
2025-11-05 18:30:33 +08:00
|
|
|
|
this.allowPlayMap.set(key, false);
|
2025-08-27 18:23:59 +08:00
|
|
|
|
if (this.currentPlayingKey === key) {
|
|
|
|
|
|
this.currentPlayingKey = null;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-07-14 16:37:56 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-06 14:06:37 +08:00
|
|
|
|
// 停止所有音频
|
|
|
|
|
|
stopAll() {
|
|
|
|
|
|
for (const [k, audio] of this.audioMap.entries()) {
|
|
|
|
|
|
try {
|
|
|
|
|
|
audio.stop();
|
|
|
|
|
|
} catch (_) {}
|
|
|
|
|
|
this.allowPlayMap.set(k, false);
|
|
|
|
|
|
}
|
|
|
|
|
|
this.currentPlayingKey = null;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 销毁所有音频实例并清理状态
|
|
|
|
|
|
destroyAll() {
|
|
|
|
|
|
for (const [k, audio] of this.audioMap.entries()) {
|
|
|
|
|
|
try {
|
|
|
|
|
|
audio.destroy();
|
|
|
|
|
|
} catch (_) {}
|
|
|
|
|
|
this.allowPlayMap.delete(k);
|
|
|
|
|
|
this.retryCount.delete(k);
|
|
|
|
|
|
this.audioMap.delete(k);
|
|
|
|
|
|
}
|
|
|
|
|
|
this.audioKeys = [];
|
|
|
|
|
|
this.currentLoadingIndex = 0;
|
|
|
|
|
|
this.isLoading = false;
|
|
|
|
|
|
this.loadingPromise = null;
|
|
|
|
|
|
this.currentPlayingKey = null;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-06 17:38:34 +08:00
|
|
|
|
// 断网回调:停止当前播放,避免误状态
|
|
|
|
|
|
onNetworkLost() {
|
|
|
|
|
|
try {
|
|
|
|
|
|
this.stopAll();
|
|
|
|
|
|
} catch (_) {}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 重连回调:重建全部音频后,重放上一次的播放意图
|
|
|
|
|
|
onNetworkRestored() {
|
|
|
|
|
|
this.reinitializeOnReconnect();
|
|
|
|
|
|
if (this.pendingPlayKey) {
|
|
|
|
|
|
const key = this.pendingPlayKey;
|
|
|
|
|
|
this.pendingPlayKey = null;
|
|
|
|
|
|
// 避免与初始化竞态,轻微延迟后播放
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
|
this.play(key);
|
|
|
|
|
|
}, 20);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 重连后统一重建音频实例
|
|
|
|
|
|
reinitializeOnReconnect() {
|
|
|
|
|
|
this.destroyAll();
|
|
|
|
|
|
this.initAudios();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-08-29 10:41:25 +08:00
|
|
|
|
// 手动重新加载指定音频
|
|
|
|
|
|
reloadAudio(key) {
|
|
|
|
|
|
if (audioFils[key]) {
|
2025-09-18 10:01:10 +08:00
|
|
|
|
debugLog(`手动重新加载音频: ${key}`);
|
2025-09-18 09:28:04 +08:00
|
|
|
|
this.retryCount.set(key, 0);
|
2025-08-29 10:41:25 +08:00
|
|
|
|
this.retryLoadAudio(key);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-07-14 16:37:56 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 导出单例
|
|
|
|
|
|
export default new AudioManager();
|