From 20b1f2c54a0ad8d7649bb982af45c36b6d2d7767 Mon Sep 17 00:00:00 2001 From: Kresna Date: Sat, 1 Aug 2026 13:40:42 +0700 Subject: [PATCH] fix(voice-to-text): cache the model + pause preview during transcribe Two issues from testing: - Every Transcribe click re-initialized the Whisper pipeline (re-reading weights, re-running the load progress, blocking the thread). Cache the built transcriber by model id and reuse it; the download progress now only shows on first load. - The audio preview kept playing when pressing stop because ONNX inference blocks the main thread and freezes the native controls. Pause the preview programmatically when transcribe (or a new recording) starts. Co-Authored-By: Claude Opus 4.8 (1M context) --- src/islands/media/VoiceToText.tsx | 9 ++++++--- src/tools/media/stt.engine.ts | 20 ++++++++++++++++++-- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/src/islands/media/VoiceToText.tsx b/src/islands/media/VoiceToText.tsx index 8ea89aa..6cb0f2e 100644 --- a/src/islands/media/VoiceToText.tsx +++ b/src/islands/media/VoiceToText.tsx @@ -38,6 +38,7 @@ export default function VoiceToText() { const [subFormat, setSubFormat] = useState<'srt' | 'vtt'>('srt'); const [error, setError] = useState(''); const urlRef = useRef(''); + const audioRef = useRef(null); // Pick up a finished recording as the working audio. useEffect(() => { @@ -66,6 +67,7 @@ export default function VoiceToText() { if (recorder.recording) { recorder.stop(); } else { + audioRef.current?.pause(); setSegments(null); setError(''); recorder.start(); @@ -74,14 +76,15 @@ export default function VoiceToText() { const transcribe = async () => { if (!audioBlob) return; + audioRef.current?.pause(); // don't leave the preview playing while inference blocks the thread setError(''); setSegments(null); setTranscribing(true); - setModelProgress(0); + setModelProgress(null); // only shows once real download progress fires (first load) try { const audio = await decodeToMono16k(audioBlob); const engine = await createTranscriber(model, r => setModelProgress(r)); - setModelProgress(null); // model ready — now inference (indeterminate) + setModelProgress(null); // model ready (or cached) — now inference (indeterminate) const segs = await engine.transcribe(audio); setSegments(segs); setEditedText(segmentsToText(segs)); @@ -135,7 +138,7 @@ export default function VoiceToText() { {recorder.error && {recorder.error.message}} {audioUrl && ( -