The plan, the mechanics, and what it costs — measured on this hardware, against the actual compute budgets.
A text-to-speech model can be asked to say a sentence angrily, quietly angry, very fast, with a lot of vocal fry. This project builds the training data for that, systematically: for each of 6,000 distinct voices it renders every cell of a 832-condition grid, in English and German, and stores every sample with full measurements attached.
The end goal is a speech model that can be steered — and, further out, a speech-to-speech translation model that keeps the speaker and the emotional level across languages. That second goal is why every text exists as an EN/DE pair of the same sentence.
Everything below is counted in "condition groups" and "samples", which means nothing until you have heard one. Three complete voice profiles are published — the same reference voice rendered through all 832 conditions, with the winning take of each group playable and its scores shown:
For an outsider: the deliverable is not a model. It is a dataset — roughly a quarter of a billion short speech clips, each one labelled with what it was asked to be and measured for what it actually turned out to be. Somebody else then trains on it.
Each row of that dataset is: a voice (one of 6,000 reference speakers), a sentence (English or German, paired with its counterpart in the other language), a condition (one of 832 — "intense, freely expressed anger", "very high vocal fry", "a fearful gasp mid-sentence"), the audio, and about a hundred numbers describing the result. Nothing is thrown away: the takes that scored badly are stored too, because a later change of mind about what "good" means should never require regenerating anything.
The corpus is deliberately split. The two halves teach opposite things, and a model needs both.
For each of the 832 conditions, 32 samples are generated from 32 different texts, each written so its content justifies that exact condition. This is the breadth half: it stops the model from associating "anger" with one particular sentence, and gives a pre-training corpus real lexical variety.
Why this matters more than it sounds. A controlled experiment on this pipeline showed that a dozen emotions produced almost no measurable emotion at all — Bitterness sat at −0.005 — not because the conditioning was broken but because the sentences, drawn from a translation corpus, gave the speaker nothing to feel. Rewriting the text so its content carries the emotion raised emotion strength +22 %, genuineness +27 % and vocal-burst blend +39 %, with audio quality flat and word error rate down 12 %. Text is the single most effective lever measured on this system, so the corpus is built around it.
For each of the 125 parent groups (40 emotions, 57 VoiceNet dimensions, edge cases, character clusters, sports, explicitness), 16 texts are each rendered through all four sub-conditions. For Anger that is intense/free-flowing, intense/contained, moderate/free-flowing and moderate/contained — 16 × 4 = 64 samples per language, the same words every time.
Every text is written by a Gemma model before any speech is generated, and every one is seeded rather than invented from nothing — a model asked to write freely collapses each emotion onto its most convenient topic (every bitterness line about a betrayal), destroying the domain balance the corpus is supposed to have.
| hard domain (1 % each) | what breaks | seed keywords |
|---|---|---|
| Mathematics & formulae | equations, Greek letters, operators, fractions, exponents, sub/superscripts | α, ∑, x², 3/4, ∫, ≠, ±, matrix, derivative, lemma |
| Numbers, units & measures | large numbers, decimals, ordinals, ranges, currencies, units | 1,250,000, 3.14, 21st, 1990-1995, €4.99, 37 °C, 5 km/h |
| Dates, times & durations | formats that expand differently depending on locale and context | 3/4/25, 1900-1945, 14:05, Q3, 2h30, fortnight |
| Abbreviations & acronyms | letter-by-letter vs word-like, and the ones read as their expansion | NASA, e.g., i.e., Dr., GmbH, z.B., ISO 9001, PhD |
| Homographs & heteronyms | same spelling, different pronunciation, disambiguated only by sense | read/read, lead/lead, live/live, bow, tear, German 'ubersetzen', 'Modern' |
| Proper nouns & loanwords | personal, place and brand names, including foreign-origin spellings | Xiaomi, Worcestershire, Nguyen, Reykjavik, Bourgogne, Oaxaca |
| Code-switching EN↔DE | a mid-sentence language switch -- directly load-bearing for this project's goal | 'Ich habe das feature schon deployed', 'the Umlaut is tricky' |
| URLs, emails, paths & code | strings that are not words and must not be read as words | https://a.io/x?q=1, snake_case, C:\\Users, <div>, npm i -g |
| Slang, profanity & net register | registers a filtered web corpus under-represents, including censored forms | swear words, f***, lol, innit, Digga, emoji-adjacent text |
| Technical, medical & legal jargon | long low-frequency terms with irregular stress | acetylsalicylic acid, §823 BGB, pneumonoultramicroscopic, myocardial infarction |
This is not cosmetic — it is worth real money, for a reason specific to how batched generation works. See the batching section.
Speech is generated autoregressively in batches of 64. A batch runs until every sequence in it has finished, so one long take holds up the other 63. Today all 64 samples in a batch are the same sentence, so they finish together. Give each sample a different text and the batch waits for the longest one.
Measured on 25,850 real takes (mean 7.86 s against a 32 s generation cap, so takes genuinely do stop early — if they all ran to the cap this would cost nothing):
| batch composition | expected longest take | relative cost |
|---|---|---|
| 64 × the same text (today) | 12.46 s | 1.00× |
| 64 × different texts, unsorted | 23.20 s | 1.86× |
| 64 × different texts, sorted by word count | 16.77 s | 1.35× |
| + texts written to a target length (estimate) | — | ~1.15× |
Different token counts per sample cost almost nothing on their own — that only affects prefill, one forward pass, against ~96 sequential decode steps. The entire penalty is the spread in durations. Which is why the text generator is asked for a target length: it turns a 1.86× tax into something near 1.15×.
A gasp, a sigh, a shaky breath, a laugh. There are 64 trained burst adapters, and the corpus should contain them inside sentences rather than as isolated sound effects.
The measured recipe is specific, and it constrains the design:
| burst adapter dose | burst occurs | rest of the line survives | blend | WER |
|---|---|---|---|---|
| none (inline tag in the script only) | 21.9 % | 1.00 | 6.63 | 0.05 |
| λ = 0.50 ← the operating point | 50.3 % | 0.90 | 4.93 | 0.15 |
| λ = 1.00 | 72.1 % | 0.45 | 4.07 | 0.57 |
At full merge more than half the words after the burst are never spoken. λ = 0.5 is the measured optimum for "burst and sentence and blend".
Design rules that follow:
The obvious way to assign bursts — let each emotion draw the classes that fit it —
produces a wildly skewed corpus. Sighs and gasps fit almost every emotion and would appear
constantly; wolf_whistle, hiccup or gurgling fit almost none
and would nearly vanish. A class with a few thousand examples is not learnable next to one with
several million.
So the allocation is inverted: the target count per class is fixed first, and emotion-fit only decides where each class's quota gets spent.
swallows against 1,396 for the largest, median 178), and 19 of
the 64 were scored without a blind metric. Equal representation in the corpus is therefore
not a claim of equal quality — per-class burst-detection rates should be measured on
the output and published alongside, so a downstream user can weight them.| model | subset A texts | subset B pool | GPU-h | core-h | share of total job |
|---|---|---|---|---|---|
| Gemma 4 E4B | 79.9 M pairs | 200k pairs | 3,767 | 271,222 | 1.7 % |
| Gemma 12B bf16 | 79.9 M pairs | 200k pairs | 11,301 | 813,665 | 5.0 % |
| batching strategy | subset A GPU-h | subset B GPU-h | total GPU-h | core-h |
|---|---|---|---|---|
| naive (no bucketing) | 198,083 | 99,041 | 297,124 | 21,392,916 |
| word-count bucketing | 143,770 | 71,885 | 215,654 | 15,527,117 |
| length-controlled + bucketing | 122,470 | 61,235 | 183,706 | 13,226,803 |
Speech generation only, all 6,000 voices, both subsets. Cost is proportional to sample count, so it follows the block sizes directly — the VoiceNet block is the expensive one simply because it is the biggest.
| block | what it is | groups | samples (6,000 voices) | GPU-h | share |
|---|---|---|---|---|---|
| Emotions | 40 emotions x {intense, moderate} x {free, contained} x {EN, DE} | 320 | 92.2 M | 82,944 | 38.5 % |
| VoiceNet dimensions | 57 dimensions x 4 levels x {EN, DE} | 456 | 131.3 M | 118,195 | 54.8 % |
| Edge cases | screams, groans, shivers, laughter, whimpering, crying | 28 | 8.1 M | 7,258 | 3.4 % |
| Character clusters | 12 character LoRAs x {EN, DE} | 24 | 6.9 M | 6,221 | 2.9 % |
| Sports commentator | x {EN, DE} | 2 | 0.6 M | 518 | 0.2 % |
| Explicitness | x {EN, DE}, behind an age gate | 2 | 0.6 M | 518 | 0.2 % |
| total | 240 M | 215,654 | 100 % | ||
The same thing per number of reference voices. These are GPU-hours; JUPITER bills whole nodes, so multiply by 72 for core-hours (288 core-h per node-hour, 4 GPUs per node).
| voices | samples | audio | text GPU-h | speech GPU-h | total GPU-h | core-h | wall-clock @64 nodes |
|---|---|---|---|---|---|---|---|
| 100 | 4.0 M | 9k h | 188 | 3,594 | 3,783 | 272,346 | 0.6 days |
| 500 | 20.0 M | 44k h | 942 | 17,971 | 18,913 | 1,361,732 | 2.9 days |
| 1,000 | 39.9 M | 87k h | 1,883 | 35,942 | 37,826 | 2,723,464 | 5.9 days |
| 6,000 | 239.6 M | 523k h | 11,301 | 215,654 | 226,955 | 16,340,781 | 35.1 days |
| scenario (speech + 12B text) | core-h | % of REFORMO left | % of LAIONIZE left | % of both |
|---|---|---|---|---|
| naive (no bucketing) | 22,206,581 | 32.3 % | 59.1 % | 20.9 % |
| word-count bucketing | 16,340,781 | 23.8 % | 43.5 % | 15.4 % |
| length-controlled + bucketing | 14,040,468 | 20.4 % | 37.4 % | 13.2 % |
Planning on the middle row — word-count bucketing, which is measured rather than estimated — the whole corpus costs 16,340,781 core-h, about 15 % of the two budgets combined. REFORMO expires 2026-12-31 and LAIONIZE 2027-04-30, so the split between them is a scheduling question, not a feasibility one.
| nodes dedicated | wall-clock |
|---|---|
| 32 nodes (×4 GPUs) | 70 days |
| 64 nodes (×4 GPUs) | 35 days |
| 128 nodes (×4 GPUs) | 18 days |
| 256 nodes (×4 GPUs) | 9 days |
Nothing forces all 6,000 voices at once. The matrix is per-voice, so the corpus can be built in voice batches and stopped at any point with a complete, usable result. Every column below is the full 832-condition matrix and both subsets, priced with word-count bucketing and the 12B text model.
| reference voices | samples | audio | text GPU-h | speech GPU-h | total core-h | % of both budgets | storage | wall-clock @64 nodes |
|---|---|---|---|---|---|---|---|---|
| 100 | 4.0 M | 9k h | 188 | 3,594 | 272,346 | 0.26 % | 0.6 TB | 0.6 days |
| 500 | 20.0 M | 44k h | 942 | 17,971 | 1,361,732 | 1.28 % | 3.1 TB | 2.9 days |
| 1,000 | 39.9 M | 87k h | 1,883 | 35,942 | 2,723,464 | 2.56 % | 6.3 TB | 5.9 days |
| 6,000 | 239.6 M | 523k h | 11,301 | 215,654 | 16,340,781 | 15.38 % | 37.7 TB | 35.1 days |
At 160 kbps and 48 kHz — the quality the corpus is now written at — 240 M samples of 7.86 s come to about 38 TB of audio, plus metadata. Dropping to 128 kbps saves roughly a fifth. It is stored as WebDataset tar shards plus parquet, never as loose files: at this scale loose files would mean a quarter of a billion objects on a shared filesystem.
Throughput measured on GH200: 1,500 generations/GPU-h at batch 64 (6,016 candidates in 4.01 GPU-h); node billing 72 core-h per GPU-hour. Budgets as of 2026-08-10.