loudness.ai
Research & Benchmarks

Teaching Piper TTS to Read: Text Normalization and Word Timings

Abstract

Piper is a fast neural text-to-speech engine that runs on ordinary processors, but it reads text exactly as written. Prices, dates, phone numbers, abbreviations and units are often read literally: “$5.50” becomes “dollar five point five zero”, and “Dr. Smith” ends a sentence after “Doctor”. We built a text preparation layer for Piper that rewrites these forms while remembering, for every spoken word, the written word it came from. It also sets pauses between sentences, lines and paragraphs, repairs British pronunciations in some American English voices, and gives exact word timings for subtitles from Piper’s own phoneme durations. On 84 English and Spanish test sentences we wrote, the phoneme error rate fell from 32.6% to 0.9%, and none of 19 control sentences changed. On the held-out half of NVIDIA NeMo’s public test cases, which we did not write, the phoneme error rate in English was 41.0% for unprocessed text, 28.5% for a typical number-to-words script and 15.8% with our layer; in Spanish it was 64.7%, 56.2% and 43.2%. The code, test sentences and results are available under the Apache License 2.0.

What Piper reads when text is not prepared

Piper turns text into phonemes with espeak-ng and then synthesizes each sentence with a small VITS model. It does no text normalization of its own. espeak-ng reads plain words, ordinary numbers, ordinals such as “21st”, times such as “3:30 pm” and e-mail addresses well, but many everyday written forms are read symbol by symbol, and some abbreviations end a sentence halfway through a name.

We passed everyday sentences through the same espeak-ng build that Piper 1.8 uses and wrote down what a listener hears. The table compares that with what our layer gives the voice instead. The last column is produced by the current code each time this page is built.

Everyday sentences without and with text preparation. “Heard” is what espeak-ng read, written out as words; the last column is the text the voice receives, in which digits are read as numbers.
WrittenVoiceHeard without preparationGiven to the voice with preparation
Dr. Smith lives on Elm St.English (US)“Doctor.” (the sentence ends) “Smith lives on Elm Saint.”Doctor Smith lives on Elm Street.
It costs $5.50.English (US)“It costs dollar five point five zero.”It costs 5 dollars and 50 cents.
Due on 4/5/2026.English (US)“Due on four slash five slash two thousand twenty-six.”Due on April 5th, 20 26.
In the 1990s.English (US)“In the nineteen hundred ninety Z.”In the 19 nineties.
At 60 km/h, open 24/7.English (US)“At sixty K M slash H, open twenty-four slash seven.”At 60 kilometers per hour, open 24 7.
Read chapter IV about Henry VIII.English (US)“Read chapter Roman four about Henry Roman eight.”Read chapter 4 about Henry the 8th.
Great job 👍English (US)“Great job thumbs up”Great job.
Wait... what?English (US)“Wait what?” with no pauseWait, what?
Call +1 555-123-4567.English (US)“Call plus one five hundred fifty-five dash one hundred twenty-three dash four thousand five hundred sixty-seven.”Call plus 1, 5 5 5, 1 2 3, 4 5 6 7.
El Sr. García llegó.Spanish (Spain)“El señor.” (the sentence ends) “García llegó.”El señor García llegó.
Llegamos el 15/10/2026.Spanish (Spain)“Llegamos el quince barra diez barra dos mil veintiséis.”Llegamos el 15 de octubre de 2026.
Vive en la Av. de Mayo.Spanish (Spain)“Vive en la ab de Mayo.”Vive en la avenida de Mayo.
Cuesta 5,50 €.Spanish (Spain)“Cuesta cinco coma cincuenta euro.”Cuesta 5 euros con 50 céntimos.
A las 10 a. m.Spanish (Spain)“A las diez a eme.”A las 10 de la mañana.
Llama al 912 345 678.Spanish (Spain)“Llama al novecientos doce trescientos cuarenta y cinco seiscientos setenta y ocho.”Llama al 9 1 2, 3 4 5, 6 7 8.

Listen: before and after

These recordings come from the Loudness.ai Text to Speech engine: the same voice and the same text, before the text preparation layer went live and after it, on 15 October 2026. Each take is generated fresh, so small differences in delivery are normal; listen for what the text turns into.

Daniel · US English

