fix(mi-voice): await AudioContext.resume before source.start
Suspended contexts silently queued audio that never played. Await resume, split connect() chain to avoid undefined-return on older browsers, and re-check state after source setup in case first resume lost the gesture.
This commit is contained in:
parent
858711e783
commit
71782b1cf1
|
|
@ -88,12 +88,12 @@ export class MiVoiceBridge {
|
|||
|
||||
// ── Bridge TTS ──
|
||||
|
||||
#ensureAudioCtx(): AudioContext {
|
||||
async #ensureAudioCtx(): Promise<AudioContext> {
|
||||
if (!this.#audioCtx || this.#audioCtx.state === "closed") {
|
||||
this.#audioCtx = new AudioContext();
|
||||
}
|
||||
if (this.#audioCtx.state === "suspended") {
|
||||
this.#audioCtx.resume();
|
||||
try { await this.#audioCtx.resume(); } catch { /* gesture may be required */ }
|
||||
}
|
||||
return this.#audioCtx;
|
||||
}
|
||||
|
|
@ -158,14 +158,18 @@ export class MiVoiceBridge {
|
|||
} catch { /* ignore bad header */ }
|
||||
const mp3Bytes = buf.slice(4 + headerLen);
|
||||
|
||||
const ctx = this.#ensureAudioCtx();
|
||||
const ctx = await this.#ensureAudioCtx();
|
||||
const audioBuffer = await ctx.decodeAudioData(mp3Bytes.slice(0)); // slice to copy
|
||||
const source = ctx.createBufferSource();
|
||||
source.buffer = audioBuffer;
|
||||
const gain = ctx.createGain();
|
||||
gain.gain.value = gainValue;
|
||||
source.connect(gain).connect(ctx.destination);
|
||||
source.connect(gain);
|
||||
gain.connect(ctx.destination);
|
||||
this.#currentSource = source;
|
||||
if (ctx.state === "suspended") {
|
||||
try { await ctx.resume(); } catch { /* ignore */ }
|
||||
}
|
||||
|
||||
source.onended = () => {
|
||||
this.#currentSource = null;
|
||||
|
|
|
|||
Loading…
Reference in New Issue