Skip to content

Parakeet — speech-to-text (NVIDIA NeMo FastConformer)

Offline, streaming, and live-microphone transcription of WAV audio on the CPU (any rate/channels — see Audio input).

This example runs NVIDIA NeMo Parakeet FastConformer ASR models from GGUF: CTC, TDT, and hybrid TDT+CTC decoder heads, multilingual prompt-conditioned models, and cache-aware streaming variants. It is a port of mudler/parakeet.cpp by Ettore Di Giacinto (the ready-to-run GGUF weights come from his conversions), pinned at 89f5e29, and is validated against it per stage: the hard parity target is an exact decoded token-id sequence; intermediate stages gate on cosine (op-order makes bit-exact unrealistic).

Getting the model

Weights are not part of this repository. mudler/parakeet-cpp-gguf is one flat repo with every supported NVIDIA NeMo Parakeet model × quantization (f16/q8_0/q6_k/q5_k/q4_k). Start with tdt_ctc-110m-f16.gguf (267 MB hybrid, fast) or tdt-0.6b-v3-f16.gguf (1.44 GB, multilingual). The hf CLI (from pip install -U huggingface_hub; shared download machinery in docs/RUNNING-MODELS.md) fetches single files:

mkdir -p models/parakeet
hf download mudler/parakeet-cpp-gguf tdt_ctc-110m-f16.gguf --local-dir models/parakeet

License. The NVIDIA NeMo Parakeet weights are distributed under CC-BY-4.0.

The streaming modes (--stream, --mic, --mic-sim, --stream-bench) need a model with streaming.* metadata — the runner errors otherwise. The models benchmarked for streaming in docs/BENCHMARK.md are realtime_eou-120m and the multilingual nemotron-0.6b. Their files in the same repo are realtime_eou_120m-v1-f16.gguf and nemotron-3.5-asr-streaming-0.6b-f16.gguf, with the same quantization ladder:

hf download mudler/parakeet-cpp-gguf realtime_eou_120m-v1-f16.gguf --local-dir models/parakeet

Audio input

Any RIFF WAV decodes: PCM 16/24/32-bit int or 32-bit IEEE float (incl. WAVE_FORMAT_EXTENSIBLE), any sample rate, any channel count. Multi-channel audio is downmixed by averaging and resampled to 16 kHz with the same linear resampler as parakeet.cpp (load_audio_16k_mono), so native 16 kHz mono is simply the no-resampling path. Other containers (MP3/FLAC/…) are not supported.

The repository ships no audio. The pinned parity reference carries two 16 kHz mono fixtures — speech.wav (7.435 s of speech, the fixture behind the parakeet rows in docs/BENCHMARK.md) and clip.wav (2 s, stage-parity fixture; it decodes to an empty transcript, so use speech.wav when you want visible output):

tools/fetch_refs.sh parakeet.cpp
# -> refs/parakeet.cpp/tests/fixtures/{clip,speech}.wav  (refs/ is gitignored)

Smallest end-to-end run (model from above + fetched fixture; prints the transcript on stdout):

zig build parakeet -Doptimize=ReleaseFast -- \
  --model models/parakeet/tdt_ctc-110m-f16.gguf \
  --audio refs/parakeet.cpp/tests/fixtures/speech.wav --transcribe

CLI

# Transcribe a WAV (clean transcript on stdout)
zig build parakeet -Doptimize=ReleaseFast -- --model models/parakeet/tdt_ctc-110m-f16.gguf \
  --audio clip.wav --transcribe

# JSON output / per-word timestamps (offline decode only)
zig build parakeet -Doptimize=ReleaseFast -- --model models/parakeet/tdt_ctc-110m-f16.gguf \
  --audio clip.wav --transcribe --json --timestamps

# Batch a manifest (one audio path per line)
zig build parakeet -Doptimize=ReleaseFast -- --model models/parakeet/tdt_ctc-110m-f16.gguf \
  --manifest files.txt

# Streaming pipeline (cache-aware chunked encode; use a streaming-capable model)
zig build parakeet -Doptimize=ReleaseFast -- --model models/parakeet/tdt_ctc-110m-f16.gguf \
  --audio clip.wav --stream

# Live microphone (build the capture backend in with -Dparakeet-mic)
zig build parakeet -Dparakeet-mic -Doptimize=ReleaseFast -- \
  --model models/parakeet/tdt_ctc-110m-f16.gguf --mic

# Benchmarks: best-of-N offline timing / streaming RTF + first-token latency
zig build parakeet -Doptimize=ReleaseFast -- --model ... --audio clip.wav --transcribe --bench-reps 5
zig build parakeet -Doptimize=ReleaseFast -- --model ... --audio clip.wav --stream-bench --bench-reps 5

Running with only --model prints the config + tensor summary.

Flags

flag meaning
--model <path> parakeet GGUF (required)
--audio <path> WAV to transcribe (see Audio input)
--transcribe offline transcription of --audio
--stream streaming pipeline (cache-aware chunked encode) over --audio
--manifest <file> batch transcription: one audio path per line
--mic live microphone capture (needs -Dparakeet-mic, see below)
--mic-sim feed --audio through the incremental mic driver
--stream-bench streaming RTF + first-token latency benchmark
--json JSON output (offline decode only)
--timestamps per-word start/end/confidence (offline decode only)
--threads <n> worker thread count (0 = default)
--decoder tdt\|ctc decoder head for hybrid models (default tdt)
--lang <XX> target locale for multilingual prompt-conditioned models (default auto)
--compare <stage> <dump> parity-check a stage vs a parakeet.cpp PKD1 dump (offline stages: stft, mel, subsampling, encoder, ctc, tdt, joint0; streaming stages: stream-mel, stream-sub, stream-encoder, stream-prompt, stream-session, stream-decode, stream-full)
--tol <f> max-abs tolerance for --compare (default 1e-4)
--f32-cache cache sequence linear weights as f32 and route them through BLAS
--fast-mel BLAS mel filterbank projection (on by default; --no-fast-mel disables)
--bench-reps <n> run transcribe n times in one loaded session, report best timing

--json is rejected in combination with --stream; --decoder ctc needs a model that actually carries a CTC head (the hybrids do) and errors cleanly otherwise. A failed --compare stage exits nonzero, so the parity harness is mechanically enforcing; regenerating the reference dumps needs the out-of-tree instrumentation patch (tools/fetch_refs.sh --patch parakeet.cpp).

--manifest lines are trimmed; blank lines and # comments are skipped, and a line starting with { is parsed as a NeMo-style JSONL entry (its "audio_filepath" value is used). Each file's output honors --json/--timestamps, is preceded by a # <path> header, and is identical to transcribing that file singly.

Live microphone: -Dparakeet-mic

--mic is compiled out of the default build. Build with -Dparakeet-mic to link the vendored miniaudio capture stack — it reuses the NAM example's audio shim (examples/nam/audio_shim.c + third_party/miniaudio.h, capture only, no MIDI). The option defaults to false to keep the default parakeet build fast; without it, --mic exits with a message pointing at the flag. On macOS the build links the CoreFoundation/CoreAudio/AudioToolbox frameworks; on other platforms miniaudio dlopens its backend at runtime. --mic also requires a streaming-capable model. --mic-sim runs the same incremental driver over --audio without any capture hardware and needs no build option.

--mic captures from the first capture device at 16 kHz for a fixed 20 s window, reprinting the partial transcript as tokens arrive, then finalizing and exiting. On macOS the microphone permission is attributed to your terminal app — a denied permission yields silence with no error (check System Settings → Privacy → Microphone); the NAM example has the same note for the same capture stack.

Performance

Honest perf note: transcription is parity-checked against parakeet.cpp/NeMo, but parakeet.cpp is still faster on CPU — on the 110m hybrid, Fucina full-transcribe RTF ≈ 0.034 vs parakeet.cpp ≈ 0.021 (M1 Max), a ~1.6× gap. See docs/BENCHMARK.md.

Shared knobs

Build discipline (-Doptimize=ReleaseFast, build on the machine you run on or pass -Dcpu), global thread control, and the BLAS backends are shared across all runners and documented in ../../docs/RUNNING-MODELS.md. This runner has no MoE-streaming, GPU-offload, or constrained-decoding surface; --threads above is its per-run thread override.