This repo runs automated TTS evals against a generated inputs/ tree and a shared refs/ tree.
Current runners:
ctc: transcript-faithfulness viatorchaudioWAV2VEC2_ASR_LARGE_960Hctc_tortoise: transcript-faithfulness viajbetker/wav2vec2-large-robust-ft-libritts-voxpopuliwith the Tortoise tokenizer/cleanersdnsmos: no-reference quality proxy via TorchMetrics DNSMOS overallnisqa: no-reference MOS proxy via TorchMetrics NISQA MOSspeaker_sim: per-utterance speaker similarity via batched SpeechBrain ECAPA embeddingsutmos: per-utterance MOS prediction via the officialUTMOSv2packageaudiobox: per-utterance aesthetic scores via Audiobox Aesthetics (CE,PQ)
Model runners:
chatterbox_turbo: generates eval-ready WAV/TXT pairs intodata/inputs/chatterbox_turbousingResembleAI/chatterbox-turbo
Generated inputs are organized by model:
inputs/
model_a/
speaker01_00001.wav
speaker01_00001.txt
speaker01_00002.wav
speaker01_00002.txt
model_b/
speaker01_00001.wav
speaker01_00001.txt
Reference files are pooled in one directory:
refs/
speaker01_00001.wav
speaker01_00001.txt
speaker01_00002.wav
speaker01_00002.txt
Model runners consume references by parsed speaker id. Every reference WAV for a speaker is used in sorted order with deterministic round-robin selection, and every model runner must consume utterance texts from one global stream without resetting between speakers.
The shared generation target is currently fixed at 128 utterances per speaker.
Utterance text construction is intentionally left as a placeholder in src/tts_eval/utterance_dataset_config.py. Fill DEFAULT_UTTERANCE_TEXT_DATASET_SPECS with Hugging Face streaming dataset specs before running a model runner.
Run Chatterbox Turbo:
model-runners/chatterbox_turbo/d.shThis runner:
- reads refs from
data/refsby default - requires GPU Docker access and CUDA execution
- validates every reference WAV before model load and requires each clip to be longer than
5seconds - writes generated WAV/TXT pairs to
data/inputs/chatterbox_turbo - uses the same Docker launcher pattern as the eval runners
Optional runtime tuning:
CHATTERBOX_TURBO_DEVICE=cuda model-runners/chatterbox_turbo/d.sh
CHATTERBOX_TURBO_TEMPERATURE=0.7 CHATTERBOX_TURBO_TOP_P=0.9 model-runners/chatterbox_turbo/d.shYou can still override refs and pass a fixed UTC timestamp:
model-runners/chatterbox_turbo/d.sh /abs/path/to/refs 2026-03-11T18:20:00ZEach runner builds or reuses its own Docker image, mounts the repo root at /app, runs the model code as your current host UID/GID instead of root, and mounts your host cache root into the container so model downloads are reused across runs.
Run CTC:
eval/runners/ctc/d.shRun Tortoise-backed CTC:
eval/runners/ctc_tortoise/d.shRun DNSMOS:
eval/runners/dnsmos/d.shRun NISQA:
eval/runners/nisqa/d.shRun speaker similarity:
eval/runners/speaker_sim/d.shRun UTMOS:
eval/runners/utmos/d.shRun Audiobox aesthetics:
eval/runners/audiobox/d.shTune UTMOS batch size (default 32) via env var:
UTMOS_BATCH_SIZE=32 eval/runners/utmos/d.shTune UTMOS data loading workers (default 2) via env var:
UTMOS_NUM_WORKERS=8 eval/runners/utmos/d.shLimit CPU thread fanout in numeric libs used by UTMOS preprocessing (default 1):
UTMOS_CPU_THREADS=1 eval/runners/utmos/d.shIf you see Unexpected bus error encountered in worker (shared memory exhaustion), increase Docker shm for UTMOS (default is 8g):
UTMOS_SHM_SIZE=16g eval/runners/utmos/d.shTo re-enable silence trimming (default is disabled), set:
UTMOS_REMOVE_SILENT_SECTION=true eval/runners/utmos/d.shOptional speed knobs for UTMOS internals (may change score characteristics):
UTMOS_SPEC_MIXUP_INNER=false UTMOS_SPEC_NUM_FRAMES=1 eval/runners/utmos/d.shTune DNSMOS batch size (default 8) via env var:
DNSMOS_BATCH_SIZE=16 eval/runners/dnsmos/d.shTune NISQA batch size (default 8) via env var:
NISQA_BATCH_SIZE=16 eval/runners/nisqa/d.shTune speaker-sim batch size (default 8) via env var:
SPEAKER_SIM_BATCH_SIZE=16 eval/runners/speaker_sim/d.shTune Audiobox batch size (default 16) via env var:
AUDIOBOX_BATCH_SIZE=32 eval/runners/audiobox/d.shOptional DNSMOS runtime tuning:
DNSMOS_DEVICE=auto DNSMOS_NUM_THREADS=8 eval/runners/dnsmos/d.shOptional speaker-sim runtime tuning:
SPEAKER_SIM_DEVICE=auto eval/runners/speaker_sim/d.shOptional NISQA runtime tuning:
NISQA_DEVICE=auto eval/runners/nisqa/d.shOptional Audiobox runtime tuning:
AUDIOBOX_DEVICE=cuda:1 eval/runners/audiobox/d.shOptional Tortoise CTC runtime tuning:
TORTOISE_CTC_DEVICE=cuda eval/runners/ctc_tortoise/d.shBy default, all eval launchers use:
data/inputsdata/refs
You can still override them:
eval/runners/ctc/d.sh /abs/path/to/inputs /abs/path/to/refsTo force a rebuild of a runner image:
BUILD_IMAGE=1 eval/runners/ctc/d.shYou can optionally pass a fixed UTC timestamp as the third argument:
eval/runners/ctc/d.sh /abs/path/to/inputs /abs/path/to/refs 2026-03-06T14:32:10ZRun all implemented runners:
scripts/run_all.shThis currently runs:
ctcctc_tortoisednsmosnisqaspeaker_simutmosaudiobox
Runner outputs are written under the repo-level data/evals/ tree:
data/evals/ctc/<model>/...data/evals/ctc_tortoise/<model>/...data/evals/dnsmos/<model>/...data/evals/nisqa/<model>/...data/evals/speaker_sim/<model>/...data/evals/utmos/<model>/...data/evals/audiobox/<model>/...
Each runner writes timestamped JSON artifacts per model:
summary_<timestamp>.jsonmetadata_<timestamp>.json
The ctc runner also writes:
per_utt_<timestamp>.jsonl
The ctc_tortoise runner also writes:
per_utt_<timestamp>.jsonl
The dnsmos runner also writes:
per_utt_<timestamp>.jsonl
The nisqa runner also writes:
per_utt_<timestamp>.jsonl
The speaker_sim runner also writes:
per_utt_<timestamp>.jsonl
The utmos runner also writes:
per_utt_<timestamp>.jsonl
The audiobox runner also writes:
per_utt_<timestamp>.jsonl
The coalescer searches recursively, so you can point it at the repo root and it will find the current output tree.
python3 scripts/coalesce_jsons.py --eval-root . --output data/evals/coalesced_summary.jsonThe coalesced file contains one object per model and currently includes:
ctc_closeness_meanctc_tortoise_closeness_meandnsmos_ovrl_meannisqa_mos_meanspeaker_sim_ecapa_meanutmos_meanaudiobox_ce_meanaudiobox_pq_mean
Generate a styled combined figure of the latest mean evals with one subplot per metric:
scripts/plot_eval_means/d.sh --eval-root . --output data/evals/mean_eval_plot.pngAdd stddev error bars where the metric exposes metric_std:
scripts/plot_eval_means/d.sh --eval-root . --output data/evals/mean_eval_plot.png --include-stddevThe plotting command writes:
- a combined PNG at the requested output path
- a matching combined SVG alongside it
- one standalone PNG per metric under
data/evals/mean_eval_plot_metrics/ - one standalone SVG per metric under
data/evals/mean_eval_plot_metrics/
The plots use:
metric_meanfor utterance-level metrics such asctc,ctc_tortoise, anddnsmosmetric_meanfor utterance-level metrics such asnisqametric_meanfor utterance-level metrics such asspeaker_simmetric_meanfor utterance-level metrics such asutmosce_meanandpq_meanforaudiobox- mean values only, never median values
- a separate y-scale per metric subplot so mixed metric ranges are not forced onto one axis
- the plotting command runs fully inside Docker; no host venv or system Python packages are required
--group-by-modelis retained as a deprecated no-op for backward compatibility
ctcrequires Docker with GPU access because the runner uses--gpus all.ctc_tortoiseuses the Tortoise-author ASR checkpointjbetker/wav2vec2-large-robust-ft-libritts-voxpopuli, applies the Tortoiseenglish_cleaners-style transcript normalization, and records the sameexp(-ctc_loss/target_len)closeness transform as the stockctcrunner.dnsmosuses the TorchMetrics functional DNSMOS API and records only the overall MOS-like output.nisqauses the TorchMetrics functional NISQA API and records only the MOS output.speaker_simusesspeechbrain/spkrec-ecapa-voxceleb, averages all reference embeddings per speaker, then scores each generated utterance with cosine similarity against that speaker centroid.speaker_simruns batched ECAPA inference (default8) when waveform lengths match and shows per-model tqdm progress bars.utmosuses the officialUTMOSv2package pinned to commite53a6762948b908105d48d6cfd453f1b58156ed0, runs batchedinput_dirinference with the pretrainedfusion_stage3model, and requires Docker GPU access.audioboxusesaudiobox_aesthetics, recordsCEandPQper utterance, and requires Docker GPU access.dnsmosevaluates utterances in batches (default8per forward pass) when sample rate and waveform length match; it falls back to per-utterance scoring if a batch call fails.nisqaevaluates utterances in batches (default8) when waveform lengths match and marks the whole batch as failed on backend exceptions.audioboxevaluates utterances in batches (default16) and recursively splits failed batches to isolate per-utterance failures.dnsmospersists TorchMetrics model downloads by mounting the host cache path${XDG_CACHE_HOME:-$HOME/.cache}/torchmetricsinto/home/app/.torchmetrics.utmospersists model downloads by mounting the host cache path${XDG_CACHE_HOME:-$HOME/.cache}into/home/app/.cacheand settingUTMOSV2_CHACHE=/home/app/.cache/utmosv2inside the runner container.audioboxpersists model downloads by mounting the host cache path${XDG_CACHE_HOME:-$HOME/.cache}into/home/app/.cacheand using HF/Torch cache env vars inside the runner container.- Invalid or unreadable WAVs are skipped during file discovery.