feat(audio): migrate from ScriptProcessorNode to AudioWorkletNode for low-latency broadcasting
- Implemented `BroadcasterProcessor` for audio processing in a separate audio thread. - Replaced deprecated `ScriptProcessorNode` with `AudioWorkletNode` in `startBroadcast`. - Enhanced audio performance by reducing main thread interference and improving scalability. - Added `broadcaster-processor.js` to handle custom audio processing logic. This change ensures compatibility with modern browsers and improves broadcast audio quality.
This commit is contained in:
parent
f07b3fac56
commit
d3e3e92f9b
26
app.js
26
app.js
@ -86,32 +86,38 @@ async function startBroadcast() {
|
||||
|
||||
try {
|
||||
audioContext = new (window.AudioContext || window.webkitAudioContext)();
|
||||
|
||||
// Load and register the audio worklet processor
|
||||
await audioContext.audioWorklet.addModule('broadcaster-processor.js');
|
||||
|
||||
micStream = await navigator.mediaDevices.getUserMedia({
|
||||
audio: { deviceId: currentDeviceId ? { exact: currentDeviceId } : undefined },
|
||||
});
|
||||
|
||||
const source = audioContext.createMediaStreamSource(micStream);
|
||||
const processor = audioContext.createScriptProcessor(4096, 1, 1);
|
||||
|
||||
source.connect(processor);
|
||||
processor.connect(audioContext.destination);
|
||||
// Create AudioWorkletNode
|
||||
const broadcasterNode = new AudioWorkletNode(audioContext, 'broadcaster-processor');
|
||||
source.connect(broadcasterNode);
|
||||
|
||||
processor.onaudioprocess = (event) => {
|
||||
const audioData = event.inputBuffer.getChannelData(0);
|
||||
const buffer = b4a.from(new Float32Array(audioData).buffer);
|
||||
|
||||
// Send audio data to all connections
|
||||
// Handle audio data
|
||||
broadcasterNode.port.onmessage = (event) => {
|
||||
const buffer = event.data;
|
||||
for (const conn of conns) {
|
||||
conn.write(buffer);
|
||||
}
|
||||
};
|
||||
|
||||
broadcasterNode.connect(audioContext.destination); // Optional monitoring
|
||||
|
||||
isBroadcasting = true;
|
||||
console.log("Broadcasting started.");
|
||||
console.log("Broadcasting started with AudioWorklet.");
|
||||
} catch (err) {
|
||||
console.error("Error accessing microphone:", err);
|
||||
console.error("Error accessing microphone or setting up broadcast:", err);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Function to stop broadcasting and clean up resources
|
||||
function stopBroadcast() {
|
||||
if (!isBroadcasting) return;
|
||||
|
19
broadcaster-processor.js
Normal file
19
broadcaster-processor.js
Normal file
@ -0,0 +1,19 @@
|
||||
class BroadcasterProcessor extends AudioWorkletProcessor {
|
||||
process(inputs, outputs) {
|
||||
const input = inputs[0];
|
||||
const output = outputs[0];
|
||||
|
||||
if (input && output) {
|
||||
for (let channel = 0; channel < input.length; ++channel) {
|
||||
const inputChannel = input[channel];
|
||||
const outputChannel = output[channel];
|
||||
for (let i = 0; i < inputChannel.length; ++i) {
|
||||
outputChannel[i] = inputChannel[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
return true; // Keep the processor alive
|
||||
}
|
||||
}
|
||||
|
||||
registerProcessor('broadcaster-processor', BroadcasterProcessor);
|
13
index.html
13
index.html
@ -24,6 +24,18 @@
|
||||
}
|
||||
|
||||
</style>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
navigator.mediaDevices.getUserMedia({ audio: true })
|
||||
.then(stream => {
|
||||
// Handle audio stream here
|
||||
console.log("Microphone access granted:", stream);
|
||||
})
|
||||
.catch(error => {
|
||||
console.error("Microphone access denied:", error);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
<body class="bg-dark text-light">
|
||||
<div id="titlebar">
|
||||
@ -31,6 +43,7 @@
|
||||
</div>
|
||||
<div class="container mt-5 text-center">
|
||||
<h1>pearCast</h1>
|
||||
<div id="retry-message-bar" class="alert alert-warning d-none" role="alert"></div>
|
||||
<div id="setup" class="btn-group mt-4">
|
||||
<button id="create-station" class="btn btn-primary">Create Station</button>
|
||||
<button id="open-join-modal" class="btn btn-secondary" data-bs-toggle="modal" data-bs-target="#joinModal">Join Station</button>
|
||||
|
Loading…
Reference in New Issue
Block a user