From 4d7c487614c4728b939e347b0117a621ef0fd175 Mon Sep 17 00:00:00 2001 From: Quentin Fuxa Date: Tue, 16 Sep 2025 23:31:00 +0200 Subject: [PATCH] replace deprecated ScriptProcessorNode with AudioWorklet --- whisperlivekit/web/live_transcription.js | 38 ++++++++++++++++-------- whisperlivekit/web/pcm_worklet.js | 16 ++++++++++ 2 files changed, 41 insertions(+), 13 deletions(-) create mode 100644 whisperlivekit/web/pcm_worklet.js diff --git a/whisperlivekit/web/live_transcription.js b/whisperlivekit/web/live_transcription.js index 12d738c..e649912 100644 --- a/whisperlivekit/web/live_transcription.js +++ b/whisperlivekit/web/live_transcription.js @@ -12,7 +12,7 @@ let timerInterval = null; let audioContext = null; let analyser = null; let microphone = null; -let scriptProcessor = null; +let workletNode = null; let recorderWorker = null; let waveCanvas = document.getElementById("waveCanvas"); let waveCtx = waveCanvas.getContext("2d"); @@ -459,9 +459,12 @@ async function startRecording() { microphone = audioContext.createMediaStreamSource(stream); microphone.connect(analyser); - scriptProcessor = audioContext.createScriptProcessor(4096, 1, 1); - microphone.connect(scriptProcessor); - scriptProcessor.connect(audioContext.destination); + if (!audioContext.audioWorklet) { + throw new Error("AudioWorklet is not supported in this browser"); + } + await audioContext.audioWorklet.addModule("/web/pcm_worklet.js"); + workletNode = new AudioWorkletNode(audioContext, "pcm-forwarder", { numberOfInputs: 1, numberOfOutputs: 0, channelCount: 1 }); + microphone.connect(workletNode); recorderWorker = new Worker("/web/recorder_worker.js"); recorderWorker.postMessage({ @@ -477,12 +480,16 @@ async function startRecording() { } }; - scriptProcessor.onaudioprocess = (e) => { - const inputData = e.inputBuffer.getChannelData(0); - recorderWorker.postMessage({ - command: "record", - buffer: inputData.buffer, - }, [inputData.buffer]); + workletNode.port.onmessage = (e) => { + const data = e.data; + const ab = data instanceof ArrayBuffer ? data : data.buffer; + recorderWorker.postMessage( + { + command: "record", + buffer: ab, + }, + [ab] + ); }; startTime = Date.now(); @@ -526,9 +533,14 @@ async function stopRecording() { recorderWorker = null; } - if (scriptProcessor) { - scriptProcessor.disconnect(); - scriptProcessor = null; + if (workletNode) { + try { + workletNode.port.onmessage = null; + } catch (e) {} + try { + workletNode.disconnect(); + } catch (e) {} + workletNode = null; } if (microphone) { diff --git a/whisperlivekit/web/pcm_worklet.js b/whisperlivekit/web/pcm_worklet.js new file mode 100644 index 0000000..c35858d --- /dev/null +++ b/whisperlivekit/web/pcm_worklet.js @@ -0,0 +1,16 @@ +class PCMForwarder extends AudioWorkletProcessor { + process(inputs) { + const input = inputs[0]; + if (input && input[0] && input[0].length) { + // Forward mono channel (0). If multi-channel, downmixing can be added here. + const channelData = input[0]; + const copy = new Float32Array(channelData.length); + copy.set(channelData); + this.port.postMessage(copy, [copy.buffer]); + } + // Keep processor alive + return true; + } +} + +registerProcessor('pcm-forwarder', PCMForwarder);