From 029719f50a93a201aaa4ad9f188894a950a94fd6 Mon Sep 17 00:00:00 2001 From: taran nathan Date: Wed, 22 Jul 2026 13:48:31 -0700 Subject: [PATCH] functions, doesn't overwrite --- script.js | 101 ++++++++++++++++++++++++++++++++---------------------- 1 file changed, 60 insertions(+), 41 deletions(-) diff --git a/script.js b/script.js index c1a1991..04e0dfb 100644 --- a/script.js +++ b/script.js @@ -12,6 +12,7 @@ let streamProcessorNode; let streamMuteNode; let committedTranscription = ""; let partialTranscription = ""; +let lastCommittedTurn = -1; const startButton = document.getElementById("btnStart"); const stopButton = document.getElementById("btnStop"); @@ -32,7 +33,9 @@ function renderTranscription() { return; } - transcriptionElement.textContent = partial ? `${committed} ${partial}`.trim() : committed; + transcriptionElement.textContent = partial + ? `${committed} ${partial}`.trim() + : committed; } function cleanupCaptureResources(closeTranscriptionSocket = true) { @@ -56,7 +59,10 @@ function cleanupCaptureResources(closeTranscriptionSocket = true) { audioContext.close(); } - if (closeTranscriptionSocket && transcriptionSocket && transcriptionSocket.readyState === WebSocket.OPEN) { + if ( + closeTranscriptionSocket && transcriptionSocket && + transcriptionSocket.readyState === WebSocket.OPEN + ) { transcriptionSocket.close(); } @@ -102,43 +108,34 @@ function extractTranscriptFromRealtimeEvent(payload) { } if (typeof payload.error === "string") { - return { - kind: "error", - text: payload.error - }; + return { kind: "error", text: payload.error }; } - const typeValue = typeof payload.type === "string" ? payload.type.toLowerCase() : ""; - const finalByType = - typeValue.includes("termination") || - typeValue.includes("final") || - typeValue.includes("completed"); - - const candidates = [ - payload.text, - payload.transcript, - payload.delta, - payload.partial, - payload.final, - payload.turn?.text, - payload.turn?.transcript, - payload.turn?.final, - payload.result?.text, - payload.result?.transcript - ]; - - const transcriptText = candidates.find((value) => typeof value === "string" && value.trim()); - if (!transcriptText) { + if (payload.type !== "Turn") { + return null; + } + + const transcriptText = typeof payload.transcript === "string" + ? payload.transcript + : ""; + if (!transcriptText.trim()) { return null; } return { - kind: finalByType || payload.is_final === true || payload.final === true ? "final" : "partial", - text: transcriptText + kind: payload.end_of_turn === true ? "final" : "partial", + text: transcriptText, + turnOrder: typeof payload.turn_order === "number" + ? payload.turn_order + : undefined, }; } -function downsampleToTargetSampleRate(float32Samples, inputSampleRate, targetSampleRate) { +function downsampleToTargetSampleRate( + float32Samples, + inputSampleRate, + targetSampleRate, +) { if (targetSampleRate >= inputSampleRate) { return float32Samples; } @@ -154,7 +151,11 @@ function downsampleToTargetSampleRate(float32Samples, inputSampleRate, targetSam let total = 0; let count = 0; - for (let i = inputIndex; i < nextInputIndex && i < float32Samples.length; i += 1) { + for ( + let i = inputIndex; + i < nextInputIndex && i < float32Samples.length; + i += 1 + ) { total += float32Samples[i]; count += 1; } @@ -201,8 +202,11 @@ function connectTranscriptionSocket() { } catch { return; } + console.log(parsed); + console.log(JSON.stringify(parsed, null, 2)); const transcriptEvent = extractTranscriptFromRealtimeEvent(parsed); + console.log(transcriptEvent); if (!transcriptEvent) { return; } @@ -213,7 +217,10 @@ function connectTranscriptionSocket() { } if (transcriptEvent.kind === "final") { - appendCommittedTranscription(transcriptEvent.text); + if (transcriptEvent.turnOrder > lastCommittedTurn) { + lastCommittedTurn = transcriptEvent.turnOrder; + appendCommittedTranscription(transcriptEvent.text); + } } else { updatePartialTranscription(transcriptEvent.text); } @@ -235,12 +242,15 @@ startButton.addEventListener("click", async () => { try { committedTranscription = ""; partialTranscription = ""; + lastCommittedTurn = -1; renderTranscription(); - microphoneStream = await navigator.mediaDevices.getUserMedia({ audio: true }); + microphoneStream = await navigator.mediaDevices.getUserMedia({ + audio: true, + }); systemStream = await navigator.mediaDevices.getDisplayMedia({ video: true, - audio: true + audio: true, }); console.log(systemStream.getTracks()); @@ -274,7 +284,10 @@ startButton.addEventListener("click", async () => { streamMuteNode.connect(audioContext.destination); streamProcessorNode.onaudioprocess = (audioProcessEvent) => { - if (!transcriptionSocket || transcriptionSocket.readyState !== WebSocket.OPEN) { + if ( + !transcriptionSocket || + transcriptionSocket.readyState !== WebSocket.OPEN + ) { return; } @@ -282,7 +295,7 @@ startButton.addEventListener("click", async () => { const downsampled = downsampleToTargetSampleRate( channelSamples, audioContext.sampleRate, - TARGET_SAMPLE_RATE + TARGET_SAMPLE_RATE, ); const pcm16Buffer = float32ToPcm16Buffer(downsampled); transcriptionSocket.send(pcm16Buffer); @@ -292,9 +305,11 @@ startButton.addEventListener("click", async () => { "audio/webm;codecs=opus", "audio/webm", "audio/ogg;codecs=opus", - "audio/ogg" + "audio/ogg", ]; - const selectedMimeType = preferredMimeTypes.find((type) => MediaRecorder.isTypeSupported(type)); + const selectedMimeType = preferredMimeTypes.find((type) => + MediaRecorder.isTypeSupported(type) + ); console.log("selected mime type:", selectedMimeType); mediaRecorder = selectedMimeType ? new MediaRecorder(mixedAudioStream, { mimeType: selectedMimeType }) @@ -309,8 +324,10 @@ startButton.addEventListener("click", async () => { }); mediaRecorder.addEventListener("stop", () => { - const audioBlob = new Blob(audioChunks, { type: mediaRecorder.mimeType || "audio/webm" }); - console.log(audioBlob) + const audioBlob = new Blob(audioChunks, { + type: mediaRecorder.mimeType || "audio/webm", + }); + console.log(audioBlob); if (recordedAudioUrl) { URL.revokeObjectURL(recordedAudioUrl); @@ -352,7 +369,9 @@ stopButton.addEventListener("click", () => { return; } - if (transcriptionSocket && transcriptionSocket.readyState === WebSocket.OPEN) { + if ( + transcriptionSocket && transcriptionSocket.readyState === WebSocket.OPEN + ) { transcriptionSocket.send(JSON.stringify({ type: "Terminate" })); } -- 2.47.3