ealeng
Menu

Episode 06 of 10

Testing text-to-speech

How to test a machine that speaks: an AI voice read "x7-Q9" as "negative nine quetzals". Text-to-speech has five separate parts of quality, every automatic measure has a blind spot, and the most common bug isn't about sound at all.

Lab 06 · Text-to-Speech Evaluation6 min read
Video transcript5:54

0:00Cold open

0:01

I asked an AI voice to read one line: use the password x7-Q9 for access. Listen.

0:08

🔊 Aura-2 Thalia reads the line

0:12

Did you catch it?

0:15

x seven, negative nine, quetzals. A dash became negative, and Q became the currency of Guatemala.

0:22

It sounds perfectly natural. A naturalness score even liked it more than this voice’s correct readings, on average. And it’s wrong.

0:31Intro

0:32

Hands-on voice AI evals, episode six.

0:36

Today, the other direction: how to test a voice that turns text into speech.

0:4201 · Four questions

0:43

Good speech means four different things, and each one needs its own test.

0:47

Does it say the right words?

0:49

Does it read written things right, like prices, times and dates?

0:53

Does it sound natural?

0:55

And how soon does it start talking?

0:57

I tested six voices: three from Deepgram, three from OpenAI.

1:0302 · The round trip

1:04

First, the right words. I made each voice read thirty everyday sentences. Then a speech recognizer turned the audio back into text, and I counted the word errors. That’s a round trip.

1:16

A recognizer makes mistakes of its own, so I used two. If both make the same mistake, the voice probably said it.

1:24

And on plain sentences, every voice came back almost perfect. One recognizer heard every word, from every voice. Coral’s two point eight percent is one clip; I’ll come back to it.

1:36

So this test hits a ceiling. It can’t tell these voices apart.

1:40

But one of Coral’s clips came back empty.

1:43

🔊 “Children under twelve must be accompanied by an adult.”

1:48

A clear sentence. Deepgram’s recognizer returned nothing at all.

1:52

OpenAI’s recognizer heard every word. So the voice was fine: the measuring tool failed. With only one recognizer, I would have blamed the voice.

2:0303 · Reading “14:00”

2:04

So the evaluation effort belongs on harder text. Like a time.

2:08

🔊 “The briefing starts at 14:00.”

2:11

The briefing starts at fourteen. Fourteen what? Four of the six voices said just: fourteen.

2:18

And a price, read by OpenAI’s older voice, tts-1.

2:22

🔊 “Each download costs $0.99.”

2:25

Ninety-nine. Ninety-nine what? The cents are gone.

2:29

Before speaking, a voice has to decide how to say written text. That’s called text normalization. So I wrote forty tricky items, each with the readings a listener would accept.

2:40

Some trip up everyone. All six voices read the date 2024-12-31 as plain numbers: twenty twenty-four, twelve thirty-one.

2:51

The same voices that read plain sentences perfectly got three to eight of these forty wrong. If your agent reads back totals or times, these are the mistakes customers hear.

3:0204 · Does it sound natural?

3:04

Next: does it sound natural? The classic test is a listening test. People rate clips from one to five, and you average the ratings. That’s called a mean opinion score, or MOS.

3:15

People are slow and expensive, so there are models that predict that score from the audio. I used one called UTMOS. But a predictor is a measuring tool, so I checked it.

3:27

Here are the six voices, all between four point two and four point five.

3:31

Now I added real people: audiobook recordings. Where do you think they ranked?

3:38

Last, tied with the weakest voice.

3:42

The model was trained mostly on ratings of computer voices, and these audiobooks are amateur recordings. So use the score to compare voices, not to ask whether one sounds human.

3:53

Then I damaged a voice on purpose: a phone line, noise, clipping.

3:58

The score dropped every time, down to one point three with heavy noise. So it notices bad sound.

4:04

But here’s its blind spot. I compared readings that were right with readings that were wrong.

4:09

The wrong ones scored just as high, or higher. Negative nine quetzals sounds perfectly natural. The score can’t hear meaning.

4:1805 · How soon does it start?

4:20

Last question: how soon does it start talking? I timed seventy requests per voice, from sending the text to the first audio arriving.

4:28

Deepgram’s fastest voices sent their first audio almost at once: under a fifth of a second.

4:35

But audio often starts with silence. Andromeda sends about four tenths of a second of silence before its first word.

4:42

Andromeda’s audio arrives twice as fast as Coral’s and Ash’s, OpenAI’s newer voices. But it starts with silence, so you hear its first word at about the same time.

4:54

And look at the slowest requests. In my main run, one request in ten to Coral or Ash waited five seconds or more for the first sound.

5:03

Two requests took ten minutes to start. For a caller, that’s silence. A voice agent needs a timeout, and a backup plan.

5:1106 · What I’d test

5:13

So, to test a voice: round trips with two recognizers, on hard text, not just plain sentences. And a test set of the prices, times and codes your product actually says.

5:24

A naturalness score, checked, with real listeners for the final call. And time the first word heard, including the slowest requests.

5:33

