17.2. dasLLAMA text to speech: models, voices, synthesis
Text to speech in pure daslang: load a converted StyleTTS2-lineage GGUF (KittenTTS nano and
mini, Kokoro-82M), run text through the das-native front end (normalizer, part-of-speech tagger,
grapheme-to-phoneme), and synthesize mono f32 PCM per sentence chunk, timed per model stage.
Run with -jit; utils/dasllama-server/txt2wav.das is the canonical program shape, and
the server’s /v1/audio/speech route serves the same facade.
The front-end packs tts_g2p.bin and tts_postag.bin load from the GGUF’s directory. A
model’s voices and sample rate come from caps; the rows GEMMs serve as Q8_0 by default, and
set_tts_q8 pins the lane for the loads that follow.
Hands-on: the text to speech tutorial.
17.2.1. Constants
- TTS_CHUNK_CHARS = 400
TTS_CHUNK_CHARS:int const
17.2.2. Enumerations
- TtsKind
enum TtsKind
17.2.3. Structures
- TtsModel
A loaded TTS model: the shared assembly, the family’s driver data, and the front-end packs
read beside the GGUF. Load with load_tts_model; delete frees every plane and pack.
- Fields:
kind : TtsKind - the family the GGUF’s architecture named
model : StyleTts2Model = StyleTts2Model() - the shared StyleTTS2-lineage assembly, its voices and the family’s driver data
scratch : St2Scratch = St2Scratch() - the activation carrier every synthesis reuses
g2p : G2pModel -
tts_g2p.bin: lexicons, rules and the fallback chaintagger : PosTagger -
tts_postag.bin: the tokenizer and part-of-speech taggerpocket : PocketModel = PocketModel() - the Pocket TTS family’s model, when the GGUF is one; it reads text, not phonemes
pocket_scratch : PocketScratch = struct<dasllama_pocket::PocketScratch>(uninitialized ) - its activation carrier
chunk_chars : int = TTS_CHUNK_CHARS - the longest chunk a synthesis speaks, in codepoints (
tts_set_chunk_chars)
- TtsVoicePrompt
Portable Pocket codec latents. Prepare with an encoder-bearing model; register with a compatible model, including a compact file without the encoder. Serialization is caller-owned.
- Fields:
version : int = 1 - Serialized prompt format version.
family : string = “pocket” - Model family tag.
language : string - Language expected by the receiving model.
sample_rate : int - Reference audio sample rate in hertz.
latent_dim : int64 - Number of codec latent values per frame.
frame_samples : int64 - Number of PCM samples represented by each frame.
latents : array<float> - Flattened codec latent frames.
- TtsCaps
What a loaded TTS model can do: its voices (canonical names; aliases resolve in the family file), the PCM rate it emits, the languages it speaks, whether it clones a voice from audio, and whether a speed means anything to it.
- Fields:
voices : array<string> - canonical voice names, in the model’s order
voice_langs : array<string> - the language each voice speaks, by index into
voicessample_rate : int - PCM samples per second the model emits
langs : array<string> - the languages it speaks, as codes
cloning : bool - whether a voice can be cloned from audio
speed : bool - whether a speed scales the durations; a model without one refuses any speed but 1.0
- TtsTimings
Where a synthesis spent its time, in microseconds of wall clock, model loading excluded:
the text front end, then each model stage. total_us covers front end through waveform.
- Fields:
front_end_us : int64 - normalize, tag, phonemize, map into the family’s symbols
bert_us : int64 - the PL-BERT pass
text_enc_us : int64 - the text encoder
duration_us : int64 - the duration encoder and the alignment
prosody_us : int64 - F0 and energy
decoder_us : int64 - the decoder
source_us : int64 - the harmonic-plus-noise source
generator_us : int64 - the iSTFTNet generator
prompt_us : int64 - the voice and text prompt through the backbone (the continuous-audio families)
backbone_us : int64 - the frame loop’s backbone steps
head_us : int64 - the frame loop’s flow head
codec_us : int64 - latents to PCM through the codec decoder
total_us : int64 - front end through waveform
samples : int64 - PCM samples produced
- TtsAudio
Synthesized speech: mono f32 PCM at sample_rate, with the time it took.
- Fields:
pcm : array<float> - mono samples in [-1, 1]
sample_rate : int - samples per second
timings : TtsTimings - where the synthesis spent its time
- TtsNoise
The source noise a synthesis consumed - captured from the oracle for a parity run, or drawn from the session’s own generator into a carrier every synthesis reuses.
- Fields:
uniform : array<float> - one initial phase per harmonic; drawn per synthesis into a reused carrier
normal : array<float> - [samples x harmonics], sample-major
captured : bool - supplied by an oracle dump: a synthesis reads it as is and never redraws
- KittenFamily
struct KittenFamily
- KokoroFamily
struct KokoroFamily
17.2.4. Loading and capabilities
- caps(m: TtsModel ): TtsCaps
What the loaded model offers: the canonical voice names the front end can drive, each with its language (a voice whose language it does not phonemize is left out; aliases resolve at synthesis), the PCM rate, the languages, whether it clones a voice, whether a speed applies.
- Arguments:
m : TtsModel
- finalize(m: TtsModel )
Free the model’s planes, the carrier and both packs; delete m runs it.
- Arguments:
m : TtsModel
- g2p_pack_path(dir: string ): string
def g2p_pack_path (dir: string) : string
- Arguments:
dir : string
- load_tts_model(path: string ): TtsModel
Load a TTS model: a converted GGUF (KittenTTS, Kokoro or Pocket TTS, picked from its architecture) or its prepared
.dlim image; the phoneme families read tts_g2p.bin and tts_postag.bin from the same directory (Pocket
needs neither). A GGUF serves the rows GEMMs on the lane tts_serves_q8 names; an image serves the lane it was baked on.
- Arguments:
path : string
- tts_has_phonemes(m: TtsModel ): bool
Whether tts_phonemize has an answer for this model: the phoneme families yes, a Pocket
model no - it reads text, and its chunks carry no phoneme string.
- Arguments:
m : TtsModel
- tts_needs_packs(path: string ): bool
Whether the model at path reads the front-end packs (tts_g2p.bin, tts_postag.bin)
from its directory: the phoneme families do, a Pocket TTS GGUF reads text and needs neither -
decided before any load, by the architecture test load_tts_model makes (a .dlim is a phoneme family).
- Arguments:
path : string
- tts_voice_lang(m: TtsModel; voice: string ): string
The language the front end reads voice in - a canonical name from caps or a family
alias, resolved the way synthesize resolves it. A voice this model cannot drive panics
here with the message a synthesis would give, rather than making the caller speak a line.
- Arguments:
m : TtsModel
voice : string
17.2.5. Voices
tts_prepare_voice_prompt (var m: TtsModel; pcm: array<float>; sample_rate: int) : TtsVoicePrompt
tts_register_voice (var m: TtsModel; name: string; pcm: array<float>; sample_rate: int)
tts_register_voice_prompt (var m: TtsModel; name: string; prompt: TtsVoicePrompt) : bool
tts_voice_prompt_valid (m: TtsModel; prompt: TtsVoicePrompt) : bool
- tts_prepare_voice_prompt(m: TtsModel; pcm: array<float>; sample_rate: int ): TtsVoicePrompt
Encode a reference once for shipping as a voice prompt. The original model revision’s language/codec must match the receiving model; incompatible data is rejected on registration.
- Arguments:
m : TtsModel
pcm : array<float>
sample_rate : int
- tts_register_voice(m: TtsModel; name: string; pcm: array<float>; sample_rate: int )
Clone a voice: pcm (mono, at the model’s own sample rate - caps().sample_rate) joins
the roster under name and speaks from the next synthesis on. A model whose caps() says
it cannot clone panics here.
- Arguments:
m : TtsModel
name : string
pcm : array<float>
sample_rate : int
- tts_register_voice_prompt(m: TtsModel; name: string; prompt: TtsVoicePrompt ): bool
Add or replace a named prepared voice without a codec encoder. Failure preserves the roster; the prompt is copied and its backbone state is built lazily at the first synthesis.
- Arguments:
m : TtsModel
name : string
prompt : TtsVoicePrompt
- tts_voice_prompt_valid(m: TtsModel; prompt: TtsVoicePrompt ): bool
Validate bounded finite latent frames and the model’s language/codec geometry before registration.
- Arguments:
m : TtsModel
prompt : TtsVoicePrompt
17.2.6. Text front end
17.2.6.1. tts_chunks
- tts_chunks(m: TtsModel; norm: string ): array<string>
The chunks a synthesis on this model takes, by the family’s own driver rule: Kitten’s appends a comma to a chunk the split left bare, Kokoro’s sends the text as it is (its voices render an added mark as a breath), Pocket’s splits on its tokenizer’s sentence marks under a token budget.
- Arguments:
m : TtsModel
norm : string
- tts_chunks(norm: string; max_len: int = TTS_CHUNK_CHARS; ensure_punct: bool = true ): array<string>
- tts_normalize(text: string ): string
The spoken form of text: the normalization pass every synthesis runs first (rules
alone - it reads no pack), where “Dr.” reads “Doctor”, “3.5” reads “three point five” and
“$12” reads “twelve dollars”.
- Arguments:
text : string
17.2.6.2. tts_phonemize
- tts_phonemize(m: TtsModel; text: string ): string
The phonemes one already-normalized sentence becomes, in the front end’s own American
inventory (the tagger of tts_postag.bin labels the tokens, the grapheme-to-phoneme pass of
tts_g2p.bin reads them); a family wanting other symbols rewrites them. A Pocket model panics.
- Arguments:
m : TtsModel
text : string
- tts_phonemize(m: TtsModel; text: string; lang: string ): string
17.2.7. Synthesis
- synthesize(m: TtsModel; text: string; voice: string; speed: float = 1f ): TtsAudio
Text -> speech: normalize, chunk by sentence, phonemize, map into the family’s symbols,
synthesize; the chunks concatenate, the timings sum. Sample-identical to
synthesize_stream over the same text.
- Arguments:
m : TtsModel
text : string
voice : string
speed : float
- synthesize_stream(m: TtsModel; text: string; voice: string; speed: float; blk: block<(chunk:TtsAudio):auto> ): auto
Text -> speech, one sentence-sized chunk at a time: the block receives each chunk’s audio as
soon as it exists, in order. Normalization runs once over the whole text; voice is a
name from caps or a family alias, and an unknown one panics.
- Arguments:
- tts_release_scratch(m: TtsModel )
Free the activation scratch a synthesis or a clone left behind - the carrier of either family (a StyleTTS2 carrier whose noise the parity rail captured stays) and the block home’s scratch globals; the next run grows them back for a few milliseconds. For a platform where idle memory matters, called when nothing is queued.
- Arguments:
m : TtsModel
- tts_set_chunk_chars(m: TtsModel; chars: int )
Cap the chunk a synthesis speaks at chars codepoints (default TTS_CHUNK_CHARS; under one is
refused): the peak memory a say holds is the largest chunk’s, so a memory-tight platform lowers it and
the same text speaks in more, shorter pieces, each its own synthesis. Pocket’s chunks take no cap.
- Arguments:
m : TtsModel
chars : int
17.2.8. The served lane
- reset_tts_q8()
Drop the set_tts_q8 pin: the next load follows the policy default again.
- set_tts_q8(on: bool )
Pin the GEMM weights’ format for subsequent TTS loads of every family: Q8_0 quants (their
own prepared image beside the GGUF) or the file’s f32 planes; reset_tts_q8 returns to
the policy default. The pin is per context: a worker thread pins for itself.
- Arguments:
on : bool
- tts_serves_q8(): bool
Would the next TTS load serve its rows GEMMs as q8 - the pin when set, the policy otherwise.
17.2.9. Timings
- rtf(t: TtsTimings; sample_rate: int ): double
Real-time factor: generation seconds per second of audio produced.
- Arguments:
t : TtsTimings
sample_rate : int
- timings_line(t: TtsTimings; sample_rate: int ): string
One line for the log: seconds of audio, wall time, RTF and the per-stage split in ms - the stages a family ran.
- Arguments:
t : TtsTimings
sample_rate : int