A reconstruction of Microsoft Anna, the default voice of Windows Vista and Windows 7 (the SAPI 5 "TTS20" engine, internally called Mulan, from Microsoft Research Asia), in plain C99. It reads the original voice data files and produces exactly the same audio as the real engine: every sample, bit for bit, from text in to 16 kHz PCM out. It runs anywhere: no Windows, no SAPI, no COM.
No Microsoft code or data is included. The C was written from a disassembly and decompilation of the engine DLLs and checked stage by stage against the running engine.
The voice data is not included. You need your own copy of Anna: an installed Windows Vista/7
(C:\Program Files (x86)\Common Files\SpeechEngines\Microsoft\TTS20) or the Windows 7 voices
installer. tools/anna_extract.py turns either into the data folder the engine reads.
| stage | source | verified against the real engine |
|---|---|---|
| text normalizer: sentences, abbreviations, numbers, dates, times, money, phone numbers, SAPI XML | src/anna_norm.c |
token lists identical on 300+ lines and fuzz corpora |
vendor lexicon (M1033DSK.TTS, SAPI compressed lexicon) |
src/anna_lex.c |
400/400 words vs SAPI |
letter-to-sound (M1033DSK.LTS) |
src/anna_lts.c |
4000/4000 words vs the real DLL |
| morphology, POS tagger (63 Brill rules), homographs, phrasing, accents | src/anna_morph.c, anna_pos.c, anna_prosody.c |
10271/10271 words, all record fields |
| unit segmentation and target features | src/anna_units.c |
4553/4553 unit specs |
| unit selection (CART trees + Viterbi), joins, fades | src/anna_voice.c, anna_render.c |
exact |
| WSOLA rate change, TD-PSOLA pitch change, volume | src/anna_render.c |
exact |
| WMA Voice 9 decoder (12 kbps) | src/anna_dec_wmav.c |
all 66218 blocks + 25000 random reads identical to Windows' 32-bit WMSPDMOD.DLL |
| end to end, text → WAV | anna, anna_say |
579/579 test lines bit-exact, including <pitch>, <rate>, <volume>, <silence>, <emph>, <spell>, <bookmark>; identical with MSVC x86/x64, clang and GCC |
make (Linux / macOS / MinGW; output in build/)
src\build.bat x64 (MSVC; x86 works too; output in build\x64\)
Floating point must stay exact: never -ffast-math, keep -ffp-contract=off (the Makefile does), and
32-bit x86 needs -msse2 -mfpmath=sse.
Windows builds produce anna.dll with its import library, and build\<arch>\dist\ collects the DLL,
the .lib, the headers (anna_tts.h is the one to include) and the CLI — everything another program needs.
On other platforms make dist does the same with libanna.so / .dylib.
python tools/make_portable.py goes one step further and writes build\<arch>\portable\: the same drop
with a copy of your own voice data beside it and a launcher that points at it, so the folder runs on a
machine with no voice installed. The data in it is Microsoft's, so keep that folder to your own machines.
python tools/anna_extract.py "C:\Program Files (x86)\Common Files\SpeechEngines\Microsoft\TTS20" anna-data
python tools/anna_extract.py Win7_TTS_Voices_v1.1.exe anna-data (reads the installer without running it)
This writes M1033DSK.{CSD,IDX,UNT,CRT,APL,UDT,TTS,LTS,WIH} plus M1033DSK.KEY (the 129-byte key that
descrambles the audio, read out of MSTTSDecWrp.dll) and M1033DSK.HAN (the pitch changer's window
table, read out of MSTTSEngine.dll). About 39 MB. Keep it to yourself; it is Microsoft's.
The engine also accepts the installed TTS20 folder directly.
build/anna --data anna-data "Hello, my name is Microsoft Anna." hello.wav
build/anna --data anna-data --events @story.txt story.wav (print word/sentence events)
build/anna --data anna-data --xml @song.xml out.wav (SAPI XML)
build/anna --data anna-data --rate 5 --pitch -3 --volume 80 "Faster and lower." out.wav
src/anna_tts.h is the interface; make also builds libanna.so / .dylib / .dll.
anna_tts *t = anna_tts_open("anna-data", err, sizeof err);
anna_tts_set_rate(t, 3); /* -10..10 like SAPI; up to 18 for fast listening */
anna_tts_speak(t, "Hello world.", 0, &callbacks); /* blocks; streams PCM and events */
anna_tts_cancel(t); /* from any thread, stops within one audio chunk */Audio (16 kHz, 16-bit mono) is streamed through a callback as it is produced. Events: sentence and
word starts (byte offsets into the UTF-8 text, with the audio position), <bookmark mark="..."/>
(for index marks), and end. With ANNA_SPEAK_XML the text may use SAPI XML: <pitch middle|absmiddle>,
<rate speed|absspeed>, <volume level>, <emph>, <silence msec>, <spell>, <bookmark mark>.
One anna_tts per thread.
- She is 5.5 hours of one woman's recordings, cut into 159,043 pieces (about 109 ms each). Speaking means picking one recorded piece per sound (unit selection) and gluing them together.
- The recordings are stored at 12 kbps in WMA Voice 9, a codec for voice chat over dial-up, scrambled with a 129-byte XOR key. Windows' decoder also runs a noise-suppressing post-filter over it. Hence the muffled, slightly metallic tone.
- She always talks 25% faster than she was recorded. Her voice settings contain
DefaultRate = 2, so every sentence is squeezed by 3^0.2 = 1.2457 with WSOLA, which adds a slight warble. - Her joins ignore the audio. The cost of gluing two pieces is a constant per word/phrase boundary; it never compares pitch or timbre, so she can jump in pitch mid-sentence. Joins are 6 ms fades with no overlap.
- There is no intonation model. The text front end chooses which kind of piece is wanted (sound, position in the syllable, word and phrase, stress, neighbours), never a pitch or duration. Her melody is whatever the chosen recordings happened to have.
- Pitch changes re-space the recorded vocal-cord pulses (TD-PSOLA using the pitch marks in the
.WIHfile), which keeps her timbre: she sounds like the same person speaking higher or lower, not like a sped-up tape. The range is ±10 = ±5 semitones.
Odd details reproduced for bit exactness: the join-fade window uses pi as 3.1415926535 in single
precision; the decoder is primed by decoding the target block once and then the block before it; WSOLA
restarts every sentence and keeps a carry buffer between chunks (a <silence> passes the carry through,
a rate change drops it); the accent choices come from the C runtime's rand() seeded with 1 on the
speaking thread.
src/– the engine, library, CLI (anna_cli.c), the verification front endanna_say.c, test drivers.anna_dec_dmo.cis an alternative decoder that uses Windows' own WMA Voice DMO.anna_*_tab.hhold small tables transcribed from the engine and codec (abbreviations, tagger rules, word lists, codebooks) that the code needs to behave identically.notes/– how every stage works, with addresses in the original DLLs (engine.md,frontend.md,norm.md,lex.md,units.md,decwrp_common.md,wmav.md).harness/–annatap*.c: load the real engine into a 32-bit process, hook it, speak through SAPI and log its internal records (unit specs, decoder reads, word records) for comparison.tools/– Python models of each stage, comparison scripts,anna_extract.py;tools/re/– reverse-engineering helpers (disassembler, DMO-driving decodercsddec.cpp, spectrum and string tools).tests/– input corpora and comparison scripts per stage (reference outputs from the real engine are not included; the harness regenerates them).ghidra/– headless export scripts used for the decompilation.
MIT for the code in this repository (see LICENSE). Microsoft Anna's voice data and the original engine
are Microsoft's and are not included.