Typed text

Dr. Smith's new schedule starts on 4/5/2026 at 9:05 a.m. The ticket costs $5.50, or $12.99 with the fast lane. Wait... is that right?

  • “Dr.” no longer stops the sentence
  • The date is “April 5th, 2026” and the time “nine oh five”
  • “$5.50” is “five dollars and fifty cents”
  • A real pause after “Wait…”
  • American “schedule” and “new”

Before: text as written

After: with text preparation

Olivia · US English

Typed text

In the 1990s, the train ran at 60 km/h. Henry VIII appears in chapter IV. Call +1 555-123-4567 or email hello@loudness.ai.

  • “Nineteen nineties”, not “nineteen hundred and ninety z”
  • “Kilometers per hour”
  • “Henry the Eighth” and “chapter four”, without “Roman”
  • The phone number in digit groups

Before: text as written

After: with text preparation

Jenny · Irish English

Typed text

Shopping list

- Tomatoes
- 1/2 cup of yogurt
- Fresh herbs

That's everything for today 👍

  • Each list line ends like a sentence
  • “One half cup” instead of “one slash two”
  • A longer pause before the last paragraph
  • No “thumbs up” read from the emoji

Before: text as written

After: with text preparation

Hannah · US English

Typed text

this transcript was pasted without any punctuation so the voice used to read every word in one long breath which made it hard to follow and tiring to listen to because there was never a moment to pause and the ideas all ran together

  • Short comma pauses inside a long text that has no punctuation

Before: text as written

After: with text preparation

Kristin · US English

Typed text

Mr. and Mrs. Jones met J. K. Rowling at St. Mary's on Elm St. They paid 1,250,000 dollars. Really?!

  • “Mr.”, “Mrs.” and “J. K. Rowling” no longer stop the sentence
  • “Elm St.” is read “Elm Street” and still ends the sentence

Before: text as written

After: with text preparation

Jordan · US English

Typed text

Either way, the garage privacy policy is necessary for the new library laboratory. Students can check the schedule every Tuesday.

  • American “either”, “garage”, “privacy”, “necessary”, “library” and “laboratory”
  • American “new”, “students”, “schedule” and “Tuesday”

Before: text as written

After: with text preparation

Rules that remember the original text

Each line of text passes through an ordered list of rules. A rule is a regular expression and a function that says what to read instead, or declines. Rules always match the original text, never each other’s output, and the first rule to claim a span of characters wins. The line is then rebuilt from untouched text and replacements, and every character of the result carries the span of the original it came from.

This has two practical effects. Rules cannot corrupt each other: a web address claimed by the address rule is never touched by the abbreviation or slash rules. And every spoken word knows its source: “dollars” in “5 dollars and 50 cents” points back to “$5.50”, so subtitles can show what the person wrote while the timing follows what the voice said. A rule that keeps its first word, such as “Felipe VI” read as “Felipe sexto”, claims only the rest, so each written word keeps its own spoken words. Double square brackets typed into the text are neutralized, so nobody can inject raw phonemes.

Loudness.ai
One sentence through the layer: each written word, what the voice reads for it and when it is spoken, from one synthesized take.

What the rules cover

English and Spanish have full rule sets; the other 25 languages of our deployment get the layout and pause rules.

Titles and street words such as “Dr.”, “Mrs.”, “St.”, “Sr.” and “Av.” are expanded without a false sentence end, while a real sentence end is kept when the abbreviation closes a sentence, as in “Apple Inc. We…”. Money is read with singular and plural forms and regional currencies: “$” is read as pesos for Mexican Spanish. Dates are read in numeric, ISO, dotted and written forms, in each region’s order, with American year pairs (“twenty twenty-six”) for US English. Clock times include seconds, 24-hour times and Spanish “a. m.” and “p. m.”.

Decades, units, ranges and fractions cover forms such as “the 1990s”, “60 km/h”, “pages 10–20” and “1/2 cup”. Phone numbers are read in digit groups, including Spain’s nine-digit format. Roman numerals are read after keywords and regnal names, as in “chapter IV” and “Henry VIII”. Initials, thousands grouped with spaces and slashes between words (“and/or”) are handled too.

