fix(voice): TTS играет через AudioContext (фикс для iPad Safari)
All checks were successful
Deploy / deploy (push) Successful in 1m45s
All checks were successful
Deploy / deploy (push) Successful in 1m45s
iOS Safari блокирует <audio>.play() даже после silent-WAV unlock — каждый new Audio() считается новым элементом без gesture. Решение: при тапе кнопки в VoiceController создаём общий AudioContext (под user-gesture) и пробуждаем его. VoiceOverlay теперь играет TTS через этот ctx (decodeAudioData + BufferSource). HTMLAudioElement остаётся fallback'ом если ctx недоступен. decodeAudioData в Safari исторически callback, в Chrome — Promise: используем оба варианта.
This commit is contained in:
@@ -178,19 +178,21 @@ export default function VoiceController() {
|
||||
if (state !== 'idle' && state !== 'error') return
|
||||
setState('loading')
|
||||
|
||||
// 0. «Audio unlock» — Android Chrome не даёт <audio>.play() без user-gesture.
|
||||
// Wake-word срабатывает сам, поэтому позже play() будет тихо отвергнут.
|
||||
// Проигрываем 1мс silent WAV прямо сейчас (user тапнул кнопку → есть gesture).
|
||||
// 0. «Audio unlock» — iOS Safari / Android Chrome не дают воспроизводить
|
||||
// звук без user-gesture. Wake-word срабатывает сам, поэтому позже TTS
|
||||
// тихо отвергнется. Создаём общий AudioContext прямо сейчас (тап = gesture)
|
||||
// и сохраняем в window — VoiceOverlay будет играть через него.
|
||||
try {
|
||||
const silent = new Audio(
|
||||
'data:audio/wav;base64,UklGRigAAABXQVZFZm10IBIAAAABAAEARKwAAIhYAQACABAAAABkYXRhAgAAAAEA',
|
||||
)
|
||||
silent.volume = 0
|
||||
await silent.play()
|
||||
silent.pause()
|
||||
console.log('[voice] audio unlock ok')
|
||||
const w = window as any
|
||||
if (!w.__voicePlaybackCtx) {
|
||||
const Ctx = w.AudioContext || w.webkitAudioContext
|
||||
if (Ctx) w.__voicePlaybackCtx = new Ctx()
|
||||
}
|
||||
const ctx: AudioContext | undefined = w.__voicePlaybackCtx
|
||||
if (ctx && ctx.state === 'suspended') await ctx.resume()
|
||||
console.log('[voice] playback AudioContext state=', ctx?.state)
|
||||
} catch (e: any) {
|
||||
console.warn('[voice] audio unlock failed:', e?.message)
|
||||
console.warn('[voice] AudioContext init failed:', e?.message)
|
||||
}
|
||||
|
||||
// 1. Запрос разрешения на микрофон отдельно
|
||||
|
||||
Reference in New Issue
Block a user