Module transcribe

Source
Expand description

Speech → text (whisper.cpp), the subtitle generator on top of it, and the double-take detector.

Nothing here ships in the exe: no model, no ML crate. The model is downloaded once into Settings::cache_dir()\models (visible progress, size named before the click) and the inference is a child process, exactly like ffmpeg (media/ffpipe.rs) and yt-dlp: locate the exe, run it, parse its stdout, never block the UI. With neither installed the app is untouched — the pane just says what to install and where it looked.

whisper.cpp prints one line per segment, [00:00:01.000 --> 00:00:02.400] text, so the transcript streams in while it runs and the progress fraction is the last timestamp over the audio length. -ml 1 -sow makes those lines one word each — that is where the word timings come from, regrouped into sentences by group_words.

The subtitle conventions follow the usual auto-subs ones: ~42 characters a line, split on word boundaries, a minimum on-screen time that never eats the next cue.

Structs§

GroupOpts
How one-word segments are regrouped into sentences — every knob the “Regenerate” pass turns.
Job
A running transcription. segments() grows while it runs; progress drives the UI and cancels it.
Options
Segment
One transcribed span. words is filled only when the run asked for word timings.

Constants§

HOST 🔒
MERGE_GAP 🔒
Duplicate ranges closer than this are cut as one.
MIN_WORDS 🔒
Fewer words than this and a repeat is just normal speech (“yeah”, “okay”), not a second take.
MODELS
Downloadable whisper.cpp models: (name, file, download size in MB).

Functions§

default_model
Index into MODELS of Settings.transcribe_model (by name or file), else base.en.
download 🔒
download_model
Download a model into the cache. curl.exe ships with Windows 10 1803+; progress is the size of the .part file against the published one, because curl’s own meter needs a console.
dup_ranges
Timeline ranges of every take but the last of each group — what “cut the duplicates” removes. Sorted and merged so two duplicates in a row are one cut.
duplicate_takes
Groups of segments that say the same thing again — a flubbed line and its retakes. Each group is in time order with at least two members and the LAST one is the keeper. window is how long a silence may sit between one take and the next.
exe
A whisper.cpp binary, or None. main.exe is only accepted from a directory we were pointed at — a main.exe picked up off PATH would be anything at all.
exe_dir
Where the app looks for a whisper binary it did not find on PATH.
extract_wav 🔒
16 kHz mono wav of the clip’s range, through the ffmpeg we already ship next to.
group_words
One-word segments (-ml 1 -sow) → sentences, keeping the word timings. A sentence ends per GroupOpts: on punctuation, on a long gap, at max_chars, or at max_words.
have_model
Is the model there and not a truncated download?
install_hint
Exactly what to install and where it is expected (shown in the pane when exe() is None).
model_path
model_url
models_dir
%LOCALAPPDATA%\SimpleEditor\cache\models — where downloaded models live.
normalize
Lower-cased words with the punctuation stripped (“So, THAT one!” → [“so”, “that”, “one”]).
parse_line
[00:00:01.000 --> 00:00:02.400] Hello there → (1.0, 2.4, “Hello there”). Anything else (whisper’s banner, timings, blank-audio markers) is None.
retime
Source seconds → timeline seconds: the clip’s speed is one linear map over its whole range.
ripple_time
Where t ends up after those ranges are rippled out — None when t was inside one of them. Used to drag the cues and the transcript along with the cut.
run 🔒
settings 🔒
short_label
Name for a marker on a duplicate take: the transcript, short enough to read on the timeline.
similarity
How alike two token lists are, 0..=1: twice their longest common subsequence over their combined length. Order-aware (unlike a bag overlap) and forgiving of the “um“s a retake adds.
start
to_cues
Segments → subtitle cues: wrapped to max_chars per line, lines lines to a cue (joined with \n), timed by the word timings when there are any and proportionally to the characters otherwise, each held for at least min_dur unless the next cue needs the time. Where a sentence continues across the cue split, cont = (prefix, suffix) marks it: the suffix goes on the cut-off cue, the prefix on its continuation (e.g. ("…", " —")). ponytail: the marks are added after the wrap, so a marked line can run a few chars past max_chars.
wrap_words 🔒
Greedy line wrap on word boundaries; a single word longer than max_chars gets its own line.