diff --git a/web/zebra-spaces.html b/web/zebra-spaces.html
index 4e650b0..c35e358 100644
--- a/web/zebra-spaces.html
+++ b/web/zebra-spaces.html
@@ -946,10 +946,28 @@ async function refreshMicList(){
} catch(e){ logLine('err','could not list inputs: '+e.message); }
}
function tagTrack(t){ if (t) t.contentHint = musicMode ? 'music' : 'speech'; }
+/* Firefox sometimes ignores the EC/NS/AGC constraints at getUserMedia time
+ * for non-mic sources (e.g. PulseAudio monitor) and applies its default
+ * processing pipeline anyway. applyConstraints() after the fact tends to
+ * stick. Log the actual settings so we can see what the UA ended up with —
+ * silent disagreement between requested and effective constraints is what
+ * makes music-mode-on-a-monitor-source sound 'cleaned up'. */
+async function enforceMicConstraints(track){
+ if (!track) return;
+ try { await track.applyConstraints(micConstraints()); } catch(e){ logLine('', 'applyConstraints rejected: '+e.message); }
+ try {
+ const s = track.getSettings();
+ logLine('', 'mic track settings: '+JSON.stringify({
+ ec: s.echoCancellation, ns: s.noiseSuppression, agc: s.autoGainControl,
+ ch: s.channelCount, hz: s.sampleRate, dev: (s.deviceId||'').slice(0,8)
+ }));
+ } catch(_){}
+}
async function getMic(){
if (micStream) return micStream;
micStream = await navigator.mediaDevices.getUserMedia({ audio: micConstraints(), video:false });
- tagTrack(micStream.getAudioTracks()[0]);
+ const t = micStream.getAudioTracks()[0];
+ tagTrack(t); await enforceMicConstraints(t);
return micStream;
}
async function setSenderBitrate(sender){
@@ -998,6 +1016,7 @@ async function applyMicMode(){
const ns = await navigator.mediaDevices.getUserMedia({ audio: micConstraints(), video:false });
const nt = ns.getAudioTracks()[0];
tagTrack(nt); nt.enabled = !muted;
+ await enforceMicConstraints(nt);
async function swap(pc){
const sender = pc.getSenders().find(s=>s.track && s.track.kind==='audio') || pc.getSenders()[0];
if (sender){ try { await sender.replaceTrack(nt); } catch(_){} setSenderBitrate(sender); }
@@ -1707,8 +1726,8 @@ logLine('', 'ready — pick a handle, type a rendezvous code, enter the space');