fix(metronome): 修复 iOS 真机静音键模式下无声音

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 <cursoragent@cursor.com>
This commit is contained in:
2026-05-25 11:48:16 +08:00
co-authored by Cursor
parent 8f14f3190e
commit edc9993403
2 changed files with 31 additions and 8 deletions
+13
View File
@@ -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) {
+18 -8
View File
@@ -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()
}