Six episodes in, I have a lot of tests. Next time, I turn them into automatic gates, so a bad model can’t ship.Yuki: Gates in CI? 🤔

5:41Summary

5:43

A voice can sound perfect and say the wrong thing. Test the words, the readings, the sound, and the speed, separately.

5:50

Then your voice says what you meant.Yuki: Thanks! 😄

Key takeaways

  • ✓TTS quality has five parts: intelligibility, correctness, naturalness, speed, and clean audio. A voice can be strong on one and weak on another.
  • ✓Round-trip WER needs two recognizers: an error both make is probably the voice's; an error only one makes is probably the recognizer's.
  • ✓Text normalization is the most common TTS bug: voices that read plain sentences perfectly got 3–8 of 40 tricky items wrong.
  • ✓A naturalness predictor can't tell right words from wrong ones: "negative nine quetzals" scored as natural as the correct reading.
  • ✓Time to first byte isn't what the listener waits for: add the silence at the start of the audio.

So far every chapter has evaluated machines that listen. This one evaluates a machine that speaks. Text-to-speech (TTS) looks easier to test, because you choose the text, so you always have the right answer. But "good speech" has several separate parts, and each needs its own measure. One line from the lab shows why: asked to read "Use the password x7-Q9 for access.", a voice said "x seven, negative nine, quetzals." The dash became "negative", and Q became the currency of Guatemala. It sounded perfectly natural (a naturalness score even liked it a little more than this voice's correct readings, on average), and it was wrong.

The lab tests six voices, three from Deepgram (Aura-2 thalia, andromeda, apollo) and three from OpenAI (coral and ash on gpt-4o-mini-tts, and alloy on the older tts-1).

Part Question Automatic measure
Intelligibility Can you understand the words? Round-trip WER
Correctness Did it say what the text meant? Text-normalization tests
Naturalness Does it sound like a person? Predicted MOS
Speed How soon does audio start? Time to first byte, time to first speech
Clean audio No glitches, odd pauses, distortion? Glitch scans

A voice can be strong on one and weak on another, and every automatic measure has blind spots, so a TTS evaluation uses several.

Round trip, with two instruments

The simplest automatic check: synthesize a sentence, transcribe it, and compute WER against the text you sent. The catch is that the recognizer has errors of its own. So use two independent recognizers: an error both make in the same way was probably in the audio; an error only one makes was probably that recognizer's.

Try it · Round trip, with two instrumentsReal results from Lab 06 · click a voice
VoiceWER via nova-3WER via gpt-transcribe
aura-2-thalia0.0%0.0%
aura-2-andromeda0.6%0.0%
aura-2-apollo0.3%0.0%
openai-coral2.8% (1 empty)0.0%
openai-ash0.0%0.0%
openai-tts1-alloy0.6%0.0%

Both recognizers make the same error

Probably in the audio: blame the voice.

Only one recognizer makes it

Probably that recognizer's: blame the instrument.

The instrument failed, not the voice. Nova-3 returned an empty transcript for one clean 4.35 s clip (“Children under twelve must be accompanied by an adult.”), every time it was tried. gpt-transcribe heard it perfectly, and any tiny change fixed it: 0.3 s of silence at the start, resampling to 16 kHz, or half the volume. One recognizer alone would have blamed the voice.

Two lessons from the lab. First, a ceiling: on 30 plain sentences, one recognizer heard all six voices perfectly, and no error was made by both. Plain sentences can't separate modern voices, so the effort belongs elsewhere. Second, the instrument failed, not the voice: one recognizer returned an empty transcript for a clean clip, every time, and adding 0.3 s of silence fixed it. One recognizer alone would have blamed the voice.

The most common bug: reading written text

Before speaking, a TTS system has to decide how to say written text: "$1,250.50", "Dr.", "St." (Street or Saint?), "3/4" (a fraction or a date?), "14:00", "support@acme.io". This is text normalization, and it's the most common real TTS bug. For a voice agent reading back a total, a date or a phone number, it is the product.

The lab tested 40 items in 14 categories. The same voices that read plain sentences perfectly got 3 to 8 of them wrong. Try a few yourself:

Try it · How would a voice read this?Pick what a listener should hear · real results from Lab 06
  • Use the password x7-Q9 for access.

  • The deadline is 2024-12-31.

  • The briefing starts at 14:00.

  • It weighs approx. four kilos.

  • Each download costs $0.99.

  • Her salary is $85k a year.

  • Email support@acme.io for help.

Two practical notes. "Unsure" is a real category: when the two recognizers disagree, someone has to listen before filing a bug, because the judge is two machines with their own formatting habits. And accept the written form back too: recognizers write some readings as digits, so compare digit strings as digit runs (the same trick as Chapter 2's entity checks).

Does it sound natural?

MOS (mean opinion score) is listeners rating clips from 1 (bad) to 5 (excellent). People are slow and expensive, so a predicted MOS model estimates the score from the audio; the lab uses one called UTMOS. Treat the predictor as an instrument and check three things before trusting it: an anchor (what does real human speech score?), a sanity check (does the score fall on obviously damaged audio?), and its blind spot.

Try it · Naturalness and speedReal results from Lab 06
  • openai-ash4.49
  • aura-2-thalia4.44
  • openai-coral4.40
  • aura-2-apollo4.38
  • aura-2-andromeda4.28
  • openai-tts1-alloy4.17
  • Human read speech4.14

Every synthetic voice scored at or above real human read speech (LibriSpeech audiobooks). That says as much about the predictor, trained on ratings of synthetic speech, as about the voices. And ash beat thalia by +0.05: statistically clear, practically meaningless on a 1–5 scale.

The lab's predictor passed the sanity check: noise at 0 dB dropped a voice from 4.43 to 1.28. But every synthetic voice scored at or above real human read speech, which says as much about the predictor (trained on ratings of synthetic speech) as about the voices. And its blind spot matters: readings judged wrong scored the same as correct ones. "Negative nine quetzals" sounds perfectly natural.

How soon does the voice start?

For a voice agent, speed is part of quality. Measure:

  • Time to first byte (TTFB): request sent → first audio bytes arrive.
  • Time to first speech: TTFB plus any silence at the start of the audio. This is what the listener actually waits for.
  • Generation ÷ audio length: below 1, audio is produced faster than it plays, so streaming playback won't stutter.

Switch the widget above to "When is the first word heard?". By first byte, aura-2-andromeda was twice as fast as the OpenAI voices; by first word heard, they tied, because it starts with 0.38 s of silence. And look at the tail. In the main run, one request in ten to gpt-4o-mini-tts waited 5.5 s or more for its first audio (coral), and 12 s or more (ash), while the median was about 0.65 s. Two requests took over 10 minutes with the connection open. For a voice agent, the tail is the silence callers remember: use a timeout and a fallback. (The lab's client now times out at 60 s.)

Streaming matters too: playing audio as it arrives, instead of waiting for the whole file, saved 0.25–1.3 s per reply.

Find glitches without listening to everything

You can't listen to thousands of clips, but you can scan them: speaking rate per clip, long pauses, clipping, leading silence. The lab flagged 14 clips worth a listen, like a phrase spoken at 5.5 words per second. One lesson from building it: the voice activity detector's default settings are built for transcription (0.4 s padding, short pauses merged), so they reported exactly zero leading silence on every clip. For timing, turn the padding off. The same bug had quietly skewed Chapters 3 and 5, and fixing it corrected their numbers.

Practice: score a voice

Implement the pieces: two-recognizer blame, a reading checker that matches spelled-out and written digits, leading-silence detection, and time to first speech. The tests use the lab's real bugs ("negative nine quetzals", the lost cents, the lost "acme") and its real timings.

Try it · Practice in PythonRuns in your browser · ⌘/Ctrl + Enter to run
tts_practice.py55 lines

Quiz

Try it · Check your understanding0 of 6 answered
  1. 1.You synthesize 30 sentences, transcribe them with one recognizer, and it returns an empty transcript for one clean clip. What do you conclude?

  2. 2.Six modern voices read 30 plain sentences, and the second recognizer heard every one perfectly. What does that tell you about where to spend evaluation effort?

  3. 3.Why is text normalization the most common real TTS bug?

  4. 4.A predicted-MOS model scores six synthetic voices between 4.17 and 4.49, and real human read speech at 4.14. What's the right conclusion?

  5. 5.Voice A's first audio byte arrives in 0.33 s, voice B's in 0.63 s. Which one does the listener hear first?

  6. 6.For gpt-4o-mini-tts, the median time to first byte was about 0.6 s. Why might a voice agent still feel broken?

Check your understanding

Try each question before opening its answer.

What are the separate parts of TTS quality, and how is each measured?

Measure intelligibility with round-trip WER, reading correctness with normalization tests, naturalness with MOS/listening, speed with time to first byte and first speech, and clean audio with glitch scans.

How does round-trip WER work, what can't it tell you, and why use two recognizers?

Transcribe synthesized speech and compare it with the input text. It does not measure naturalness and inherits recognizer errors; independent recognizers help distinguish audio errors from one instrument's mistakes.

Why is text normalization a common TTS bug, and how would you test it?

Written forms such as dates, abbreviations, currency, and codes have multiple readings. Build a categorized test set with accepted readings, compare the output through recognizers, and listen to confirm failures.

What's MOS, and how do you decide whether to trust a MOS predictor?

MOS is a 1–5 listener rating; a predictor estimates it. Check an anchor against human speech, a sanity response to damaged audio, agreement with human ratings, and known blind spots.

What's the difference between time to first byte and what the user waits for?

TTFB marks when audio bytes arrive. The listener waits through any leading silence and playback buffering until the first speech, so measure time to first speech too.

How do you find glitches in thousands of clips without listening to all of them?

Automatically scan for unusual speaking rate, long pauses, leading silence, clipping, and recognizer disagreements. Treat flags as a prioritized listening queue, not a verdict.

Next up: turning all of this into a release process. Chapter 7 builds quality gates in CI, including one that blocked a better model, and was right.

Learning something useful?

New labs, articles, and videos on evaluating and testing AI systems come out regularly. Follow along so you don't miss the next one.