Layout, in every language: list markers, markdown and emoji are removed, web addresses lose “https://” and “www”, and a line without final punctuation gets a full stop so that it ends with a falling tone.

Sentences, lines, paragraphs and pauses

The prepared text is split into segments, and each segment is followed by a pause of a known kind: sentence, list line, paragraph, ellipsis or clause. A sentence ends only at a full stop, question mark or exclamation mark that is not followed by a lowercase letter, and only after abbreviations have been expanded. With p as the pause the user chooses, the gap is p after a sentence, line or ellipsis, 0.4 p after a clause, and the larger of 2 p and p + 0.3 seconds after a paragraph.

Long runs without punctuation are hard to follow. A run of more than 28 words is split before a conjunction near its middle where one exists (seven languages have conjunction lists), otherwise at a word gap, and the first part ends with a comma so that the voice keeps a continuing tone.

Each segment is synthesized on its own and joined with exact silences. The voices often stop on a sample that is not zero, 28 to 57 dB below the peak in our measurements, which can click against digital silence. A 3 ms fade-in and a 12 ms fade-out at every join remove the click.

American voices trained on British phonemes

Each Piper voice is trained on the phonemes of one espeak-ng voice, recorded in the model’s configuration. Several community English (US) voices, trained on American audiobook speakers, record espeak’s “en” voice, which is British English. For most words this does no harm: the model learned to turn British symbols into the American sounds of its speakers.

It matters where the British form is a different pronunciation rather than a different accent. espeak “en” gives “shed-yool” for schedule, “tom-ah-toe” for tomato, privacy with a short i, and “zed” for the letter Z, and the voices say exactly that.

Switching these voices to American phonemes does not work, because their training data never contained symbols such as the r-coloured vowel or the flapped t. Instead we wrote American forms of 118 frequent words, including words ending in -ary and -ory, words such as “new” and “student”, and the letter Z, using only symbols from the British set. They are passed to Piper as raw phoneme blocks, and only to US voices whose model records “en”. A unit test checks that no block uses a symbol outside that set.

Two Piper details matter here. Punctuation directly after a raw phoneme block is dropped and the next word is joined to the block, so punctuation must go inside the block. And a raw block followed by text leaves a doubled word gap, which we collapse before synthesis.

Word timings from the model’s own durations

Subtitles and word highlighting need to know when each word is spoken. The usual way is to align the audio after synthesis, with a forced aligner or a speech recognizer. Piper’s VITS models already predict how long every phoneme lasts, and Piper 1.8 can expose those durations as an extra model output. We use them directly.

For each segment we get the start and end of every phoneme symbol, including punctuation symbols, whose durations carry the pauses. Each prepared word is phonemized on its own to estimate how many symbols it has, and a small dynamic programme chooses where each word’s symbols begin: close to its expected share, preferring gaps between spoken words, and giving every word at least one symbol. This copes with espeak-ng merging short words (“for the” became one spoken word in our tests) and expanding one written word into several (“2026”).

Spoken words are then mapped back to written words through the spans described above. When several spoken words cover several written words, as in “5 euros con 50 céntimos” for “5,50 €”, they share the time in proportion to the written words’ lengths. Subtitle cues follow the segments: one cue per sentence or line, longer ones split at word gaps to fit two lines of about 42 characters, and a cue that stops mid-thought is joined to the next when both fit.

A memory note for developers. Exposing the durations means editing the model graph. Piper’s loader can do this in memory, but in our deployment that used 811 MB for four voices, against 480 MB when the same voices were loaded from files. Writing each edited model to disk once and loading that file brought memory use back to 486 MB.

Results on our own test suite

We measure the phoneme error rate (PER). Each output and a reference reading are phonemized with the same espeak-ng voice; after removing stress marks, punctuation and word gaps, PER is the edit distance between the two symbol strings divided by the reference length. A case is exact when PER is zero. Because both sides are read by espeak-ng, the metric isolates text preparation from espeak-ng’s reading of plain words. We also count false sentence endings.

Our suite has 103 sentences: 84 test cases in 13 categories with hand-written references, and 19 control sentences of plain text whose reference is the text itself, so any change to a control counts as harm. Two known failures, a heteronym and an ambiguous unit, are included on purpose.

