8.7.7. dasLLAMA-07 — Speech to Text
dasLLAMA transcribes speech through one uniform surface: a single loader that
sniffs the file format, one verb set, and a caps() call that tells you what
the loaded model honestly supports. No family names appear in the API — the
same program runs a whisper.cpp bin, a Qwen3-ASR GGUF pair, or a Parakeet-TDT
bin.
Run it with any supported model and a 16 kHz mono PCM16 WAV:
daslang.exe -jit tutorials/dasLLAMA/07_speech_to_text.das -- ggml-tiny.bin jfk.wav
daslang.exe -jit ... -- Qwen3-ASR-0.6B-Q8_0.gguf mmproj-Qwen3-ASR-0.6B-bf16.gguf jfk.wav
daslang.exe -jit ... -- ggml-parakeet-tdt-0.6b-v2-f32.bin jfk.wav
8.7.7.1. One loader, sniffed formats
load_asr_model looks at the file, not the filename: a ggml bin routes to
whisper or Parakeet by its vocabulary size; a GGUF decoder takes its
audio-encoder mmproj as a second path. A mismatched or unsupported file panics
with a message that says what to do instead.
var m <- load_asr_model("ggml-tiny.bin") // whisper family
var q <- load_asr_model("Qwen3-ASR-0.6B-Q8_0.gguf",
"mmproj-Qwen3-ASR-0.6B-bf16.gguf") // GGUF pair
8.7.7.2. caps(): ask, don’t assume
Capabilities are advisory — they let a front end grey out what a model can’t do. Invalid requests still panic loudly at the call site; nothing is silently ignored.
let c <- caps(m)
// c.languages — codes create_session accepts (empty = the model detects itself)
// c.translate — speech-to-English translation task
// c.timestamps — none / segment / word granularity
// c.streaming — native incremental decode
// c.prompt — context/prompt conditioning (Qwen3-ASR: AsrSession.context)
8.7.7.3. Transcribe
The one-shot form returns the full text; the block form yields each
TranscribeSegment as its window completes — with centisecond timestamps,
the raw token ids, and avg_logprob, the mean per-token log-probability
(closer to zero = more confident).
var s <- create_session(m, "auto") // "auto": whisper detects the language
let text = transcribe(m, s, samples) // one-shot
print("{s.detected_lang}: {text}\n")
transcribe(m, s, samples) $(seg) { // per-segment
print("[{seg.t0} - {seg.t1}] {seg.text} (avg_logprob {seg.avg_logprob})\n")
}
Language auto-detection costs whisper one extra prompt decode on the first
audio, then sticks for the session. Qwen3-ASR always detects — it reports the
spoken language itself, surfaced through the same detected_lang field.
8.7.7.4. The push-chunks rail
feed buffers 16 kHz samples, drain transcribes every complete 30 s
window (leaving the rest pending), and flush finishes the tail. This is
the shape a live audio source drives — pair it with dasAudio’s microphone
capture (AUDIO-11 — Recording from the Microphone). Models that transcribe whole
clips at once say so with a loud panic, matching caps().streaming.
feed(m, s, chunk) // as audio arrives
drain(m, s) $(seg) { ... } // complete windows only
flush(m, s) $(seg) { ... } // the sub-30 s tail, at end of stream
See also
Full source: tutorials/dasLLAMA/07_speech_to_text.das
Previous tutorial: dasLLAMA-06 — The Architecture Registry · Next tutorial: dasLLAMA-08 — Audio Chat
The transcription CLI: examples/dasLLAMA/transcribe.das