Tune live transcription for ~8 s latency
3 s dispatch tick (was 10), 2 s minimum chunk (was 5), 30 s force-cut for
unbroken speech (was 45), 0.5 s render debounce (was 1).
4 changed files+15−7
CLAUDE.mdmodified+3−1View file
@@ -75,7 +75,9 @@ src/
7575 line-JSON stdio; it reads raw flushed frame ranges straight from the
7676 growing WAVs (header bypassed). Chunks are cut at >= 300 ms of quiet
7777 (RMS < 300) tracked from the live sample stream — never mid-word — with a
78- 45 s force-cut for unbroken speech; all-quiet chunks skip ASR entirely.
78+ 30 s force-cut for unbroken speech; all-quiet chunks skip ASR entirely.
79+ Constants are tuned for ~8 s typical end-of-utterance-to-text latency
80+ (3 s tick, 2 s min chunk, 0.5 s render debounce).
7981 Results re-render transcript.md live and are persisted to asr/*.json in the
8082 offline format, so `transcribe` can re-render or upgrade models later. If
8183 the helper dies, it logs once and recording continues.
README.mdmodified+3−1View file
@@ -56,7 +56,9 @@ With `record --transcribe`, transcription happens **live during the meeting**:
5656 a single faster-whisper model stays loaded, audio is transcribed in chunks cut
5757 at natural pauses, and `<out>/transcript.md` is continuously rewritten — open
5858 it (or `watch cat`) while the meeting runs, and it is finalized the moment you
59-stop. If the transcriber ever fails, the recording is unaffected.
59+stop. Text typically appears within ~8 seconds of a pause (someone speaking
60+non-stop can lag up to ~30 s until the force-cut). If the transcriber ever
61+fails, the recording is unaffected.
6062
6163 Alternatively (or to redo a recording with a bigger model), transcribe
6264 afterwards:
package.jsonmodified+1−1View file
@@ -1,6 +1,6 @@
11 {
22 "name": "commonroom-recorder",
3- "version": "0.3.0",
3+ "version": "0.3.1",
44 "description": "CLI bot that joins a commonroom room and records every participant's audio (and the chat) to disk for transcription",
55 "type": "module",
66 "bin": {
src/livetranscribe.tsmodified+8−4View file
@@ -22,11 +22,15 @@ import type {WavWriter} from './wav.js'
2222 // If the helper dies, recording is NEVER affected: live transcription
2323 // disables itself with a log line, and `transcribe` can be run afterwards.
2424
25+// Tuned for ~8 s from end-of-utterance to text on screen: pause detect
26+// (~0.3 s) + WAV flush (~0.5 s avg) + tick (~1.5 s avg) + ASR (1-4 s with
27+// `small` on CPU) + render debounce (0.5 s). Smaller chunks trade a little
28+// per-chunk context/efficiency for latency; turn merging glues the text back.
2529 const QUIET_RMS = 300 // ~ -41 dBFS: below this a 10 ms frame counts as quiet
2630 const QUIET_CUT_MS = 300 // this much consecutive quiet = a safe cut point
27-const TICK_MS = 10000
28-const MIN_CHUNK_SEC = 5 // don't bother the model with less than this
29-const MAX_CHUNK_SEC = 45 // force a cut after this much unbroken speech
31+const TICK_MS = 3000
32+const MIN_CHUNK_SEC = 2 // don't bother the model with less than this
33+const MAX_CHUNK_SEC = 30 // force a cut after this much unbroken speech
3034 const FINISH_TIMEOUT_MS = 180000
3135
3236 interface AsrSegment {
@@ -382,7 +386,7 @@ export class LiveTranscriber {
382386 this.renderTimer = setTimeout(() => {
383387 this.renderTimer = null
384388 this.render(null)
385- }, 1000)
389+ }, 500)
386390 }
387391
388392 private render(endedAtMs: number | null) {