The layer removed almost all differences from the references in the categories it targets, removed all 18 false sentence endings and changed none of the 19 controls. The four remaining differences are the two deliberate failures, a fraction whose Spanish gender the rule cannot know (“media taza”) and a spelling variant in one reference. Because we wrote both the rules and this suite, these numbers show coverage of the intended cases, not generalization.

Our suite: mean phoneme error rate (%), exactly correct cases and false sentence endings, without and with text preparation.
GroupCasesPER withoutPER withExact withoutExact withFalse ends withoutFalse ends with
All test cases8432.60.91080180
English5833.60.9655130
Spanish2630.40.842550
Controls (unchanged text)190.00.0191900
brand name120.80.00100
dates641.80.00610
decades311.50.01300
initials20.00.02230
known limits223.423.40000
layout772.60.02730
money950.50.00900
phone numbers385.20.00300
ranges, fractions, slashes925.52.40800
Roman numerals526.70.00500
times627.70.00600
titles2115.30.3520110
units1031.80.001000

An independent test we did not write

To test generalization we used the English and Spanish written-to-spoken test cases published with NVIDIA NeMo text processing (commit ddadfb2, 957 usable cases). We split them into two halves by a hash of the written text. We studied individual failures in the development half only and fixed general gaps there; we report the held-out half, whose individual cases we did not inspect.

We compared three systems: the raw text as Piper receives it; a typical community script that we wrote to be capable and fair, combining num2words with currency, percentages, ordinals, years, decimals, numeric dates, clock times, thousands separators and common abbreviations; and our layer. NeMo’s own normalizer was not evaluated on its own test cases.

On the held-out half our layer had the lowest error in both languages. On the development half the figures were 41.9%, 29.4% and 19.5% in English and 64.8%, 55.4% and 36.9% in Spanish. After the evaluation, while checking the writing tips below, we found that Spanish phone numbers in Spain’s grouped format (“912 345 678”) and amounts such as “1 500 kg” were joined into one number. We corrected both; no reported number changed.

NeMo test cases, held-out half: mean phoneme error rate (%) and exactly correct cases. Lower error is better.
LanguageCasesRaw textnum2words scriptThis layerExact: rawExact: scriptExact: this layer
English20041.028.515.8658599
Spanish27764.756.243.2476164
Loudness.ai
Mean phoneme error rate on the held-out half of NeMo’s test cases, for raw text, the num2words script and this layer.

Where the gains came from

Per category, the largest gains were for money, telephone numbers, times, dates and fractions. Our layer was worse than raw text for web addresses, because it drops “https://” and “www” by design and NeMo’s references keep them. The script was better for Spanish cardinals and times: espeak-ng reads large Spanish numbers such as one billion (mil millones) as “uno mil millones”, which the script avoids by writing numbers out itself, and NeMo reads “01:00” as “una” where we say “una en punto”.

NeMo’s references follow one reading style, so a correct reading in another style, such as “two thousand twelve” for NeMo’s “twenty twelve”, counts as an error for every system. The differences between systems therefore matter more than the absolute numbers.

Mean phoneme error rate (%) per NeMo category on the held-out half.
LanguageCategoryCasesRaw textnum2words scriptThis layer
Englishaddress651.443.246.0
Englishcardinal1215.429.315.2
Englishdate2359.029.020.5
Englishdecimal64.436.74.4
Englishelectronic1910.210.916.8
Englishfraction657.962.425.5
Englishmath20.00.00.0
Englishmeasure1141.641.434.6
Englishmoney3877.826.513.3
Englishordinal1016.723.116.7
Englishrange1237.423.819.1
Englishroman160.060.00.0
Englishserial139.411.610.1
Englishtelephone779.983.212.0
Englishtime9103.080.119.0
Englishwhitelist24.20.020.0
Englishword233.65.41.5
Spanishcardinal3751.215.546.8
Spanishdate1039.336.939.3
Spanishdecimal1025.228.616.6
Spanishelectronic1727.327.347.3
Spanishfraction4066.664.347.6
Spanishmeasure1949.255.939.6
Spanishmoney3371.266.553.7
Spanishordinal6598.496.842.7
Spanishtelephone670.269.728.6
Spanishtime15120.867.177.9
Spanishwhitelist481.881.881.8
Spanishword210.00.00.0

Which rules matter

