Internals
audio.cpp
The inference engine: one binary, no Python and no CUDA install, and how Soundbox picks a backend for your machine.
Prompt. Words, a duration and a seed. Nothing has been loaded yet. The request is queued against the model you picked.
audio.cpp is the inference engine behind most of the models Soundbox runs. It is a ggml-based C++ runtime that implements 50+ audio model families behind a single CLI, with CUDA, HIP, Metal, Vulkan and CPU backends. There is no Python in the inference path.
Because of that, adding a model that runs through audio.cpp only takes a catalogue entry: an id, a repo and a few defaults. No separate torch install or sidecar process is needed.
Installing it
Soundbox manages the binary for you:
soundbox model install audiocpp # download, verify, cache
soundbox model status audiocpp # what is installed, and which backendThe installer picks a release asset for your OS and GPU, verifies its SHA-256
against GitHub's asset digest and the release's checksum manifest, and caches
it under <data dir>/bin/audiocpp/<version>/. An asset with no published
checksum is refused unless you set SOUNDBOX_ALLOW_UNVERIFIED_BINARIES=1. A
mismatch is always refused. To override the choice:
soundbox model install audiocpp --backend cuda # cuda | hip | metal | vulkan | cpu
soundbox model install audiocpp --version 0.6.1The CLI then works without the server or web app running:
soundbox list # the catalogue, with download status
soundbox pull audiocpp-stable-audio-3-sfx
soundbox run audiocpp-stable-audio-3-sfx "glass breaking on concrete" --out test.wavrun offers to pull a model you have not downloaded. Pass --yes to skip the
prompt.
Using a binary you already have
Prebuilt Windows CUDA and CPU packages are on the
releases page. On macOS, run
brew tap 0xShug0/audio-cpp && brew install audio-cpp. On Linux, build with
scripts/build_linux.sh --backend cuda --target audiocpp_cli.
Point Soundbox at the binary, or put audiocpp_cli on your PATH:
SOUNDBOX_AUDIOCPP_BIN=/path/to/audiocpp_clisoundbox model status audiocpp reports which binary it resolved and how. A
managed install shows its version; a binary of yours shows external (env) or
external (PATH). The same three routes, in the same order, apply to the
prompt-assistant engine via SOUNDBOX_LLAMA_SERVER_BIN (soundbox model status llm). On macOS both also check the Homebrew prefixes directly, because
a desktop launcher's PATH often omits them.
Choosing a backend
Soundbox infers a backend and falls back to CPU. To override the guess, or to compare backends, force one:
SOUNDBOX_AUDIOCPP_BACKEND=cuda # cpu | cuda | vulkan | metal | hip | bestIf a generation is far slower than expected, it is usually running on CPU.
soundbox model status audiocpp shows which backend is live.
Where weights go
<models dir>/audiocpp/<model-id>/Weights do not go in the Hugging Face cache. The GGUF repos hold every family audio.cpp supports, and Soundbox downloads only the files for the model you asked for.
The model catalogue
The catalogue lives in audiocpp_models_catalog.json. Adding or correcting a
model is a JSON edit, with no new dependency, adapter or environment.
Models come in two tiers:
- Listed models appear in the model picker.
- Extras are catalogued but hidden until you opt in, via the Models page's
"Enable more models" section or
PATCH /api/settings {"enabled_audiocpp_models": ["…"]}. Enabling one adds it toGET /api/modelsimmediately, with no restart.
GET /api/models/audiocpp/catalog returns the whole catalogue, all tiers.
Bring your own model
Entries in <data dir>/models/custom.json are loaded alongside the shipped
catalogue, in the same shape:
{
"models": [
{
"id": "my-ace-step-finetune",
"name": "ACE-Step (my finetune)",
"vendor": "me",
"description": "Q8 quantisation of my own finetune.",
"family": "ace_step",
"category": "music",
"weights_path": "/models/ace-step-mine-q8_0.gguf",
"max_duration_s": 240,
"sample_rate": 44100,
"channels": 2,
"vram_estimate_mb": 8000,
"defaults": { "duration_s": 30, "steps": 27, "cfg": 4.0 },
"supports": { "seed": true, "steps": true, "cfg": true, "lyrics": true },
"license": { "name": "Apache-2.0", "commercial": "yes", "url": "https://…" }
}
]
}weights_pathis an absolute path to weights already on your disk: a file, or a directory forweights_layout: "directory". Nothing is downloaded, and Soundbox never deletes the file.- Alternatively, give
hf_repo+weights_globand it downloads like any shipped entry, into<models dir>/audiocpp/<your id>/. familymust be one audio.cpp knows (audiocpp_cli --list-loaders). Every other field (defaults,supports,edit_routes,voice_ids,request_optionsand the rest) means the same as for a shipped entry.- If you omit
license, the model is shown as Unspecified / not commercial, since Soundbox cannot know the terms for weights on your disk.
Custom entries are always enabled; there is no separate toggle in Settings.
They are never marked verified, because "verified" means someone ran the model
end-to-end through this adapter. They use the same download/load flow in the
app as other models, and soundbox list tags them [custom].
The file is re-read whenever it changes, so you can edit it while the server
runs, with no restart. A malformed file, or an entry missing
id/name/family or naming neither weights_path nor hf_repo, is
skipped with a warning on the server's console. An id that matches a shipped
model replaces it. Use this to point an existing model at your own
quantisation.
Every entry declares its licence, and the UI shows it before you download. Some of these models have open weights but a non-commercial licence.
Capabilities
Each family supports a different set of features. The catalogue declares what each one supports, and the app offers only what is declared.
| Capability | Notes |
|---|---|
| Generation | Duration, steps, guidance and seed are common across gen loaders |
| Lyrics | Lyrics-conditioned vocal models (ACE-Step). Some require lyrics; see below |
| Separation | htdemucs, bs_roformer, mel_band_roformer split a mix into stems |
| Extend | Continue a clip past its end, with the original kept bit-for-bit |
| Cover | Re-render a clip in a new style, at the source's length |
| Repaint | Regenerate a window inside a clip |
Negative prompts are not available on this path. No family exposes a flag for them, so Soundbox does not offer one.
Lyrics
A lyrics-conditioned model takes the sung words separately from the prompt.
The prompt sets genre and production; the lyrics are what is sung. Some
models, including MiniMax Music 3, refuse to run without lyrics. For
those, Soundbox supplies an explicit [instrumental] marker when you give
none.
Separation
Separation is its own task mode. Two constraints produce confusing errors if you miss them:
- The source is passed as the audio input, not as a reference input. The reference input is for voice conversion and style transfer. Using it for plain separation fails with an unrelated-sounding contract error.
- htdemucs requires exactly 44.1 kHz. Other rates fail with a sample-rate mismatch, so Soundbox resamples the source to the model's rate before invoking it. Splitting a 48 kHz ACE-Step render is the common case.
htdemucs emits four stems: vocals, drums, bass, other.
Audio-to-audio routes
There are three, available on models that declare support:
| Route | What you get |
|---|---|
extend |
The source unchanged, followed by new material up to the target length |
cover |
The clip re-rendered in the style of a new prompt, locked to the source's length |
repaint |
A window inside the clip regenerated, with a strength and a mode |
The sound editor's Repaint tool runs repaint on a highlighted range
(POST /api/repaint, or generate with edit: "repaint").
Extend is implemented as a repaint whose window starts past the end of the
source, so the original is kept sample-for-sample. cover takes a strength
that sets how closely it follows the original.
The source is resampled to the model's rate first, as with separation.
Adding a model
Most models are a single GGUF file, and a catalogue entry naming the repo and the file pattern is enough. Two cases need more care.
Package models
Some models ship a directory of components instead of one file. MiniMax Music 3 has five (condition encoder, language model, transformer, RVQ depth decoder, vocoder) plus config and tokenizer JSON. Passing only the largest file to the loader would drop the rest without an error.
These catalogue fields cover this. All have defaults, so existing entries are unaffected:
weights_layout: "file" | "directory"."directory"passes the whole weights directory to the loader.weights_required: string[]. Every file that must be present. The model counts as downloaded only when all of them are, so a half-finished download reports "not downloaded" instead of failing inside ggml.weights_root: string. The repo-relative subdirectory that is the package's real root, for a package under a prefix inside a shared repo. audio.cpp's own downloader strips the prefix (strip_prefixin the package spec); Soundbox preserves repo paths, so--modelhas to point at the subdirectory. MiniMax H3'sMiniMax-H3-Q4-GGUF/prefix inside the shared registry repo needs it; MiniMax Music 3's dedicated repo does not.
weights_glob accepts a list, which keeps a download bounded. One of these
repos is 58 GB and the entry pulls 8.45 GB of it.
Component filenames are not yours to choose
For a package model, the loader reads its component filenames from a package spec compiled into the binary, and checks those files exist before it parses any option. Overriding a component path with a session or load option does not work, because the check has already failed.
The catalogue entry therefore has to match the package the installed binary ships. Read it out of the binary; the spec is embedded as plain JSON:
node -e "const d=require('fs').readFileSync('audiocpp_cli.exe','latin1'); const i=d.indexOf('minimax_music3_q4_0'); console.log(d.slice(i-3000,i+1500))"Two things to watch for. The package's name does not mean its contents are
uniform: a "q4_0" package can ship a q8_0 component. And the per-component
option defaults in the same spec can disagree with the package's own file
list. Use the files array, not the name or the defaults.
How Soundbox drives it
- Loading resolves paths; it does not fill VRAM. The child process holds the model in memory, per generation.
- Cancelling a job kills the child. An orphaned process would hold a GPU that Soundbox believes is free, and the next job would contend for it.
- A failed run shows the child's own last lines, so you see the ggml error instead of "generation failed".
- The binary is never auto-installed as part of a model download. Install
it explicitly with
soundbox model install audiocpp.
Adapter tests drive a fake CLI speaking the same protocol, so they cover the integration without the real binary or real weights.