Merge origin/fix/asio-all-app-audio (teammate teardown fix)

Kept theirs: fresh loopback context per session + close() on teardown
(orphaned-worklet fix) and its test. Kept ours: deferred debug-gated
install lines, cache-control, decision-vector diag.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
OmikronApex
2026-07-11 18:05:16 +02:00
co-authored by Claude Fable 5
2 changed files with 27 additions and 19 deletions
+17
View File
@@ -54,6 +54,7 @@ function makeFakeContext(sampleRate = 48000) {
this.mediaStreamSource = stream;
return { connect() {}, disconnect() {} };
},
close() { this.closed = true; return Promise.resolve(); },
};
return ctx;
}
@@ -231,6 +232,22 @@ test('exclusive output + loopback available → engages without any song loaded'
assert.equal(sb.__calls.setPageMuted.length, 0, 'suppress constraint honoured — no page mute');
});
test('loopback context is closed on disengage (no orphaned tap worklet)', async () => {
let excl = true;
const stream = makeLoopbackStream();
const sb = makeSandbox({ exclusive: () => excl, displayMedia: () => Promise.resolve(stream) });
await sb.window._reevaluateRendererBus(); // engage loopback
const lbCtx = sb.__createdContexts.at(-1);
assert.equal(lbCtx?.mediaStreamSource, stream, 'loopback engaged');
assert.notEqual(lbCtx.closed, true, 'context live while engaged');
excl = false;
await sb.window._reevaluateRendererBus(); // disengage
assert.equal(lbCtx.closed, true, 'loopback context closed on disengage');
assert.ok(stream.__stopped.includes('audio'), 'capture stream stopped');
});
test('loopback preferred over stems when both available', async () => {
const stream = makeLoopbackStream();
const sb = makeSandbox({ exclusive: () => true, displayMedia: () => Promise.resolve(stream) });