Skip to content
←Back to Open Source

OPEN SOURCE DEEP DIVE

AudioTTSASR

audio.cpp: One C++ Runtime for 110+ Local Audio Models

A ggml-based pure C++ audio inference framework that pulls 110+ model families (TTS, voice cloning, voice conversion, speech recognition, diarization, VAD, source separation, codecs and music generation) into one native runtime with CUDA, HIP, Vulkan, Metal and CPU backends, entered through a CLI, an OpenAI-style server, an embedded WebUI and JSON pipelines; same-machine comparisons against Python reference paths show 1.8x to 5x on several TTS routes and up to 37% lower peak VRAM with Q8 packages.

0xShug0/audio.cpp3.5kC++NOASSERTION10 min read

audio.cpp is a pure C++ audio inference framework built on ggml. It pulls more than a hundred model families — text-to-speech, voice cloning, voice conversion, speech-to-speech, speech recognition, speaker diarization, VAD, source separation, forced alignment, audio codecs, music and sound-effect generation — into one native runtime with a shared execution path. As of v0.9.1 (2026-10-07) the repository covers 110+ model families and 190+ variants, runs on Windows, Linux and macOS, and targets CUDA, HIP/ROCm, Vulkan, Metal and CPU-only machines. The question it answers is blunt: can local audio models run fast, stable and portable without a dozen Conda environments and hundreds of Python packages?

The problem: audio models trapped in Python deployments

The pain in local audio is not the models, it is the deployment shape. Every TTS model ships its own repository, its own dependency list, its own inference script; swap to another ASR and install again; voice conversion is a third stack. The README says it plainly: trying a few audio models means juggling Conda environments, Python packages and dependency conflicts. audio.cpp gives those disconnected research codebases a shared native execution path instead. Weights load as GGUF or at original precision, compute graphs live on ggml, and backend selection, session management, batching and streaming are framework responsibilities. Python survives only in the weight-preparation step of some models, never in the inference path.

The second pain is cognitive. Audio model naming, lineage and architecture differ wildly, so the README ships an explicit task taxonomy: TTS, Clone, VC, S2S, ASR, Classify, Wake, Align, VAD, Diar, Codec, Sep, MIDI, Music, SFX, Edit, Design, Dialogue and Ctrl. The companion Audio Model Architecture Atlas draws 107 families into one interactive lineage graph, showing which models share an autoregressive backbone, which vocoder, which codec family. Atlas and repository are two exits of the same understanding.

Architecture: one shared framework, 84 family directories

The code splits cleanly into framework and models. src/framework/ is the shared layer: runtime/ holds session.cpp (session lifecycle and reusable state), graph_executor.cpp and graph_optimizer.cpp (ggml graph execution and optimization), kv_cache.cpp, flow_kv_cache.cpp and bounded_static_kv_decode.cpp (caches for autoregressive and flow decoding), greedy_causal_decoder.cpp and registry.cpp (family registration). Sibling directories — codecs/, conditioners/, decoders/, tokenizers/, modules/, model_spec/, package_manager/, sampling/ — hold the vocoders, conditioning encoders and sampling strategies that many families reuse.

src/models/ contains 84 family directories (ace_step, apollo, chatterbox, cosyvoice3, qwen3_tts, vibevoice and friends), each a loader plus execution path for one family; new ports land in src/community_models/ first, where review is lighter but reproducible validation is still required. src/capi/audiocpp.cpp exposes a C ABI, the entry point for downstream wrappers and packaging. The application layer lives in app/: cli/, server/, streaming/, workflow/, model_manager/ and gguf/, covering the command line, the HTTP server, streaming sessions, JSON pipelines, model download management and GGUF packaging.

audio.cpp architecture poster: 14 shared pipeline groups mapped to 107 model families
The static cut of the architecture atlas: 14 shared pipeline groups (AR audio generation, AR-guided acoustic generation, conditioned acoustic generation, audio token diffusion, duration-based synthesis, MTS-style synthesis, encoded audio to AR symbols, transducer recognition, CTC recognition, latent voice conversion, direct audio reconstruction, speaker activity tracking, transcript alignment, conditioned visual generation) with 107 families placed on their routes. Fix a shared component once and a whole column of models benefits.

The payoff of that layering shows in release cadence. Between v0.5 and v0.9.1 the family count grew from 44 to over 110, and most additions wire into existing shared components rather than rewriting an inference stack. The README's stated goal is exactly this: highly optimized, reusable building blocks for audio models, so new integrations come up faster, shared parts improve once and benefit many families, and real end-to-end inference paths stay efficient, maintainable and portable.

Coverage: a task-by-technology matrix

The repository's own task-by-technology matrix makes the coverage concrete: 43 inventory entries on speech synthesis, 26 on recognition and understanding, 11 on music/sound/video, 6 on voice conversion and coding, 8 on separation and restoration, 6 on diarization/VAD/alignment, 3 on neural audio utilities. On the technology axis, Qwen-family backbones appear in 16 families, DiT and ConvNeXt in 8 each, CAM++ in 7, MOSS codec in 6. Language backbones, generation architectures, encoders, codecs and prediction heads each get their own column, so a reader can see at a glance which stacks are reused across tasks.

Task by technology matrix: cross counts between seven task groups and thirty-plus technology stacks
Task-by-technology matrix. Darker cells mean more families share that technology; a blank cell only means no recorded association, not absence of support. The matrix also makes the real point: coverage is not 107 independent implementations, it is a handful of stacks reused across tasks.

Four surfaces: CLI, OpenAI-style server, WebUI, JSON pipelines

There are four ways in. audiocpp_cli is the direct one; --metrics prints offline wall time, audio duration, RTF, realtime factor, sample rate and channels. audiocpp_server speaks OpenAI-style /v1/audio/speech and /v1/audio/transcriptions, plus the more general /v1/tasks/run and /v1/tasks/batch, so existing OpenAI clients connect by changing a base URL. The embedded SvelteKit WebUI starts on port 8080 with --ui and ships an Arena tab that queues several models or GGUF variants over one shared input and sorts results by metrics, plus experimental generation history. app/workflow runs JSON pipelines; files such as assets/pipeline/speech_redub.json chain multi-step tasks into one reproducible path. Build-time, AUDIOCPP_MODEL_SET=full|core|custom trims which families get compiled into the binary.

Performance evidence: measured against Python on the same box

The methodology deserves its own paragraph. Every Python-relative number was measured on one Ubuntu machine with an RTX 5090 and the CUDA backend, calling the framework runtime API directly for both audio.cpp and the matching Python reference path — not CLI end-to-end timings. The authors also state that these numbers were taken at initial release and several paths have improved substantially since, so read them as a release baseline, not a current peak.

Model (one-shot)Speedup vs PythonWall time reduction
vevo25.03x-80.11%
pocket_tts3.68x-72.80%
miotts2.73x-63.39%
moss_tts_local2.33x-57.07%
qwen3_tts1.83x-45.34%
vibevoice1.40x-28.75%

Long-lived sessions, where one loaded session serves requests in sequence, keep the gains: pocket_tts 3.22x, qwen3_tts 2.74x, moss_tts_local 2.66x. That column matters most for real applications, because model load, cached state and runtime setup amortize across requests. Absolute speed is clearer from the RTF table:

Model / modeAudio length (s)Wall time (s)RTFFaster than realtime
pocket_tts (one-shot)8.080.260.03231.09x
moss_tts_local (one-shot)9.600.970.1019.91x
omnivoice (one-shot)9.001.320.1466.84x
miotts (one-shot)20.403.300.1626.18x
supertonic (long-form, CUDA)379.322.020.005187.62x
supertonic (long-form, CPU)379.4061.400.1626.18x
pocket_tts (long-form)353.127.300.02148.40x
vibevoice (long-form, 4 speakers)5615.731376.840.2454.08x
Bar chart of one-shot speedups versus the Python reference implementation per model
One-shot speedup bars. The dotted line is 1.0x parity: above it audio.cpp beats Python, below it loses. Three orange bars — Citrinet ASR, HydraNets separation and MarbleNet VAD — sit below the line, and the authors keep them visible with the note that bars under 1.0x spotlight where optimization work is still worth doing.

Three headline results put the tables in context. VibeVoice 1.5B generates a 93.9-minute podcast in 18.2 minutes with 10 diffusion steps and no quantization, roughly 5.15x faster than real time. Supertonic 3 produces about 10 hours of audio in 3 minutes on an RTX 5090, with 47 ms time-to-first-token in CUDA streaming mode. On the TranscrIA benchmark of messy real French meetings, audio.cpp's Nemotron 3.5 ASR matches the WER of other implementations in about a quarter of the wall time.

GGUF and quantization: memory savings are real, speedups are per-model

Model distribution centers on GGUF packages (the audio-cpp/audio.cpp-gguf repo on Hugging Face plus a ModelScope mirror). The Q8 report's conclusion: tested packages reach up to 1.53x speedup and 37% lower peak VRAM versus 16-bit weights. The memory column is what matters on consumer GPUs:

Model16-bit peak VRAM (GiB)Q8 peak VRAM (GiB)
higgs_audio_tts9.15.9
fish_audio11.87.5
voxtral_realtime10.77.6

The authors' stance on quantization is unusually restrained: lower precision is a per-model, per-route optimization, not a globally safe default. ACE-Step 1.5 low-precision runs can fail at runtime with ACE-Step planner masked decode found no valid token while higher precision completes; VeVo2 outputs away from fp32 drift visibly from the fp32 reference under the repo's own waveform and log-mel similarity metrics, even in output length; and on Qwen3-TTS, q8_0 buys only about 3.8% speed for about 25% less peak VRAM. Memory you can buy; a free lunch you cannot.

Engineering method: parity gates and honest negative results

A runtime that hosts 100+ families risks optimizations silently breaking a path nobody runs daily. audio.cpp's answer is a documented parity workflow: establish a CPU reference at fp32 with fully controlled randomness, generate a baseline WAV for CUDA or the target backend, then after any optimization, refactor or fix run the comparison script through three gates — byte-identical output, cosine similarity, log-mel similarity. Only deep optimizations that intentionally trade a little accuracy for performance may take the controlled baseline-reset branch; ordinary changes must stay byte-identical. tests/warmbench.py is the harness for those comparisons and tools/audiocpp_cli/run_audiocpp_cli_path_tests.py covers the CLI path.

Parity test flow: CPU reference, backend baseline, development change, gate comparison and the baseline-reset decision
The parity workflow decision graph. The default rule sits at the bottom: refactors, bug fixes and ordinary performance work must preserve byte-identical outputs; only deep performance optimizations justify a controlled baseline reset.

Limitations and risks

Collecting author-stated and measurable caveats: the performance figures are an initial-release baseline and several paths have improved substantially since, so they should not be quoted as current peaks. GGUF here is a container, not a universal architecture adapter — llama.cpp or whisper.cpp GGUF files are not automatically compatible unless tensor names and metadata map onto the matching family implementation. Build_xcframework.sh is outdated and Metal plus Apple XCFramework packaging still need retesting after the framework refactor. Lazy loading never unloads; keeping many models resident needs limits such as max_loaded_models. Some models still require a Python-side manager for weight preparation, so "Python-free inference" is not "Python-free everywhere". Backend and model coverage are model-dependent: the same backend is not equally supported across families.

Ecosystem position

audio.cpp has started playing the role of an audio inference substrate. Unsloth Studio drives its speech, music and speech-to-text through audio.cpp; the FunASR platform documents deploying Fun-ASR-Nano and SenseVoice with it; the downstream list holds 16 projects, including AIRI Audio Server, a zero-Python Node.js microservice wrapping /v1/audio/speech with SSE streaming and a serialized GPU queue. Hugging Face hosts the audio-cpp/audio.cpp-gguf weight repo and the Architecture Atlas space; macOS gets a Homebrew tap. For a repository created in June 2026 and already at v0.9.1 by October, that ecosystem growth is itself a signal.

Summary

audio.cpp's value is not one model running fast. It is turning "local audio models" from a pile of mutually incompatible research repositories into composable capabilities on one native runtime: a shared framework layer for sessions, graphs, caches and vocoders; a model layer where families plug in quickly; an application layer with CLI, OpenAI-style server, WebUI and JSON pipelines as four exits. The performance claims come with same-machine comparisons against Python (1.8x to 5x on several TTS paths, 187x realtime for Supertonic 3 long-form), the engineering comes with parity gates and publicly kept negative results, and the limitations are stated plainly. For teams that need TTS, ASR, voice conversion or separation on their own machines or servers, it is one of the few options whose inference path carries no Python debt.

As an Amazon Associate, we earn from qualifying purchases.