We switched off one rule group at a time on the held-out half. Money handling contributed most in English, and number grouping most in Spanish. A negative value means that switching the group off would lower the error on this set: web addresses again, for the reason above. Groups aimed at forms absent from NeMo’s cases, such as titles, layout and pauses, show no effect here and are measured by our own suite.

Increase in mean phoneme error rate (percentage points) when one rule group is switched off, held-out half.
Rule group switched offEnglishSpanish
money+12.6+2.9
number grouping0.0+14.0
times+3.8+0.6
fractions+1.0+3.0
dates+3.90.0
units+0.5+1.9
phone numbers+2.4−0.2
decades+1.20.0
Roman numerals+0.30.0
abbreviations+0.20.0
ranges+0.10.0
slashes−0.30.0
web addresses−0.5−1.2

Timing and voice settings

Across 414 synthesized takes of the English test sentences (two voices, three speed and pause settings), every written word received a time in 408 takes; in the other six the untimed word is an emoji, which is removed and not spoken. Word times were ordered without overlap in all 414 takes, and their total matched the audio length in all 414. Of 684 word cuts, 97.2% fell on a gap between spoken words; the rest lie inside words that espeak-ng merged or expanded, where no gap exists.

We did not compare the timings with human-annotated alignments. The durations are the ones the audio was generated from, and in our checks speech recognition timestamps drifted by up to a few hundred milliseconds within a sentence, which made them unsuitable as a reference.

Separately, we scaled each voice’s noise scale and noise width to 0.7 of the model’s own values to reduce audible trembling. With an automatic naturalness predictor (UTMOS) and a speech-recognition clarity check on three sentences per voice, the mean score of 13 English voices rose from 93.2 to 94.8, and predicted naturalness from 4.16 to 4.26 out of 5. That difference is smaller than the score’s per-voice reproducibility (3.5 points); one listener, the author, preferred the calmer setting. A listening test is in preparation.

Writing tips for better speech

Text preparation handles most everyday forms, but some written forms are ambiguous, and no rule can know what the writer meant. These tips help with any Piper-based reader, including the Loudness.ai Text to Speech tool. Each one was checked against the current code.

Write this, instead of that, and why. Readings were checked with the espeak-ng build used by Piper 1.8.
WriteInstead ofWhy
9:05 AM9.05A full stop between numbers is read as a decimal point: “nine point zero five”. With AM or PM after it, “9.05 AM” is also read as a time.
$5.505.50 dollarsWith the symbol, the price is read as “5 dollars and 50 cents”. Written the other way, it is “five point five zero dollars”.
April 5, 2026 or 2026-04-054/5/2026Numeric dates follow the voice’s region: US English reads 4/5/2026 as April 5, UK English as 4 May. A written month or an ISO date means the same everywhere.
+1 555-123-45675551234567Grouped digits are read in groups. An unbroken run is read as one large number: “five billion, five hundred…”.
3 m or 3 meters3mWithout a space, “3m” could mean meters or million, so it is read as letters: “three M”.
that isi.e.Uncommon abbreviations are read as letters and can end the sentence: “I E.” Write the words.
I finished reading it yesterday.I read it yesterday.Words such as read, live and lead have two pronunciations, and the voice may choose the wrong one. Rephrase when it does.
Wait... what?Wait what?Punctuation sets the pauses: a comma gives a short pause, a full stop a sentence pause, an ellipsis a longer one and a blank line a paragraph pause. Very long sentences are split automatically, but your own commas sound more natural.
Each item on its own lineItem one - item two - item threeEach line is read as its own phrase with a pause after it. List markers such as dashes and bullets are not read.
loudness.ai/toolshttps://www.loudness.ai/tools/?ref=mailWeb addresses are read without https:// and www, but every other character is read aloud. Keep them short.
Great job, thank you!Great job 👍Emoji are removed and not read. Use words when the meaning matters.

Advice for developers using Piper

1. Normalize before phonemizing, and keep a map from every spoken word back to the original text; subtitles and highlighting depend on it.

2. Expand titles and abbreviations without their full stop, or espeak-ng ends the sentence there.

3. Check which espeak-ng voice a model was trained with before trusting its accent.

4. Put punctuation inside raw phoneme blocks.

5. Fade every join between speech and silence.

