Skip to content

Latest commit

 

History

3 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Microsoft Anna in portable C

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.

Status

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

Building

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.

Voice data

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.

Running

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

Library (for screen readers and other programs)

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.

Why Anna sounds the way she does

  • 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 .WIH file), 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.

Repository layout

  • src/ – the engine, library, CLI (anna_cli.c), the verification front end anna_say.c, test drivers. anna_dec_dmo.c is an alternative decoder that uses Windows' own WMA Voice DMO. anna_*_tab.h hold 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 decoder csddec.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.

License

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.

About

No description, website, or topics provided.

Resources

Stars

6 stars

Watchers

2 watching

Forks

Releases

Packages

Contributors

Languages