export const audioFils = { 激光已校准: "https://static.shelingxingqiu.com/attachment/2025-10-29/ddupaur1vdkyhzaqdc.mp3", 胜利: "https://static.shelingxingqiu.com/attachment/2025-09-17/dcuo9yjp0kt5msvmvd.mp3", 失败: "https://static.shelingxingqiu.com/attachment/2025-09-17/dcuo9yht2sdwhuqygy.mp3", 请射箭测试距离: "https://static.shelingxingqiu.com/attachment/2025-09-17/dcuvj8avzqyw4hpq7t.mp3", 距离合格: "https://static.shelingxingqiu.com/attachment/2025-09-17/dcutwrda0amn5kqr4j.mp3", 距离不足: "https://static.shelingxingqiu.com/attachment/2025-09-17/dcutya57qurnsj6pg4.mp3", 轮到你了: "https://static.shelingxingqiu.com/attachment/2025-09-17/dcutzdrn4lxcpv8aqr.mp3", 第一轮: "https://static.shelingxingqiu.com/attachment/2025-09-17/dcutyyr9a7m1vz2w13.mp3", 第二轮: "https://static.shelingxingqiu.com/attachment/2025-09-17/dcutyyr9ldnfexjxtw.mp3", 第三轮: "https://static.shelingxingqiu.com/attachment/2025-09-17/dcutyyr97m4ipxaze4.mp3", 第四轮: "https://static.shelingxingqiu.com/attachment/2025-09-17/dcutyyr9x5addohlzf.mp3", 第五轮: "https://static.shelingxingqiu.com/attachment/2025-09-17/dcutyyr9d7lw2gebpv.mp3", 决金箭轮: "https://static.shelingxingqiu.com/attachment/2025-09-17/dcutwrd9zs4oi2kujv.mp3", 请蓝方射箭: "https://static.shelingxingqiu.com/attachment/2025-09-17/dcutzdrxcbe5ll46as.mp3", 请红方射箭: "https://static.shelingxingqiu.com/attachment/2025-09-17/dcutzdrl3re3dhlfjd.mp3", 中场休息: "https://static.shelingxingqiu.com/attachment/2025-09-17/dcutwrd9zdk1xyolst.mp3", 比赛结束: "https://static.shelingxingqiu.com/attachment/2025-09-17/dcutya59b6pu0ur4um.mp3", 比赛开始: "https://static.shelingxingqiu.com/attachment/2025-09-17/dcuu5z3a3lumkutske.mp3", 请开始射击: "https://static.shelingxingqiu.com/attachment/2025-09-17/dcutzdrl5u0iromqhf.mp3", 射击无效: "https://static.shelingxingqiu.com/attachment/2025-09-17/dcutya55ufiiw8oo55.mp3", 未上靶: "https://static.shelingxingqiu.com/attachment/2025-09-17/dcuuznjc78ljhzuw1o.mp3", "1环": "https://static.shelingxingqiu.com/attachment/2025-09-17/dcutxin1aq7gxjih5l.mp3", "2环": "https://static.shelingxingqiu.com/attachment/2025-09-17/dcutxin64tdgx2s4at.mp3", "3环": "https://static.shelingxingqiu.com/attachment/2025-09-17/dcutxinlmf87vt8z65.mp3", "4环": "https://static.shelingxingqiu.com/attachment/2025-09-17/dcutxinniv97sx0q9u.mp3", "5环": "https://static.shelingxingqiu.com/attachment/2025-09-17/dcutxin7j01kknpb7k.mp3", "6环": "https://static.shelingxingqiu.com/attachment/2025-09-17/dcutxin4syy1015rtq.mp3", "7环": "https://static.shelingxingqiu.com/attachment/2025-09-17/dcutxin3iz3dvmjdai.mp3", "8环": "https://static.shelingxingqiu.com/attachment/2025-09-17/dcutxinnjd42lhpfiw.mp3", "9环": "https://static.shelingxingqiu.com/attachment/2025-09-17/dcutxin69nj1xh7yfz.mp3", "10环": "https://static.shelingxingqiu.com/attachment/2025-09-17/dcutxinnvsx0tt7ksa.mp3", }; // 版本控制日志函数 function debugLog(...args) { // 获取当前环境信息 const accountInfo = uni.getAccountInfoSync(); const envVersion = accountInfo.miniProgram.envVersion; // 只在体验版打印日志,正式版(release)和开发版(develop)不打印 if (envVersion === "trial") { console.log(...args); } } class AudioManager { constructor() { this.audioMap = new Map(); this.currentPlayingKey = null; this.retryCount = new Map(); this.maxRetries = 3; // 显式授权播放标记,防止 iOS 在设置 src 后误播 this.allowPlayMap = new Map(); // 串行加载相关属性 this.audioKeys = []; this.currentLoadingIndex = 0; this.isLoading = false; this.loadingPromise = null; this.initAudios(); } // 初始化音频 initAudios() { if (this.isLoading) { debugLog("音频正在加载中,跳过重复初始化"); return this.loadingPromise; } debugLog("开始串行加载音频..."); this.isLoading = true; this.audioKeys = Object.keys(audioFils); this.currentLoadingIndex = 0; this.loadingPromise = new Promise((resolve) => { this.loadNextAudio(resolve); }); return this.loadingPromise; } // 串行加载下一个音频 loadNextAudio(onComplete) { if (this.currentLoadingIndex >= this.audioKeys.length) { debugLog("所有音频加载完成"); this.isLoading = false; if (onComplete) onComplete(); return; } const key = this.audioKeys[this.currentLoadingIndex]; debugLog( `开始加载音频 ${this.currentLoadingIndex + 1}/${ this.audioKeys.length }: ${key}` ); this.createAudio(key, () => { this.currentLoadingIndex++; setTimeout(() => { this.loadNextAudio(onComplete); }, 100); }); } // 创建单个音频实例 createAudio(key, callback) { const src = audioFils[key]; const audio = uni.createInnerAudioContext(); audio.autoplay = false; audio.src = src; // 初始化为不允许播放,只有显式 play() 才允许 this.allowPlayMap.set(key, false); // 防止 iOS 误播:非授权播放立刻停止 audio.onPlay(() => { if (!this.allowPlayMap.get(key)) { try { audio.stop(); } catch (_) {} } }); // 设置加载超时 const loadTimeout = setTimeout(() => { debugLog(`音频 ${key} 加载超时`); audio.destroy(); if (callback) callback(); }, 10000); // 监听加载状态 audio.onCanplay(() => { // 预加载阶段:仅在未授权情况下暂停,避免用户刚点击播放被打断 if (!this.allowPlayMap.get(key)) { try { audio.pause(); } catch (_) {} } clearTimeout(loadTimeout); debugLog(`音频 ${key} 已加载完成`); uni.$emit("audioLoaded", key); const loadedAudioKeys = uni.getStorageSync("loadedAudioKeys") || {}; loadedAudioKeys[key] = true; uni.setStorageSync("loadedAudioKeys", loadedAudioKeys); this.retryCount.set(key, 0); if (callback) callback(); }); audio.onError((res) => { clearTimeout(loadTimeout); debugLog(`音频 ${key} 加载失败:`, res.errMsg); this.allowPlayMap.set(key, false); this.handleAudioError(key); if (callback) callback(); }); // 监听播放结束事件 audio.onEnded(() => { if (this.currentPlayingKey === key) { this.currentPlayingKey = null; } this.allowPlayMap.set(key, false); }); // 监听播放停止事件 audio.onStop(() => { if (this.currentPlayingKey === key) { this.currentPlayingKey = null; } this.allowPlayMap.set(key, false); }); this.audioMap.set(key, audio); if (!this.retryCount.has(key)) { this.retryCount.set(key, 0); } } // 处理音频加载错误 handleAudioError(key) { const currentRetries = this.retryCount.get(key) || 0; if (currentRetries < this.maxRetries) { this.retryCount.set(key, currentRetries + 1); debugLog(`音频 ${key} 开始第 ${currentRetries + 1} 次重试...`); setTimeout(() => { this.retryLoadAudio(key); }, 1000); } else { console.error( `音频 ${key} 重试 ${this.maxRetries} 次后仍然失败,停止重试` ); const failedAudio = this.audioMap.get(key); if (failedAudio) { failedAudio.destroy(); this.audioMap.delete(key); } } } // 重新加载音频 retryLoadAudio(key) { const oldAudio = this.audioMap.get(key); if (oldAudio) { oldAudio.destroy(); } this.createAudio(key); } // 播放指定音频 play(key) { // 覆盖播放:若当前播放且不是同一音频,先停止当前播放 if (this.currentPlayingKey && this.currentPlayingKey !== key) { this.stop(this.currentPlayingKey); } const audio = this.audioMap.get(key); if (audio) { // 同一音频:避免 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; } // 显式授权播放并立即播放 this.allowPlayMap.set(key, true); audio.play(); this.currentPlayingKey = key; } else { debugLog(`音频 ${key} 不存在,尝试重新加载...`); this.reloadAudio(key); } } // 停止指定音频 stop(key) { const audio = this.audioMap.get(key); if (audio) { audio.stop(); this.allowPlayMap.set(key, false); if (this.currentPlayingKey === key) { this.currentPlayingKey = null; } } } // 停止所有音频 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; } // 手动重新加载指定音频 reloadAudio(key) { if (audioFils[key]) { debugLog(`手动重新加载音频: ${key}`); this.retryCount.set(key, 0); this.retryLoadAudio(key); } } } // 导出单例 export default new AudioManager();