# Voice Corpus Documentation ## Overview uncloseai-speech uses voice samples from **LibriSpeech test-clean** for Qwen3-TTS voice cloning. LibriSpeech is a public domain corpus of read English speech from LibriVox audiobook recordings. - **Source:** [OpenSLR LibriSpeech](https://www.openslr.org/12) - **HuggingFace:** [openslr/librispeech_asr](https://huggingface.co/datasets/openslr/librispeech_asr) - **License:** Public domain (LibriVox recordings) - **Audio:** 16kHz WAV, single speaker per file ## Voice Registry System Voice-to-speaker assignments are managed by `voice_registry.json` -- an append-only, idempotent registry that permanently locks each speaker to a voice name. ### How It Works 1. **Registry file** (`voice_registry.json`): Committed to git, contains all locked assignments 2. **Name pools**: 50 female + 50 male names, assigned in order as new speakers are added 3. **Deterministic assignment**: New speakers sorted by ID (ascending), names assigned in pool order 4. **Append-only**: Once a speaker is assigned a name, that assignment never changes 5. **Multi-corpus**: Registry tracks which corpus each speaker came from ### Adding a New Corpus To add speakers from a new corpus: 1. Add the corpus config to `voice_registry.json` under `corpora`: ```json "corpora": { "librispeech-test-clean": { ... }, "librispeech-dev-clean": { "dataset": "openslr/librispeech_asr", "config": "clean", "split": "validation", "description": "LibriSpeech dev-clean, 40 speakers" } } ``` 2. Run the download script with the new corpus: ```bash python scripts/download_diverse_voices.py --corpora librispeech-test-clean librispeech-dev-clean ``` 3. New speakers get the next available names from the pool. Existing assignments are untouched. 4. Commit the updated `voice_registry.json` to lock the new assignments. ## Test-Clean Corpus The `test-clean` split contains **40 speakers** (~8 minutes each, ~350MB total): ### Female Speakers (20) | ID | LibriVox Name | Minutes | |----|---------------|---------| | 121 | Nikolle Doolin | 8.01 | | 237 | rachelellen | 8.02 | | 367 | Kathleen Dang | 6.74 | | 1221 | Dianne | 8.07 | | 1284 | Daniel Anaya | 8.16 | | 1580 | TinyPines | 8.07 | | 1995 | (unknown) | ~8 | | 2094 | amycsj | 8.09 | | 2961 | Leni | 8.07 | | 3570 | sarac | 8.05 | | 3575 | supergirl | 8.06 | | 3729 | Heather Hogan | 8.03 | | 4446 | Jen Maxwell | 8.00 | | 4507 | Rachel Nelson-Smith | 8.05 | | 4970 | airandwaters | 8.15 | | 4992 | Joyce Martin | 8.21 | | 5142 | Mary Ballard-Johansson | 8.07 | | 5570 | Ulf Bjorklund | 8.28 | | 5683 | Rachael Lapidis | 8.01 | | 6829 | LadyBug | 8.24 | ### Male Speakers (20) | ID | LibriVox Name | Minutes | |----|---------------|---------| | 61 | Paul-Gabriel Wiener | 8.08 | | 260 | Brad Bush | 8.05 | | 672 | Taylor Burton-Edward | 8.27 | | 908 | Sam Stinson | 8.05 | | 1089 | Peter Bobbe | 8.05 | | 1188 | Duncan Murrell | 8.20 | | 1320 | number6 | 8.02 | | 2300 | Mitchell L Leopard | 8.19 | | 2830 | Tim Perkins | 8.04 | | 4077 | Nathan Markham | 8.14 | | 5105 | elongman | 8.12 | | 5639 | (unknown) | ~8 | | 6930 | Nolan Fout | 8.00 | | 7021 | (unknown) | ~8 | | 7127 | (unknown) | ~8 | | 7176 | (unknown) | ~8 | | 7729 | (unknown) | ~8 | | 8230 | (unknown) | ~8 | | 8455 | (unknown) | ~8 | | 8463 | (unknown) | ~8 | Note: Names marked (unknown) were not in the SPEAKERS.TXT mirror we fetched. ## Voice Names These are our own names -- LibriSpeech only provides LibriVox usernames (like "supergirl", "LadyBug", "number6"), not character-style voice names. Names are assigned from pools in `voice_registry.json` and locked permanently. The first 21 voices (original set) are: **Female voices:** aria, clara, elena, grace, hazel, iris, luna, maya, ruby, sage, sofia **Male voices:** atlas, caleb, felix, hugo, jasper, kai, leo, marcus, owen, theo When all 40 test-clean speakers are registered, the remaining 19 get the next names from the pools (amber, brooke, cora, ... for female; archer, blake, cole, ... for male). ## How Voices Are Selected The script picks the best sample per speaker based on: - Duration: prefers 5-10 seconds (ideal for voice cloning reference) - Completeness: bonus for sentences ending with a period - Length: penalty for very long text (>300 chars) ## Gender Verification Speaker genders are fetched at runtime from the official LibriSpeech `SPEAKERS.TXT` (via GitHub mirror). This ensures female names always map to female speakers and male names always map to male speakers. No hardcoded gender assumptions. Source: https://raw.githubusercontent.com/oscarknagg/voicemap/master/data/LibriSpeech/SPEAKERS.TXT ## Other LibriSpeech Splits Additional splits can be added as new corpora in the registry: - **train-clean-100**: ~250 speakers, 100 hours - **train-clean-360**: ~920 speakers, 360 hours - **train-other-500**: ~1160 speakers, 500 hours (noisier) - **test-other**: 33 speakers (noisier conditions) - **dev-clean**: 40 speakers (validation set) - **dev-other**: 33 speakers Using larger splits would give hundreds or thousands of distinct voices, but test-clean provides the highest quality recordings. ## File Layout ``` voice_registry.json # Idempotent voice-to-speaker assignments (committed) cloned-voices/ ├── aria.wav # Female voice sample (~7s, 16kHz) ├── atlas.wav # Male voice sample ├── ... # (40 voices when fully expanded) └── voices_metadata.json # Speaker IDs, genders, transcripts, durations ``` The `cloned-voices/` directory is mounted into the Docker container at `/app/cloned-voices/`. Voice config is in `voice_to_speaker.default.yaml`.