zebra-report: zebra-audio music mode (disable voice isolation)
This commit is contained in:
parent
cb0e5454b1
commit
d22c43a2e3
1 changed files with 44 additions and 5 deletions
|
|
@ -88,6 +88,9 @@
|
||||||
<div class="dot" id="dot-path"></div>
|
<div class="dot" id="dot-path"></div>
|
||||||
<span id="path-status" class="status-line">path: —</span>
|
<span id="path-status" class="status-line">path: —</span>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<label class="note"><input type="checkbox" id="music-mode"> music mode — raw mic, no echo/noise cancellation (for playing audio through it)</label>
|
||||||
|
</div>
|
||||||
<p class="note">
|
<p class="note">
|
||||||
both partners type the same code and hit call. audio is end-to-end
|
both partners type the same code and hit call. audio is end-to-end
|
||||||
encrypted (DTLS-SRTP), peer-to-peer when the network allows, relayed through
|
encrypted (DTLS-SRTP), peer-to-peer when the network allows, relayed through
|
||||||
|
|
@ -173,14 +176,44 @@ async function refreshTurnCred(){
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ---- audio ---- */
|
/* ---- audio ---- */
|
||||||
let micStream = null, audioCtx = null;
|
let micStream = null, audioCtx = null, musicMode = false;
|
||||||
|
/* voice mode = echo-cancel + noise-suppress + auto-gain (clean speech).
|
||||||
|
* music mode = all of that OFF so music/audio passes through un-mangled. */
|
||||||
|
function micConstraints(){
|
||||||
|
return musicMode
|
||||||
|
? { echoCancellation:false, noiseSuppression:false, autoGainControl:false }
|
||||||
|
: { echoCancellation:true, noiseSuppression:true, autoGainControl:true };
|
||||||
|
}
|
||||||
|
function tagTrack(t){ if (t) t.contentHint = musicMode ? 'music' : 'speech'; }
|
||||||
|
async function setSenderBitrate(sender){
|
||||||
|
if (!sender) return;
|
||||||
|
try {
|
||||||
|
const p = sender.getParameters();
|
||||||
|
if (!p.encodings || !p.encodings.length) p.encodings = [{}];
|
||||||
|
p.encodings[0].maxBitrate = musicMode ? 160000 : 40000; /* bits/s */
|
||||||
|
await sender.setParameters(p);
|
||||||
|
} catch(_){}
|
||||||
|
}
|
||||||
async function getMic(){
|
async function getMic(){
|
||||||
if (micStream) return micStream;
|
if (micStream) return micStream;
|
||||||
micStream = await navigator.mediaDevices.getUserMedia({
|
micStream = await navigator.mediaDevices.getUserMedia({ audio: micConstraints(), video:false });
|
||||||
audio: { echoCancellation:true, noiseSuppression:true, autoGainControl:true }, video:false
|
tagTrack(micStream.getAudioTracks()[0]);
|
||||||
});
|
|
||||||
return micStream;
|
return micStream;
|
||||||
}
|
}
|
||||||
|
/* switch voice<->music mid-call: re-acquire the mic and hot-swap the track on
|
||||||
|
* the live connection (no renegotiation), preserving mute state */
|
||||||
|
async function applyMicMode(){
|
||||||
|
const ns = await navigator.mediaDevices.getUserMedia({ audio: micConstraints(), video:false });
|
||||||
|
const nt = ns.getAudioTracks()[0];
|
||||||
|
tagTrack(nt); nt.enabled = !muted;
|
||||||
|
if (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); }
|
||||||
|
}
|
||||||
|
if (micStream) micStream.getTracks().forEach(t=>t.stop());
|
||||||
|
micStream = ns;
|
||||||
|
meterFor(micStream, 'meter-mic');
|
||||||
|
}
|
||||||
function meterFor(stream, fillId){
|
function meterFor(stream, fillId){
|
||||||
if (!audioCtx) audioCtx = new (window.AudioContext||window.webkitAudioContext)();
|
if (!audioCtx) audioCtx = new (window.AudioContext||window.webkitAudioContext)();
|
||||||
const src = audioCtx.createMediaStreamSource(stream);
|
const src = audioCtx.createMediaStreamSource(stream);
|
||||||
|
|
@ -214,7 +247,8 @@ function setPath(msg, cls){ const e=$('path-status'); e.textContent=msg; e.class
|
||||||
async function ensurePC(){
|
async function ensurePC(){
|
||||||
if (pc) return pc;
|
if (pc) return pc;
|
||||||
pc = new RTCPeerConnection(rtcConfig);
|
pc = new RTCPeerConnection(rtcConfig);
|
||||||
for (const tr of micStream.getTracks()) pc.addTrack(tr, micStream);
|
for (const tr of micStream.getTracks()) { tagTrack(tr); pc.addTrack(tr, micStream); }
|
||||||
|
setSenderBitrate(pc.getSenders().find(s=>s.track && s.track.kind==='audio'));
|
||||||
pc.ontrack = (ev) => {
|
pc.ontrack = (ev) => {
|
||||||
const stream = ev.streams[0] || new MediaStream([ev.track]);
|
const stream = ev.streams[0] || new MediaStream([ev.track]);
|
||||||
$('remote-audio').srcObject = stream;
|
$('remote-audio').srcObject = stream;
|
||||||
|
|
@ -353,6 +387,11 @@ $('btn-call').addEventListener('click', startCall);
|
||||||
$('btn-hangup').addEventListener('click', hangup);
|
$('btn-hangup').addEventListener('click', hangup);
|
||||||
$('btn-mute').addEventListener('click', toggleMute);
|
$('btn-mute').addEventListener('click', toggleMute);
|
||||||
$('rdv-code').addEventListener('keydown', e=>{ if(e.key==='Enter'){ e.preventDefault(); startCall(); } });
|
$('rdv-code').addEventListener('keydown', e=>{ if(e.key==='Enter'){ e.preventDefault(); startCall(); } });
|
||||||
|
$('music-mode').addEventListener('change', async (e)=>{
|
||||||
|
musicMode = e.target.checked;
|
||||||
|
logLine('', 'mic mode: '+(musicMode?'MUSIC — raw, no voice isolation':'VOICE — echo/noise cancel'));
|
||||||
|
if (micStream){ try { await applyMicMode(); } catch(err){ logLine('err','mic mode switch failed: '+err.message); } }
|
||||||
|
});
|
||||||
logLine('', 'ready — type a rendezvous code and call. mic stays muted to the room until connected.');
|
logLine('', 'ready — type a rendezvous code and call. mic stays muted to the room until connected.');
|
||||||
})();
|
})();
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue