Repository navigation
Releases: ghalymt/word-align
Release list
WordAlign 2.2.1
Subtitles you can deliver. Every run writes <name>_final.srt: every word and tag of the transcript, laid out by subtitling rules and timed the way a reviewer finishes a job -- in sync with the speech first, the other rules as far as the speech allows.
Upgrade notes (behaviour changes)
- Cues now start with their first word and run on to the next cue across short pauses; a fast short sentence keeps its own cue even under
--min-cue-ms.--no-sync-starts,--chain-gap-ms 0and--lead-out-ms 0bring back the 2.2.0 timing. _sentence_level.srt(and VTT/ASS) carry the speech only; the editor's tags are in_final.srt(and_combined.srtwith--tags).
Added
-
<name>_final.srton every run — the deliverable, written after the combined file: every word and every[tag]of the transcript in order, within the layout and timing rules, checked when it is written (summary in the log, details underfinal_checkin the job manifest, warnings for anything left). See The final SRT in the user guide. -
Subtitle line-break rules for English (
wordalign/linebreak.py): subject + verb, auxiliaries, phrasal verbs, verb + object pronoun, names and titles, numbers and units stay together; breaks prefer sentence ends, then clause punctuation, then conjunctions and prepositions. -
Timing in a reviewer's style (measured on a finished, human-reviewed job): cues start with their first word (
--no-sync-startslets a short cue start earlier to reach--min-cue-ms), run on to the next cue across pauses up to 1 s (--chain-gap-ms), and linger 250 ms after a longer pause (--lead-out-ms). A short sentence keeps its own short cue instead of being joined to its neighbour; the final check lists those as "kept in sync" rather than as failures. Sync with the audio comes before the duration and reading-speed rules everywhere: overlap fixes and boundary moves never push a cue's start past its first word. -
The review queue flags cues next to a sound tag (words under applause or laughter are often mistimed).
wordalign-bench fit-triagealso finds a finished file named<name>*reviewed*.srt, and counts only real fixes (changed words, moved starts, ends cut before the words were over) -- not cues cut differently or left on screen longer.
Changed
- Sound tags never overlap speech. The transcript's
[tag]lines are placed in the pause where the editor put them (timed by a matching sound event, cut back to the pause), or share the neighbouring cue when the pause is too short._combined.srtand_audio_tags.srtuse the same placement, and an input SRT's tags are kept too. _sentence_level.srt(and VTT/ASS) carry the speech only; tags are in the final and combined files. The review queue and_corrected.srtfollow the final cues.- Transcript lines too long for
--max-lines×--cpl, or on screen longer than--max-duration-ms, are split at their best break instead of being left as one long line; cues never merge past the duration limit. - Short and fast cues use the free time after them and can move a boundary to a better word; with
--no-sync-startsthey may also start early, borrow a few ms from a neighbour or join it. Every cue keeps at least 1 ms from the next. - Cue boundaries that split a phrase are moved to a cleaner word nearby, or to punctuation within the same two cues; a boundary at a sentence end is never moved.
- A
[tag]line inside a sentence ("but in" /[chuckles]/ "a tornado.") stays inline where the editor put it; a tag between sentences gets a cue of its own filling its pause (down to 0.5 s), timed by a matching sound event when there is one.
Fixed
- The WhisperX worker finds the alignment model in the models folder or its
hub/subfolder (it only looked in the first). - A transcript line that is only punctuation (a closing quote on its own line) no longer gives a cue a guessed start; it joins the word before it.
- A broken-off quote (
"I don't--") ends a sentence, so the next sentence starts a new cue.
WordAlign 2.2.0
Bad audio. A new robust flow (--profile robust, GUI preset Robust) measures the recording, cleans only the parts that need it, lets several ASR engines vote, times words with forced alignment on the original audio, and sorts every cue into a review queue. Every new stage is optional and skipped with a message when its model is not installed. English first; see docs/robust_flow.md.
Upgrade notes (behaviour changes)
- Word timing uses anchors + forced alignment by default (
--timing anchor): agreeing engines give anchors, Qwen3-ForcedAligner (or wav2vec2) aligns the windows between them on the original audio, MFA and pause-aware interpolation fill the rest.--timing fill_onlybrings back the 2.1 waterfall;--no-forced-alignkeeps the engine times. - Every run with anchor timing (the default) writes
<name>_review.json(cue tiers Accept / Quick check / Redo with reasons) unless--no-triageis given. - The audio analysis stage runs by default when its model is present (Silero VAD;
--no-vadturns it off), and the hallucination filter drops engine words the audio does not support (--no-hallucination-filter). - Speaker labels use pyannote Community-1 (falls back to 3.1); accept its terms on Hugging Face once.
- Transcript-free mode skips Vosk when two timed voters already anchor the timing and a forced aligner (Qwen3-ForcedAligner or wav2vec2) is installed.
- The Qwen3-ForcedAligner language list in the model catalog was wrong (it listed Arabic); it now matches the model: zh, en, yue, fr, de, it, ja, ko, pt, ru, es.
Added
- Audio analysis: Silero VAD (ONNX, no torch in the app), VAD-planned ASR chunks, WADA-SNR, narrowband and clipping detection, and timed sound events (PretrainedSED BEATs) per chunk.
- Gated cleanup (
--cleanup auto|all|off): RoFormer vocal separation for loud music, RoFormer crowd removal, MossFormerGAN for steady noise, mixed into a separate ASR track; never on phone or clipped audio. - ROVER voting over Cohere Transcribe, Qwen3-ASR, parakeet-ultra, WhisperX and Vosk (
--voters), per VAD chunk, with each word's agreement and the losing readings kept for review. - Overlap map (
--overlap-map) and overlapped-speech ASR with SE-DiCoW (--overlap-asr); two-speaker dash cues (--dash-cues,--dash-style). - SDH tag proposals from the sound events (
--tags). - Review queue in the GUI: loop a cue, apply a voter's alternative with one click, accept with Ctrl+Enter. Corrections go to
<name>_corrected.srt(plus an edit log); the pipeline's files are never changed. - Triage calibration:
wordalign-bench fit-triage --jobs FOLDERlearns the risk weights from your corrected jobs. - Benchmark:
wordalign-benchbuilds a suite from public data (LibriSpeech alignments, MUSAN noise, FMA music) and scores WER, timing, invented words and triage per condition; see docs/benchmark.md. - Setup:
scripts/install_robust.ps1(venvs and models),scripts/fetch_models.py(--listshows what is installed),wordalign --doctor,scripts/smoke_robust.py(one real call per backend). - Configurable locations:
--models-dir/WORDALIGN_MODELS_DIR,WORDALIGN_VENVS_DIR,WORDALIGN_<NAME>_PYTHONfor every backend,WORDALIGN_VAD_MODEL,WORDALIGN_SED_DIR. - The model catalog lists the new models with their licenses.
Measured (wordalign-bench, quick suite, RTX 4070 Ti)
Five minutes of LibriSpeech speech in nine conditions (clean, noise 5 dB, music 0 dB, six-talker babble 0 dB, reverb 1.0 s, μ-law phone, a second talker at −5 dB, music only, noise only), every run with an empty cache. "2.1" is the 2.1 behaviour run by the 2.2 code (--timing fill_only --no-vad --no-hallucination-filter --no-triage).
| 2.1 | 2.2 defaults | --profile robust |
|
|---|---|---|---|
| Word-start error, known transcript (mean) | 868 ms | 69 ms | 70 ms |
| Words within 100 ms, known transcript | 83.0% | 90.1% | 90.1% |
| WER, no transcript | 16.9% | 15.0% | 15.3% |
| Word-start error, no transcript (mean) | 225 ms | 73 ms | 106 ms |
| Invented words per 10 min of music/noise only | 300 | 0 | 0 |
| Erroneous cues the review queue flags (no transcript) | – | 88% | 77% |
| Run time for the 5-minute file | 238–289 s | 234–282 s | 195–268 s |
By condition: reverb WER 43% → 2.6%; babble timing with a known transcript 5.8 s → 0.2 s. Worse than 2.1: transcript-free WER under 0 dB music with sung vocals (0% → 12–13%: the voters also transcribe the lyrics) and under 0 dB babble (73% → 84–87%, unusable either way). On this synthetic suite the robust profile's extra voters and cleanup did not beat the 2.2 defaults; whether they help on real recordings with music beds, crowds and crosstalk is not measured yet, so compare on your own material (see docs/benchmark.md).
Fixed
- Qwen chunks that ran out of GPU memory are split and retried; pieces that still fail are reported instead of being silently empty.
- WhisperX finds its venv through the same lookup as the other backends.
- MFA from a conda environment (the bundled
venvs\mfa,WORDALIGN_MFA, oraligner/mfaunder your conda folders) is now preferred over anmfaon PATH, and is run as if activated: its DLL folders on PATH and the user site-packages ignored. A pip-installed MFA in the user site-packages (nokalpyon Windows) used to win and fail on every run. --doctorstarts MFA the same way instead of only finding it, so a broken MFA is reported.- MFA only fills words that have no time; a word already timed by an engine, the aligner or a voter is no longer re-aligned (and overwritten). Each gap is aligned with three timed words of context on each side, which fixed gap words being pinned to the start of their slice (0.5 s early).
- A forced-aligned word no longer vanishes from the word-level SRT when the aligner gives it no length.
WordAlign 2.1.0
2.1.0 — 2026-10-01
New output formats, speaker labels, batch processing, a GUI job queue and a long list of fixes. Pull request numbers refer to ghalymt/word-align.
Upgrade notes (behaviour changes)
MFA never borrows the English model. For a language without a pretrained MFA model, or whose model is not installed, MFA is skipped with a warning; it used to align with the English acoustic model and produce wrong timings (#17).
MFA default is the same everywhere: on in the CLI and in the GUI's Balanced and Maximum Quality presets, and it only runs when installed and available for the language (#17). The GUI's engine checkboxes now override the preset (#10), and explicit --use-mfa / --no-* flags override --engines (#3).
Ensemble mode output changed. The primary transcript is split into cues (it used to come out as one cue for the whole file) and Vosk/Qwen refine its timings (#7).
GUI server: requests from other websites are refused (no CORS headers, cross-origin POSTs and foreign Host headers get 403). Opening app.html from disk no longer works; use wordalign --gui. For LAN access set WORDALIGN_ALLOWED_HOSTS (#4, #19).
GUI jobs queue and run one at a time by default (WORDALIGN_MAX_CONCURRENT_JOBS); jobs left running by a previous session are marked interrupted on start (#20).
python -m wordalign no longer falls back to the v1 CLI when the v2 CLI fails to import; the real error is shown (#15).
The release ZIP holds the app only. Models go in models\ next to WordAlign.exe; MODELS.txt in the ZIP says where each kind goes. build.py --zip-with-models still builds a full offline bundle (#23).
Added
WebVTT and ASS export: --formats srt,vtt,ass, GUI toggles, profile export.vtt / export.ass (#18).
Speaker labels: --diarize [--speakers N] with pyannote.audio (needs HF_TOKEN). Cues never mix speakers; names appear as WebVTT voices, the ASS Name field, an SRT prefix and transcript paragraphs (#22).
Batch mode: --batch FOLDER [--recursive] [--skip-existing] pairs talk.mp4 with talk.txt/talk.srt, keeps going after a failure and writes wordalign_batch_report.json (#21).
--max-lines N lays cues out on up to N lines; --max-lines 1 never adds a break (#16).
GPU scheduler in use: WhisperX and Qwen take a process-wide GPU slot, so two jobs never load GPU models at once (WORDALIGN_GPU_SLOTS) (#20).
Streaming uploads: GUI uploads are written to disk as they arrive instead of being held in memory (#23).
Tests and CI: CI on Linux and Windows (Python 3.10 and 3.12) for every pull request (#6); golden-file tests through the real pipeline and a real-audio timing accuracy job with Vosk (#13).
Fixed
Failed or cancelled engine runs were cached and replayed for 30 days (#2).
Reference mode with an .srt transcript: [tags] shifted every later cue, and preserved cues were not laid out (#8).
--punctuation: the second line of two-line cues was dropped, long files never used the LLM, and real cues were rejected as commentary (#9).
QA issues moved between jobs, global glossary terms were duplicated, trailing untimed gaps were missed (#11).
Windows: crash after a successful run when output was redirected, and cp1252 crashes in the WhisperX/Qwen workers, llama-cli and MFA output (#12, #14).
MFA temp directories leaked; --legacy-ensemble crashed when every engine failed; primary-transcript runs bypassed the stage cache (#12, #14).
Five-line cues fell back to one long line; impossible layouts (--max-lines 0, --cpl 0, negative durations) were accepted (#16).
A blank line inside a cue ended the WebVTT cue early (#18).
Batch: equally named files in different subfolders overwrote each other (#21). --speakers N without --diarize was ignored (#22).
Uploaded file names with ; lost their extension; a trailing CR/LF of an uploaded file was stripped (#10, #23).
A fresh clone was missing the wordalign/models package because of a .gitignore rule (#1).
Security
The local API sent Access-Control-Allow-Origin: *, so any website could read it (#4); cross-site POSTs could still start runs or clear the cache, and DNS rebinding was possible (#19).
Server text (error messages, output paths) was inserted into the GUI as HTML: an audio file named like an HTML tag could run script in the GUI (#25).
Refused requests and JSON bodies are no longer read into memory whatever their size (#19, #25).
WordAlign 2.0.0
WordAlign 2.0 — GUI, Smart Transcript Review, portable builds
For non-technical users: download WordAlign-portable.zip, unzip, double-click Launch WordAlign.vbs. No Python, no terminal.
Highlights
- Web GUI with dark mode (default) + light toggle, drag-drop files, quality presets, live progress
- Smart Transcript Review: punctuation/capitalization restoration (local LLM + rule-based), realignment, CPL fixing, hallucination checks
- Plugin engine architecture — add engines without touching core/CLI/GUI
- Model Manager: auto-detects local Vosk/Whisper/HF/MFA models; all paths configurable
- MFA forced alignment for 61 languages, threaded through gap-filling
- Fingerprint cache, job manifests, SQLite history, crash recovery, benchmark suite
- 151 tests passing; v1 algorithms preserved unchanged
See README for Windows/Linux installs and the full CLI reference.
word-align 1.0.0
First stable release. Word-accurate subtitle timing from audio + transcript, or from audio alone.
Two modes, both GPU-verified end-to-end
- Reference mode — the production-derived waterfall: Vosk → WhisperX/wav2vec2 → MFA gap-filling → interpolation → segmenter.
- Ensemble mode — no transcript needed: WhisperX + Qwen3-ASR + Vosk vote on the words, Vosk seeds the timing, and low-agreement words are flagged for review. Qwen runs out-of-process via a subprocess bridge so its dependencies never clash with the WhisperX stack.
Subtitle layout follows the .srt standard
Configurable per-line CPL × line count — --max-cpl 42 (regular video) or 32 (vertical/social), --max-lines, --max-duration-ms, --min-cue-ms.
Review-ready output
- No forced bad breaks (gold → silver split, else leave the line visible for a human).
- Abbreviations stay whole (
St. Michael'sis never split at the period);[music]-style tags never merge into dialogue. - Overlaps resolved to zero without moving a Vosk-anchored timestamp; no sub-frame cues.
Quality
23 logic tests (no GPU/audio needed), several pinned to specific bugs. MIT licensed.