6. Take word timings from the model’s own durations, and load the edited model from a file, not from memory.

7. Test plain control sentences too. A rule that changes ordinary text is a regression.

Everything in this list is built into loudness-tts, our free and open-source text-to-speech SDK for Python and JavaScript (GitHub, PyPI). The installation guide takes about five minutes.

Limitations

Our own suite was written by the author of the rules. NeMo’s cases are independent but follow one reading style, and espeak-ng reads every reference, so references it misreads add error to every system. Each case has a single reference. Full rules exist for English and Spanish only; other languages receive layout and pause rules. Heteronyms such as “read” and ambiguous forms such as “3m” remain. Large Spanish numbers are limited by espeak-ng.

The American word list is curated, and its benefit was checked at the phoneme level, not by listeners. Synthesis is stochastic, and the timing and voice-setting results come from single takes. Text preparation fixes what a voice says, not how it sounds: the trembling and warble of small VITS models remain a property of the voice model.

Reproduce the study

The study package contains the text preparation layer, ttsprep (Python 3.9 or newer, no dependencies), with its 30 unit tests, an optional adapter that produces word timings with an installed Piper, the evaluation scripts, our 103 test sentences with references, the NeMo test cases with their source, licence and checksums, all result files and the paper’s LaTeX source in English and Spanish. Code, test sentences and results are released under the Apache License 2.0.

To reproduce the tables, install piper-tts 1.8.0 and num2words 0.5.14, point ESPEAK_DATA_PATH at the espeak-ng data bundled with Piper if it is not found, and run the two evaluation scripts with an output folder. Their output matches the published result files exactly.

Piper and espeak-ng (GPL-3.0), voice models and NeMo’s code are not included. Text measurements used piper-tts 1.8.0 with its bundled espeak-ng 1.52.0.1, onnxruntime 1.19.2, NumPy 2.0.2 and Python 3.9.6 on an Apple M4 laptop; production checks used onnxruntime 1.31.0, NumPy 2.5.4 and Python 3.12.3 on a 12-vCPU AMD EPYC virtual server.

Competing interests and funding

The author develops Loudness.ai, whose Text to Speech tool uses the method described here. No external funding was received.

References

Hansen, M. and the Open Home Foundation. Piper: a fast, local neural text to speech system. eSpeak NG contributors. eSpeak NG.

Kim, J., Kong, J. and Son, J. (2021). Conditional variational autoencoder with adversarial learning for end-to-end text-to-speech. ICML 2021.

Sproat, R., Black, A. W., Chen, S., Kumar, S., Ostendorf, M. and Richards, C. (2001). Normalization of non-standard words. Computer Speech & Language, 15(3), 287–333. Ebden, P. and Sproat, R. (2015). The Kestrel TTS text normalization system. Natural Language Engineering, 21(3), 333–353.

Sproat, R. and Jaitly, N. (2016). RNN approaches to text normalization: a challenge. Zhang, H. et al. (2019). Neural models of text normalization for speech applications. Computational Linguistics, 45(2), 293–337.

Zhang, Y., Bakhturina, E., Gorman, K. and Ginsburg, B. (2021). NeMo inverse text normalization: from development to production. Interspeech 2021. NVIDIA. NeMo text processing, commit ddadfb2.

McAuliffe, M. et al. (2017). Montreal Forced Aligner: trainable text-speech alignment using Kaldi. Interspeech 2017. Radford, A. et al. (2023). Robust speech recognition via large-scale weak supervision. ICML 2023.

Koizumi, Y. et al. (2023). LibriTTS-R: a restored multi-speaker text-to-speech corpus. Interspeech 2023. Wells, J. C. (1982). Accents of English. Cambridge University Press. Saeki, T. et al. (2022). UTMOS: UTokyo-SaruLab system for VoiceMOS Challenge 2022. Interspeech 2022.

Paper and study files

Cite this report

Shehzad, Asad (2026). Teaching Piper TTS to Read: Text Normalization and Word Timings. Loudness.ai, LA-RB-2026-002, version 1.0.1. https://loudness.ai/research/piper-tts-text-normalization/ DOI: 10.5281/zenodo.23396091.

BibTeX · RIS · Research RSS feed

Sign in to Loudness.ai