Speech and audio
Text-to-Speech as a Product and the Text Front End
Design TTS around text normalization, pronunciation, language, markup, prosody intent, accessibility, and product controls.
By the end you can
- Define text-to-speech as a product and the text front end as an operational problem with explicit inputs, outputs, and boundaries
- Distinguish text normalization, grapheme-to-phoneme, and speech synthesis without treating them as interchangeable
- Trace the workflow from define the speaking contract through synthesize and verify
- Evaluate text-to-speech as a product and the text front end using text normalization and pronunciation accuracy by entity type and evidence from difficult deployment slices
Key idea
The ambiguity was resolved without telling anyone
A text-to-speech pipeline breaks in four places, and it is worth noticing where along the chain each of them sits. Ambiguous numbers and abbreviations get resolved silently. Pronunciation lexicons encode only one community's version of a name, or only one variant of it. Markup permits unsafe changes to volume or speed, or hidden changes to content. And the output gets used without anyone verifying the critical text and the language it is in.
Two of those do their damage before the model has produced any sound. They are settled in text, by rules. Everything audible that follows is downstream of a decision already taken.
So a natural voice does not prove that the text was correct. Nor unambiguous, authorized, or appropriate. A system will synthesize a dangerous upstream error faithfully, and give it the same convincing prosody it gives everything else in the sentence.
Numbers and abbreviations settled by a silent guess in the front end reach the listener wrapped in convincing prosody, with nothing in the audio to mark where the guess was made.
Convincing prosody on a dangerous error
Follow one of those errors forward and you can see why the audio is incapable of reporting it. A TTS system converts text, or structured intent, into speech in three stages: a text front end, then an acoustic or generative model, then a waveform synthesizer. What the front end settles — normalization, pronunciation, language, punctuation, markup, context — is exactly what the model is then asked to say. Fluency is manufactured at the far end of that chain, at the waveform stage. It carries no information about the text that entered at the near end. Whatever the front end got wrong arrives at the listener intact, in the same assured voice as the rest.
That also means no single number can carry a release decision. How accurately the system normalizes and pronounces each type of entity is one question. Whether a listener comes away with the right content when a sentence contains names, numbers, or a switch between languages is a second question, and a different one. Neither should stand in for the other. Neither on its own is the decision.
Judging the system by how it sounds audits only the last link; front-end decisions about pronunciation have to be inspected as text.
Example
Two safety bodies already ruled the decimal dose unresolvable by guess
Take the case where a silent resolution costs the most. It is not a hypothetical the lesson invented. It is a hazard class that safety bodies have already written down and named.
Two entries on the ISMP List of Error-Prone Abbreviations, Symbols, and Dose Designations turn on a single character. “Lack of a leading zero before a decimal point (e.g., .5 mg)” is meant to say 0.5 mg. It is “Mistaken as 5 mg if the decimal point is not seen”. “Trailing zero after a decimal point (e.g., 1.0 mg)” is meant to say 1 mg. It is “Mistaken as 10 mg if the decimal point is not seen”. Both are ten-fold errors. The list is the Institute for Safe Medication Practices', in its 2024 edition.
Both entries carry a double asterisk, and the asterisk has an accreditation standard behind it. ISMP's note to the table says so: “In the Table, error-prone abbreviations, symbols, and dose designations that are included on The Joint Commission’s “Do Not Use” list (Information Management standard IM.02.02.01) are identified with a double asterisk (**) and must be included on an organization’s “Do Not Use” list.” That list was announced in 2003 and banned in accredited hospitals by April 2004. Since 2010 the notations have been carried as elements of performance 2 and 3 under IM.02.02.01. Nine notations are banned in all, trailing and missing zeros among them — the count comes from the Agency for Healthcare Research and Quality's review Making Health Care Safer II.
And the ban is not confined to handwriting. These designations “should NOT be used when communicating medical information verbally, electronically, and/or in handwritten applications”, ISMP states. So a front end that meets ".5 mg", resolves it by rule, and hands one reading to the synthesizer is guessing in the spoken channel. That is the channel the ban explicitly names. And the class is one two safety bodies have already declared unresolvable by guess. Naturalness is what hides it. Whichever way the rule went, the audio comes out equally certain. Nothing in the waveform distinguishes 0.5 mg from a tenfold overdose.
An accuracy score reported per entity type does not close that gap by itself. It tells you how often numbers are read correctly across a set. One silent choice about one ambiguous dose can still pass through unnoticed.
- The decision underneath the whole product is how to design a TTS system across text normalization, pronunciation, language, markup, prosody intent, accessibility, and the product controls wrapped around them.
- The failure class is already named and already enforced. “.5 mg” is mistaken as 5 mg, “1.0 mg” as 10 mg — ten-fold errors, on The Joint Commission's “Do Not Use” list since April 2004, and banned by ISMP for verbal communication as much as for handwriting.
- The evidence that would catch a front-end rule resolving that class silently is text normalization and pronunciation accuracy measured by entity type, not a sentence-level or system-level score.
- The practical response is to probe every entity type the product will actually meet: dates, currency, units, names, acronyms, homographs, and mixed language.
Case
Nineteen languages, and 80 corrections that barely moved the score
That ambiguity is documented, and it is not exotic. The string “123” is read “one hundred twenty three” in “123 pages” and “one twenty three” in “123 King Ave”. The same three characters, two readings. Nothing in the characters themselves chooses between them. What follows decides.
Google's TTS front end is called Kestrel, and it keeps that choice where a person can go and inspect it. Ebden and Sproat described it in 2015: “At the core of Kestrel are text-normalization grammars that are compiled into libraries of weighted finite-state transducers (WFSTs).” Tokenization and the classification of semiotic classes — currency amounts, dates, times, measure phrases — are held separate from verbalization. So the choice between the two readings of “123” is written down somewhere. That is not an architecture diagram's preference. The same authors report the system “used daily by millions of people in nineteen languages and counting”.
Why keep hand-written grammars at all, once a neural model can learn the mapping? Sproat and six colleagues gave the reason in 2019: “Even if unrecoverable errors occur only infrequently, one never knows when to expect such an error, and, because such errors are sporadic, it is hard to guard against them if the neural model is left to its own devices.” Their covering grammars are that guard, and the size of the effect is the whole lesson. The guard changed 80 readings out of roughly 4,400 errors. Every one of them was an unrecoverable reading. Aggregate accuracy moved from 99.18% to 99.20%. Anyone managing the release by the aggregate would have called two hundredths of a percentage point a rounding error, and shipped without the guard.
That is the guard a front end reading “.5 mg” aloud does not have by default. Without it a system can read a dose as a different dose, and read it well.
Example
Four places a name can go wrong, two of them with a specification number
Kestrel's transducers are one stage among several, and it pays to be able to say which stage you mean. A name can be mispronounced in a pronunciation lexicon, in grapheme-to-phoneme conversion, in SSML, or in text normalization. Four separate places, four separate owners. Collapse any two of them into a single word and you lose track of which stage owes the evidence, in what unit, and to whom. Two of the four are not loose engineering terms at all. They have a W3C Recommendation under them, with a version number and a date. The maintenance surface is specified rather than improvised.
- Text normalization converts written symbols into the forms they are intended to be spoken as. It is the stage that had to choose between “one hundred twenty three” and “one twenty three”, and the stage that meets “.5 mg”.
- Grapheme-to-phoneme conversion maps written text to a representation of how it is to be pronounced. Its word error rate is measured language by language. It is also the stage where a name inherited from a lexicon of one variety gets a pronunciation from that variety.
- SSML is the Speech Synthesis Markup Language. Version 1.1 has been a W3C Recommendation since 7 September 2010, and its abstract gives the point: “The essential role of the markup language is to provide authors of synthesizable content a standard way to control aspects of speech such as pronunciation, volume, pitch, rate, etc. across different synthesis-capable platforms.” Vendors implement subsets. Amazon Polly calls its control tags “a subset of the SSML markup tags that are defined by Speech Synthesis Markup Language (SSML) Version 1.1, W3C Recommendation”. It supports <prosody> for “Controlling volume, speaking rate, and pitch”, and <say-as> for “Controlling how special types of words are spoken”, whose interpret-as values include cardinal, ordinal, digits, fraction, unit, date, time, address and telephone. It also supports <sub>, which substitutes a different spoken string for the written one. That last tag is precisely the mechanism behind the failure mode "hidden changes to content".
- A pronunciation lexicon is a maintained mapping from words or names to accepted pronunciations, and the maintenance is specified. The Pronunciation Lexicon Specification, PLS 1.0, has been a W3C Recommendation since 14 October 2008: “This document defines the syntax for specifying pronunciation lexicons to be used by Automatic Speech Recognition and Speech Synthesis engines in voice browser applications.” In production that becomes a concrete object to own. Microsoft's Azure Speech requires a .xml or .pls file using PLS 1.0 elements and attributes, capped at 100 KB. It is cached by URI for up to 15 minutes before a change takes effect. It is case-sensitive at the lexeme level: a phoneme given for "Hello" does not apply to "hello". And an xml:lang attribute binds it to exactly one locale. Somebody maintains that file, for some set of communities and name variants rather than all of them. A corrected name is not audible for a quarter of an hour after the fix.
Comparison
Written form, pronunciation, waveform — and 2.0% against 48.2%
Those places do not sit side by side. They sit in order, and each hands its output to the next. Written forms such as dates, units, and abbreviations are mapped to spoken forms by text normalization. Grapheme-to-phoneme and speech synthesis then take over further down the chain, the first producing a pronunciation representation and the second a waveform.
What the middle column costs is measurable. In 2020 a project called WikiPron mined 1.7 million pronunciations out of Wiktionary, across 165 languages, dialects and scripts. Its authors could not do that without pulling varieties apart first: “Many Wiktionary pronunciations are paired with dialectal specifications, as exemplified by Figure 1. If these specifications were simply ignored, we would obtain a large number of pronunciation variants for each word.” So English appears twice in their table: “English (UK, R.P.)” with 52,425 entries and “English (US, Gen. Am.)” with 48,556. A lexicon encoding one community's version of a word is not a caution here. It is a separate resource, with its own row and its own count.
Their generic grapheme-to-phoneme models use one architecture and one pipeline across all fifteen evaluation languages. Only early stopping and dimensionality selection are chosen per language, on the development set. Neural seq2seq word error then comes out at 2.0% on Hungarian, 5.3% on Bulgarian and 6.0% on French at one end. At the other end: 22.6% on Georgian, 28.8% on Korean, 30.9% on Burmese and 48.2% on English (UK, R.P.). Roughly a twenty-four-fold spread in error, from the same pipeline, depending on which language you happen to ship in.
A test that clears one column therefore says nothing about the next. A result in one language says little about the next either. It is also the reason “123 King Ave” is a normalization problem rather than a synthesis problem. By the time the synthesizer sees it, the number has already been decided, and decided invisibly.
Text normalization
Maps written forms such as dates, units, and abbreviations to spoken forms.
- Decision focus: Define the speaking contract
- Useful evidence: Text normalization and pronunciation accuracy by entity type
- Watch for: Ambiguous numbers and abbreviations resolved silently
- Best used when its assumptions are documented for text-to-speech as a product and the text front end
Grapheme-to-phoneme
Estimates pronunciation from text and lexical context.
- Decision focus: Normalize and pronounce
- Useful evidence: Intelligibility and content error under names, numbers, and code-switching
- Watch for: Pronunciation lexicons encoding only one community or name variant
- Best used when its assumptions are documented for text-to-speech as a product and the text front end
Speech synthesis
Generates acoustic output from linguistic and style conditions.
- Decision focus: Generate acoustic structure
- Useful evidence: Naturalness, preference, and accessibility separately
- Watch for: Markup allowing unsafe volume, speed, or hidden content changes
- Best used when its assumptions are documented for text-to-speech as a product and the text front end
Steps
Write a TTS linguistic test suite on somebody else's class list
So the work is to force those decisions into the open before a listener meets them. The entity list should not be invented either. The released text-normalization data set behind the numbers in the next section is “1.1 billion words of English text, and 290 million words of Russian text”, drawn from Wikipedia and verbalized by Kestrel. The semiotic classes it is scored on give the suite a ready-made spine: DATE, MEASURE, MONEY, CARDINAL, DECIMAL, FRACTION, LETTERS, DIGIT.
A linguistic test suite is where a silently resolved number or abbreviation stops being your assumption and becomes someone else's evidence. It puts “123 pages”, “123 King Ave”, “.5 mg”, 2 mA, £5 and 16 ГБ in front of the front end, in every language the product ships in. Then it records what comes back out as text. As text, because audio is exactly where such an error stops being visible.
Three things go into that record. What the speaking contract assumes: what may be said, in which language, and with which markup, agreed before synthesis. One counterexample to it. And what synthesizing and then verifying against that contract actually triggers when the counterexample turns up.
1. Collect hard text
Include dates, currency, units, names, acronyms, homographs, and mixed language.
2. Define expected speech
Record acceptable pronunciations and context-dependent alternatives.
3. Test critical forms
Use high-risk numbers and negation with automatic content verification.
4. Specify fallback
Use spelling, confirmation, another voice, or human review when ambiguity remains.
Where the suite shows an ambiguity the pipeline never resolved, the listener is owed a spelled-out form, a confirmation, a second voice, or a human — not a fluent guess.
Example
0.997 overall, 0.750 on TIME, same system and same run
A suite like that produces numbers, and the numbers will not agree with one another. That is the finding, not a nuisance. In 2017 Sproat and Jaitly ran an attention-based RNN over an English test set of 92,416 tokens and scored 0.997 overall. Underneath that single aggregate, from the same system and the same run: DIGIT 0.865 and TIME 0.750 in English; in Russian, MEASURE 0.883, MONEY 0.842, DECIMAL 0.867, FRACTION 0.826 and TIME 0.750. Their own abstract states the consequence: “We show that while the RNN model achieves very high overall accuracies, there remain errors that would be unacceptable in a speech application like TTS.”
Look at what the residual errors actually are. 2 mA read as “2 megaamperes” instead of “2 milliamperes”. £5 read as “five” instead of “five pounds”. 11 billion AED read as “eleven billion danish” instead of “eleven billion dirhams”. 16 ГБ read as “sixteen hertz” instead of “sixteen gigabytes”. Each is a unit or currency substitution. Each comes out fluent. Each disappears inside 0.997.
The unit matters as much as the list. A dose read two ways is an entity, not a sentence. A sentence-level score absorbs it the way an aggregate of 0.997 absorbs TIME at 0.750. Per-entity accuracy will also pull against latency, failure fallback, and provenance coverage at some point. An evaluation is worth its cost only if it makes that disagreement visible instead of averaging it away. So the portfolio has to state, in advance, what happens when an ambiguity cannot be resolved at all — where an unresolvable number or abbreviation obliges the system to abstain or fall back rather than guess.
- For the core task, text normalization and pronunciation accuracy by entity type, reported per semiotic class — DATE, MEASURE, MONEY, CARDINAL, DECIMAL, FRACTION, LETTERS, DIGIT — because 0.997 overall and 0.750 on TIME came out of the same run.
- For how the system behaves in use, intelligibility and content error under names, numbers, and code-switching — the slice where grapheme-to-phoneme word error ranges from 2.0% to 48.2% depending on the language shipped.
- For the robustness slice, naturalness, preference, and accessibility, each reported separately rather than folded into one impression, since fluency is manufactured at the waveform stage and reports nothing about the text.
- Across the product's lifetime, latency, failure fallback, and provenance coverage — including the lexicon's own maintenance latency, the 15 minutes a cached PLS file stands between fixing a name and the fix becoming audible.
Score entity by entity, because a dose read two ways vanishes inside a sentence-level number, and say in advance what the system does when an ambiguity cannot be resolved at all.
Key takeaways
- The decimal-dose ambiguity is documented, named and accreditation-enforced. ISMP's 2024 list records “Lack of a leading zero before a decimal point (e.g., .5 mg)” as “Mistaken as 5 mg if the decimal point is not seen”, and “Trailing zero after a decimal point (e.g., 1.0 mg)” as “Mistaken as 10 mg if the decimal point is not seen”. Both sit on The Joint Commission's “Do Not Use” list, banned in accredited hospitals by April 2004, and ISMP's ban covers communicating the information verbally. A front-end rule that silently picks one reading is guessing at a class two safety bodies ruled unresolvable by guess.
- “123” is read “one hundred twenty three” in “123 pages” and “one twenty three” in “123 King Ave”, so what follows decides the reading. Kestrel keeps that decision inspectable — “At the core of Kestrel are text-normalization grammars that are compiled into libraries of weighted finite-state transducers (WFSTs).” — in a system “used daily by millions of people in nineteen languages and counting”. The 2019 covering grammars exist because unrecoverable errors are sporadic. They changed 80 readings out of roughly 4,400 errors, while the aggregate moved only from 99.18% to 99.20%.
- Fluency is produced at the waveform stage. So a natural voice proves nothing about whether the text was correct, unambiguous, authorized, or appropriate. Judging by sound audits only the last of the three links.
- Agree the speaking contract before synthesis — what may be said, in which language, with which markup — and check the output against it afterwards. Both halves have written specifications to point at: SSML 1.1, a W3C Recommendation of 7 September 2010, and PLS 1.0, a W3C Recommendation of 14 October 2008. The second arrives in production as a 100 KB, single-locale, case-sensitive file cached for up to 15 minutes.
- Text normalization, grapheme-to-phoneme, and speech synthesis answer related but different questions, so a test that clears one settles nothing about the next. WikiPron's single architecture and pipeline across fifteen evaluation languages posts 2.0% word error on Hungarian and 48.2% on “English (UK, R.P.)”, and holds English as two separate resources of 52,425 and 48,556 entries.
- Report text normalization and pronunciation accuracy by entity type — 0.997 overall alongside 0.750 on TIME and 0.826 on Russian FRACTION, where 2 mA becomes “2 megaamperes” and £5 becomes “five” — together with latency, failure fallback, and provenance coverage.