From edc9993403dd26d52e5f41ef6c9ac8fa1bc41cfc Mon Sep 17 00:00:00 2001 From: long <452591453@qq.com> Date: Mon, 25 May 2026 11:48:16 +0800 Subject: [PATCH] =?UTF-8?q?fix(metronome):=20=E4=BF=AE=E5=A4=8D=20iOS=20?= =?UTF-8?q?=E7=9C=9F=E6=9C=BA=E9=9D=99=E9=9F=B3=E9=94=AE=E6=A8=A1=E5=BC=8F?= =?UTF-8?q?=E4=B8=8B=E6=97=A0=E5=A3=B0=E9=9F=B3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit iOS 微信小程序的 InnerAudioContext 默认会受系统侧边静音键影响, 单实例 obeyMuteSwitch = false 在 iOS 上不可靠,必须调用全局 API setInnerAudioOption 才能正常发声。 - App.vue onLaunch 调用 uni.setInnerAudioOption({ obeyMuteSwitch: false, mixWithOther: true }) - useMetronome.preload() 进页面时再调一次保险 - AudioPool.play() 改用 stop() + play() 替代 seek(0) + play(), iOS 上 seek(0) 不一定能复位播放进度 - 预热改用 volume 0.01 极小音量替代 volume 0,iOS 上音量为 0 可能不会真正完成音频解码 Co-authored-by: Cursor --- TUICallKit-Vue3/App.vue | 13 +++++++++++++ TUICallKit-Vue3/hooks/useMetronome.ts | 26 ++++++++++++++++++-------- 2 files changed, 31 insertions(+), 8 deletions(-) diff --git a/TUICallKit-Vue3/App.vue b/TUICallKit-Vue3/App.vue index c58b9fcc..b88a68b7 100644 --- a/TUICallKit-Vue3/App.vue +++ b/TUICallKit-Vue3/App.vue @@ -15,6 +15,19 @@ const globalData = { let avatarUrl = ref(""); // 声明为响应式变量 uni.CallManager = new CallManager(); onLaunch(() => { + // iOS 静音键模式下 InnerAudioContext 默认不发声, + // 必须在 onLaunch 全局调用此 API,单实例 obeyMuteSwitch 在 iOS 不可靠 + // #ifdef MP-WEIXIN + try { + uni.setInnerAudioOption({ + obeyMuteSwitch: false, + mixWithOther: true, + }) + } catch (e) { + console.warn('setInnerAudioOption failed:', e) + } + // #endif + uni.login({ provider: 'weixin', success: function (loginRes) { diff --git a/TUICallKit-Vue3/hooks/useMetronome.ts b/TUICallKit-Vue3/hooks/useMetronome.ts index 34b24199..d68a616f 100644 --- a/TUICallKit-Vue3/hooks/useMetronome.ts +++ b/TUICallKit-Vue3/hooks/useMetronome.ts @@ -38,24 +38,24 @@ class AudioPool { } /** - * 预热:静音播放一次每个实例,让音频解码完成 - * 这样后续 play() 时就是热启动,没有冷启动延迟 + * 预热:极小音量短暂播放,让音频文件下载/解码到内存 + * 真正首次 play 不会再卡冷启动 + * 注意:iOS 上 volume = 0 不一定真正解码,所以用 0.01 极小音量 */ warmUp() { if (this.warmedUp) return if (this.list.length === 0) this.create() this.list.forEach((ctx) => { - const originalVolume = ctx.volume - ctx.volume = 0 try { + ctx.volume = 0.01 ctx.play() setTimeout(() => { try { ctx.stop() - ctx.volume = originalVolume === 0 ? 1 : originalVolume + ctx.volume = 1 } catch (_) {} - }, 80) + }, 60) } catch (_) {} }) this.warmedUp = true @@ -69,10 +69,11 @@ class AudioPool { const ctx = this.list[this.cursor] this.cursor = (this.cursor + 1) % this.list.length try { - ctx.seek(0) + // iOS 上 seek(0) + play() 不一定能从头播放;stop() + play() 更稳 + ctx.stop() ctx.play() } catch (_) { - ctx.play() + try { ctx.play() } catch (__) {} } } @@ -175,8 +176,17 @@ export function useMetronome(options: MetronomeOptions = {}) { /** * 主动预加载(推荐在页面 onShow 时调用,让用户进页面就完成预热) + * 同时再次设置 setInnerAudioOption,防 App.vue 全局设置失效(iOS 静音键) */ const preload = () => { + // #ifdef MP-WEIXIN + try { + uni.setInnerAudioOption({ + obeyMuteSwitch: false, + mixWithOther: true, + }) + } catch (_) {} + // #endif ensureAudio() }