zebra-spaces: wake AudioContext render thread inside entry gesture via silent oscillator
Phone telemetry post-deploy showed attachListenerStreamViaAudioContext logging ctxState=suspended despite resume() being called at entry- click time. Firefox Android requires more than just resume() to keep the audio render thread alive — the context goes back to suspended the moment the gesture window closes if nothing is actively playing through destination. Fix: inside primeAudioOnGesture, after resume(), play a brief silent oscillator (50ms, gain=0) through audioCtx.destination. That forces the render thread to ACTUALLY START rather than just queueing- pending. After this the context stays running for the session and every MediaStreamAudioSource attached to destination plays through. Added 'audioCtx primed state=...' log so we can confirm the state flipped to 'running' in the entry click.
This commit is contained in:
parent
64094ecf31
commit
0cba22e135
1 changed files with 23 additions and 9 deletions
|
|
@ -5175,16 +5175,30 @@ function primeAudioOnGesture(){
|
|||
if (p && p.then) p.then(() => {
|
||||
setTimeout(() => { try { a.remove(); } catch(_){} }, 500);
|
||||
}).catch(()=>{ try { a.remove(); } catch(_){} });
|
||||
/* Create audioCtx if absent + resume so the listener path
|
||||
* (attachListenerStreamViaAudioContext) has a granted-and-running
|
||||
* AudioContext by the time the first remote MediaStream arrives.
|
||||
* Listeners never grab a mic so without this, audioCtx would be
|
||||
* null forever and the Web Audio listener route couldn't bootstrap.
|
||||
* Speakers also benefit — meter creation later won't have to
|
||||
* resume-from-suspended (which can fail outside a gesture). */
|
||||
/* Create audioCtx + actually start the render thread inside the
|
||||
* gesture. Just calling resume() isn't enough on Firefox Android —
|
||||
* telemetry fox 2026-06-04 caught attachListenerStreamViaAudioContext
|
||||
* logging ctxState=suspended even though resume() was called at
|
||||
* entry-click time. resume() outside a subsequent gesture silently
|
||||
* fails on Android.
|
||||
*
|
||||
* Play a brief silent oscillator through audioCtx.destination
|
||||
* inside this gesture — that forces the audio rendering thread to
|
||||
* ACTUALLY START rather than queueing-pending. After this the
|
||||
* context stays running for the session and every
|
||||
* MediaStreamAudioSource attached to destination plays through. */
|
||||
try {
|
||||
if (!audioCtx) audioCtx = new (window.AudioContext || window.webkitAudioContext)();
|
||||
if (audioCtx.state === 'suspended') audioCtx.resume().catch(()=>{});
|
||||
const osc = audioCtx.createOscillator();
|
||||
const g = audioCtx.createGain();
|
||||
g.gain.value = 0; /* silent — only here to wake the render thread */
|
||||
osc.connect(g);
|
||||
g.connect(audioCtx.destination);
|
||||
osc.start();
|
||||
osc.stop(audioCtx.currentTime + 0.05);
|
||||
osc.onended = () => { try { osc.disconnect(); g.disconnect(); } catch(_){} };
|
||||
logLine('', 'audioCtx primed state='+audioCtx.state);
|
||||
} catch(e){ logLine('err', 'audioCtx prime: '+e.message); }
|
||||
/* pre-bless a pool of audio elements for unmuted autoplay. Each one
|
||||
* gets play() called inside this click handler — the gesture grants
|
||||
|
|
@ -5342,8 +5356,8 @@ logLine('', 'ready — pick a handle, type a rendezvous code, enter the space');
|
|||
|
||||
<footer style="margin:2.2rem auto 0;font-size:0.65rem;color:#999;line-height:1.7;word-break:break-all;font-family:monospace">
|
||||
<span id="pi-seal" style="color:#777;cursor:default;user-select:none" title="">page integrity</span> · built <span class="stamp-date">2026-06-04</span><br>
|
||||
md5 <span class="stamp-md5">49f9782cba53987585b1e8e4bb9cd6bd</span><br>
|
||||
sha256 <span class="stamp-sha">b89d91a064e4b657ebe7f7b4bbcc85d935f7be141679369f88913c8d07a41e80</span><br>
|
||||
md5 <span class="stamp-md5">8bfa8fa66fb1493ee39b068ab635b95d</span><br>
|
||||
sha256 <span class="stamp-sha">101001e05d546c74994310129ce846c59598d7d2ef1ded43531612532db4a70e</span><br>
|
||||
<span style="color:#bbb">hashes are of this page with these two fields zeroed — to verify, blank them and re-hash</span><br>
|
||||
<span style="color:#bbb">one self-contained file — <strong>save a copy</strong> and verify against these hashes; point at your own servers with ?signal= and ?turncred=, or <a href="host-your-own.html" style="color:#999">host your own community</a></span>
|
||||
</footer>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue