Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 26 additions & 2 deletions static/js/juce-audio.js
Original file line number Diff line number Diff line change
Expand Up @@ -614,9 +614,22 @@ import { S } from './player-state.js';
let _lbStream = null, _lbCtx = null, _lbTap = null, _lbPageMuted = false;
let _loopbackUnavailable = false; // sticky: probe once, then fall back
async function _engageLoopback() {
// Chromium's default capture pipeline treats the audio track as a
// VOICE call: echo cancellation + noise suppression + AGC + mono
// downmix. On music that is the "tin can" sound (tester 2026-07-12).
// Request the raw stereo path explicitly — every constraint here is
// best-effort (ideal-valued), so unsupported ones degrade silently
// rather than failing the capture.
const stream = await navigator.mediaDevices.getDisplayMedia({
video: true,
audio: { suppressLocalAudioPlayback: true },
audio: {
suppressLocalAudioPlayback: true,
echoCancellation: false,
noiseSuppression: false,
autoGainControl: false,
channelCount: 2,
sampleRate: 48000,
},
});
for (const t of stream.getVideoTracks()) t.stop(); // required, unused
const track = stream.getAudioTracks()[0];
Expand All @@ -637,8 +650,19 @@ import { S } from './player-state.js';
_lbPageMuted = (await api.setPageMuted(true)) === true;
}
if (window._asioDiagEnabled?.()) {
// Full track settings: shows whether the raw-audio constraints
// (no EC/NS/AGC, stereo) actually took — a track that still
// reports echoCancellation=true or channelCount=1 explains
// "tin can" quality reports directly from the log.
const s = track.getSettings?.() || {};
console.log('[asio-diag] loopback: suppressed=', suppressed,
'pageMuted=', _lbPageMuted, 'rate=', _lbCtx.sampleRate);
'pageMuted=', _lbPageMuted, 'rate=', _lbCtx.sampleRate,
'track=', JSON.stringify({
sampleRate: s.sampleRate, channelCount: s.channelCount,
echoCancellation: s.echoCancellation,
noiseSuppression: s.noiseSuppression,
autoGainControl: s.autoGainControl,
}));
}
await api.setRendererBus(true, 1.0);
tap.active = true;
Expand Down
Loading