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:
@@ -15,6 +15,19 @@ const globalData = {
|
|||||||
let avatarUrl = ref(""); // 声明为响应式变量
|
let avatarUrl = ref(""); // 声明为响应式变量
|
||||||
uni.CallManager = new CallManager();
|
uni.CallManager = new CallManager();
|
||||||
onLaunch(() => {
|
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({
|
uni.login({
|
||||||
provider: 'weixin',
|
provider: 'weixin',
|
||||||
success: function (loginRes) {
|
success: function (loginRes) {
|
||||||
|
|||||||
@@ -38,24 +38,24 @@ class AudioPool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 预热:静音播放一次每个实例,让音频解码完成
|
* 预热:极小音量短暂播放,让音频文件下载/解码到内存
|
||||||
* 这样后续 play() 时就是热启动,没有冷启动延迟
|
* 真正首次 play 不会再卡冷启动
|
||||||
|
* 注意:iOS 上 volume = 0 不一定真正解码,所以用 0.01 极小音量
|
||||||
*/
|
*/
|
||||||
warmUp() {
|
warmUp() {
|
||||||
if (this.warmedUp) return
|
if (this.warmedUp) return
|
||||||
if (this.list.length === 0) this.create()
|
if (this.list.length === 0) this.create()
|
||||||
|
|
||||||
this.list.forEach((ctx) => {
|
this.list.forEach((ctx) => {
|
||||||
const originalVolume = ctx.volume
|
|
||||||
ctx.volume = 0
|
|
||||||
try {
|
try {
|
||||||
|
ctx.volume = 0.01
|
||||||
ctx.play()
|
ctx.play()
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
try {
|
try {
|
||||||
ctx.stop()
|
ctx.stop()
|
||||||
ctx.volume = originalVolume === 0 ? 1 : originalVolume
|
ctx.volume = 1
|
||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
}, 80)
|
}, 60)
|
||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
})
|
})
|
||||||
this.warmedUp = true
|
this.warmedUp = true
|
||||||
@@ -69,10 +69,11 @@ class AudioPool {
|
|||||||
const ctx = this.list[this.cursor]
|
const ctx = this.list[this.cursor]
|
||||||
this.cursor = (this.cursor + 1) % this.list.length
|
this.cursor = (this.cursor + 1) % this.list.length
|
||||||
try {
|
try {
|
||||||
ctx.seek(0)
|
// iOS 上 seek(0) + play() 不一定能从头播放;stop() + play() 更稳
|
||||||
|
ctx.stop()
|
||||||
ctx.play()
|
ctx.play()
|
||||||
} catch (_) {
|
} catch (_) {
|
||||||
ctx.play()
|
try { ctx.play() } catch (__) {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -175,8 +176,17 @@ export function useMetronome(options: MetronomeOptions = {}) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 主动预加载(推荐在页面 onShow 时调用,让用户进页面就完成预热)
|
* 主动预加载(推荐在页面 onShow 时调用,让用户进页面就完成预热)
|
||||||
|
* 同时再次设置 setInnerAudioOption,防 App.vue 全局设置失效(iOS 静音键)
|
||||||
*/
|
*/
|
||||||
const preload = () => {
|
const preload = () => {
|
||||||
|
// #ifdef MP-WEIXIN
|
||||||
|
try {
|
||||||
|
uni.setInnerAudioOption({
|
||||||
|
obeyMuteSwitch: false,
|
||||||
|
mixWithOther: true,
|
||||||
|
})
|
||||||
|
} catch (_) {}
|
||||||
|
// #endif
|
||||||
ensureAudio()
|
ensureAudio()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user