Neural audio codec optimization¶
VoiceHub optimizes codec operations and execution boundaries, not a hard-coded list of model-specific optimization implementations. A codec may use dense residual vector quantization, hierarchical rates, a continuous VAE bottleneck, or a decoder-only graph embedded in an LLM TTS runtime. The shared layer describes those differences without changing checkpoint ownership or pretending that all codec algorithms are interchangeable.
The public building blocks in this guide live in:
from voicehub.components.audio.codecs.base import (
DenseCodecCodes,
RaggedCodecCodes,
separate_audio_codec,
)
from voicehub.components.audio.codecs.catalog import (
get_codec_entry,
list_codec_entries,
)
from voicehub.optimization.codecs import (
CodecOptimizationConfig,
capture_codec_cuda_graph,
discover_codec_compile_targets,
)
The catalog is graph-free: inspecting it does not import PyTorch or any model implementation. It is kept separate from runtime model loading while its owner bindings are tested against the active TTS registry.
Native codec inventory¶
VoiceHub's 17 native codec families cover all 23 registered LLM-based TTS
model types. Shared families appear once and list every owning model. “Split”
means waveform reconstruction continues through architecture-owned stages,
such as flow matching plus HiFT, rather than a single decode() method.
| Native codec family | LLM-TTS model types | Representation | Encoder / bottleneck / decoder | Available optimization surfaces |
|---|---|---|---|---|
| SNAC 24 kHz | orpheustts |
Hierarchical discrete | Native / native / native | Compile, CUDA Graph, Snake |
| DAC | dia, outetts, parlertts, zonos, zonos2 |
Dense discrete | Native / native / native | Compile, CUDA Graph, Snake, CuTe VQ search |
| EnCodec | bark |
Dense discrete | Native / native / native | Compile, decoder-only CUDA Graph |
| Mimi | csm, conversationtts |
Dense discrete | Native / native / native | Compile, CUDA Graph |
| XCodec2 | llasa |
Dense discrete | Native / native / native | Compile, CUDA Graph, SnakeBeta |
| NeuCodec | neutts |
Dense discrete | Native / native / native | Compile, CUDA Graph, SnakeBeta |
| MOSS Audio Tokenizer | mosstts |
Dense discrete | Native / native / native | Compile |
| Qwen3-TTS 12 Hz tokenizer | qwen3tts |
Dense discrete | Native / native / native | Compile, decoder-only CUDA Graph, SnakeBeta |
| VibeVoice acoustic tokenizer | vibevoice |
Continuous VAE | Native / Gaussian / native | Compile, safe CUDA Graph |
| VoxCPM AudioVAE v2 | voxcpm |
Continuous VAE | Native / Gaussian / native | Compile, safe CUDA Graph, Snake |
| Higgs Audio Tokenizer v2 | omnivoice, higgstts |
Dense discrete | Native / native / native | Compile, Snake |
| Fish ModifiedDAC | fishtts |
Dense discrete | Native / native / native | Compile, CUDA Graph, Snake |
| Fluac | vui |
Dense discrete | Native / native / native | Compile, CUDA Graph, Snake |
| Chatterbox S3 | chatterbox |
Dense discrete | Native / native / split native | Compile, CUDA Graph, Snake |
| GPT-SoVITS S2 | gptsovits |
Dense discrete | Integrated native pipeline | Compile, CUDA Graph |
| XTTS2 DVAE | xtts |
Dense discrete | Native / native / native | Compile, CUDA Graph |
| CosyVoice speech tokenizer | cosyvoice |
Dense discrete | Native / native / split flow + HiFT | Compile, CUDA Graph, Snake |
Query the same source of truth programmatically:
from voicehub import get_codec_entries_for_model, list_codec_entries
for entry in list_codec_entries():
owners = [owner.model_type for owner in entry.owners]
print(entry.codec_id, owners, entry.optimization.surfaces)
qwen_codec, = get_codec_entries_for_model("qwen3tts")
assert qwen_codec.stages.is_full_native_codec
Each entry also records exact implementation import paths, stage primitives, fixed-shape graph constraints, aliases, and any remaining artifact-conversion or split-pipeline boundary. This makes unsupported cases explicit instead of silently selecting a model-specific fast path.
CUDA Graph support is stage-specific. EnCodec and Qwen3-TTS advertise only
validation-free decoder tensor boundaries; validate code ranges and encoded
frame structure before capture. Their encoders retain host-side length or
bandwidth preparation outside capture. MOSS and Higgs retain
torch.compile support, but do not advertise CUDA Graphs because their active
public boundaries extract CUDA tensor values on the host.
Represent dense and multirate codes¶
DenseCodecCodes represents the common
[batch, codebook, frame] layout. The optional lengths tensor records the
valid frame count before padding.
import torch
codes = DenseCodecCodes(
values=torch.randint(
0,
1024,
(2, 8, 150),
dtype=torch.int64,
device="cuda",
),
lengths=torch.tensor([150, 121], device="cuda"),
)
print(codes.batch_size, codes.num_codebooks, codes.num_frames)
Hierarchical codecs can emit levels at different frame rates.
RaggedCodecCodes retains each level rather than padding every level to the
fastest rate. A level may have shape [batch, frame] for one codebook or
[batch, codebook, frame] for a group of same-rate codebooks.
multirate_codes = RaggedCodecCodes(
levels=(
torch.randint(0, 4096, (2, 12), device="cuda"),
torch.randint(0, 4096, (2, 1, 24), device="cuda"),
torch.randint(0, 4096, (2, 2, 48), device="cuda"),
),
lengths=(
torch.tensor([12, 10], device="cuda"),
torch.tensor([24, 20], device="cuda"),
torch.tensor([48, 40], device="cuda"),
),
# Temporal stride relative to the fastest level.
strides=(4, 2, 1),
)
print(multirate_codes.temporal_lengths)
The containers validate tensor rank, integer dtype, batch consistency, and device consistency. Codebook value ranges remain the codec's responsibility because sizes can differ between levels and checkpoints.
Separate components without reparenting modules¶
Assigning an existing encoder or decoder to a second nn.Module wrapper can
register it twice and change state-dict paths. separate_audio_codec() instead
returns a plain, non-owning view:
before = tuple(codec.state_dict())
parts = separate_audio_codec(codec)
assert parts.encoder is codec.encoder
assert parts.bottleneck is codec.quantizer
assert parts.decoder is codec.decoder
assert tuple(codec.state_dict()) == before
The view recognizes conventional encoder, quantizer or bottleneck, and
decoder attributes. Explicit attribute names can describe a different
native graph:
parts = separate_audio_codec(
codec,
encoder="acoustic_encoder",
bottleneck="vector_quantizer",
decoder="acoustic_decoder",
)
features = parts.encode_features(audio)
quantized = parts.apply_bottleneck(features)
waveform = parts.decode_features(quantized)
This is a view of the original objects, not a new checkpoint topology. It is also useful for continuous autoencoders: the encoder, Gaussian bottleneck, and decoder remain independently addressable, so a diffusion or LLM system can optimize latent decoding without capturing audio encoding or sampling.
Discover the executed boundary¶
discover_codec_compile_targets() first honors a codec's explicit
codec_optimization_compile_targets(mode) or
optimization_compile_targets(mode) declaration. Otherwise it discovers
methods structurally.
- In inference, direct code-to-waveform methods such as
decode_codes,decode_code,from_indices, anddecode_tokenstake priority over latent decoding. A decoder-only module whose operation isforwardis still a valid target. - In training,
forwardis preferred for both whole-codec and quantizer selection so the differentiable encoder, bottleneck, decoder, and losses remain connected. In inference, inverse quantizer methods such asfrom_codes,from_indices, andembed_codestake priority. - Explicit components can select
encode,quantizer,flow,vocoder,decode,forward, or all available boundaries.decodeis the serving umbrella for declared flow, vocoder, and direct decoder stages.
inference_targets = discover_codec_compile_targets(
codec,
mode="inference",
)
training_targets = discover_codec_compile_targets(
codec,
mode="training",
)
print([(target.label, target.attribute) for target in inference_targets])
print([(target.label, target.attribute) for target in training_targets])
An explicit hook may return OptimizationCompileTarget declarations, method
names, or (label, owner, attribute[, component]) tuples. Architecture-owned
targets set the optional component field so automatic and explicit stage
selection resolve the same live callable. Returning an empty iterable is a
deliberate fail-closed declaration: automatic compilation stays eager and
required compilation raises. For a runtime with a declaration, all includes
its declared live stages and any structurally separate encoder or quantizer
stages. It does not add an unrelated generic root forward method behind the
architecture's back during inference.
Choose a fidelity policy¶
Every codec configuration records one of three fidelity tiers:
| Policy | Meaning | Suitable techniques |
|---|---|---|
exact |
Preserve the same algorithm and explicit randomness; compare within dtype-appropriate numerical tolerances | PyTorch reference operations, method compilation, fixed-shape graph replay, static conditioning |
relaxed |
Permit declared numerical changes while retaining the same high-level algorithm | Triton/CUDA periodic activations, FP16/BF16, fast transcendental math after parity measurement |
approximate |
Permit a semantic or quality trade-off that must be benchmarked per checkpoint | Polynomial activations, quantization, altered stochastic behavior |
The policy is a declaration, not an automatic quality downgrade.
policy="approximate" does not itself replace an activation or freeze VAE
noise. Conversely, an automatic exact plan never selects an approximate
transformation.
Resolve and apply a decoder-only plan¶
For LLM TTS, token-to-waveform decoding is normally the latency-critical codec path. Compile that boundary without compiling an unused encoder:
from voicehub.optimization.capabilities import OptimizationContext
context = OptimizationContext(
mode="inference",
device="cuda",
dtype="float16",
)
config = CodecOptimizationConfig(
policy="exact",
kernel_backend="auto",
compile="auto",
compile_components="decode",
compile_config={
"backend": "inductor",
"mode": "max-autotune-no-cudagraphs",
"dynamic": True,
},
)
plan = config.resolve(codec, context=context)
print(plan.to_json_string())
application = plan.apply(codec)
optimized_codec = application.model
decode_method = (
plan.compile_targets[0].attribute
if plan.compile_targets
else "decode"
)
waveform = getattr(optimized_codec, decode_method)(codes.values)
# Restore method bindings and kernel selectors in reverse order.
application.restore()
compile="auto" may retain eager execution if no compatible target or
compiler is available. Use compile="required" only when fallback would be an
error.
Resolve an encoder-only plan¶
Dataset preparation and codec fine-tuning may need the encoder without the waveform decoder:
encoder_config = CodecOptimizationConfig(
policy="exact",
kernel_backend="auto",
compile="auto",
compile_components="encode",
compile_config={
"backend": "inductor",
"fullgraph": False,
"dynamic": True,
},
)
encoder_plan = encoder_config.resolve(
codec,
mode="inference",
context=context,
)
encoder_application = encoder_plan.apply(codec)
encoded = encoder_application.model.encode(audio)
encoder_application.restore()
For end-to-end fine-tuning, resolve in mode="training" and use
compile_components="forward". Custom training operations must provide a
backward implementation; a fake or shape-inference registration alone is not
autograd support. PyTorch documents the required operator surface in its
custom operator tutorial.
Select exact custom kernels before compilation¶
An architecture-owned codec block can expose:
set_codec_kernel_backend(backend);- a current
codec_kernel_backendvalue; - optional
resolve_codec_kernel_backend(backend, device, dtype)capability resolution; and supported_kernel_operationsplussupported_codec_kernel_backends.
When this protocol is present, the plan places CodecKernelPass before
TorchCompilePass. The compiler therefore captures the already resolved
operation. The pass is codec-scoped and cannot configure a diffusion or
language-model block that happens to expose the older generic selector.
Routing is operation-specific: a single DAC plan may select CuTe for vector
quantization while retaining Torch for Snake.
Current codec Snake and independent-frequency/magnitude SnakeBeta
implementations preserve the native periodic formulas and gradients with
Torch, Triton, and CUDA-extension routes. DAC vector quantizers additionally
implement audio.codec.euclidean_vq_search. Its CuTe route sends the
similarity GEMM through NVIDIA's CUTLASS Operator API, caches the compiled CuTe
DSL artifact per shape/layout/SM, and performs the norm correction and
nearest-index reduction in PyTorch. Only the discrete lookup is replaced;
embedding lookup, commitment/codebook losses, and the straight-through
gradient path remain native.
CuTe is an explicit optional backend:
config = CodecOptimizationConfig(
policy="relaxed",
kernel_backend="cute",
compile=False,
)
plan = config.resolve(codec, context=cuda_context)
optimized = plan.apply(codec)
VoiceHub validates Linux, CUDA, the CuTe DSL, the Operator API GEMM interfaces,
and the active tensor contract. If CUTLASS exposes no compatible operator for
the GPU architecture, dtype, shape, or layout, the explicit request fails;
there is no silent Torch fallback for the VQ operation. Importing VoiceHub
does not import CUTLASS or compile a kernel. NVIDIA documents the
GemmArguments discovery and compile/run contract.
CUDA Graph capture must retain at least one warmup call so the CuTe artifact is
compiled before capture; the default warmup_steps=3 already satisfies this
constraint.
The exact policy pins these periodic activations to the PyTorch reference.
Triton tl.sin and CUDA fast-math can differ numerically, so selecting either
accelerator requires policy="relaxed" or policy="approximate". With a
relaxed automatic policy, capability resolution still retains Torch when an
accelerator implementation is unavailable. Explicit CuTe also requires a
relaxed or approximate policy because GEMM rounding can change the selected
code only for vectors at a nearest-neighbor boundary.
If a codec has no selector protocol, kernel_backend="auto" leaves the native
graph untouched. An explicit backend with no supported codec operation fails
instead of pretending an optimization was applied. PyTorch's official
user-defined Triton kernel guide
explains how custom kernels participate in torch.compile.
Capture a fixed-shape CUDA Graph¶
CUDA Graphs reduce repeated Python and kernel-launch overhead, but replay uses the memory addresses and control flow recorded during capture. VoiceHub's helper owns static input buffers, copies new values into them, replays the graph, and clones outputs by default:
codec = codec.cuda().eval()
code_tensor = codes.values.to("cuda")
runner = capture_codec_cuda_graph(
codec,
(code_tensor,),
decoder_only=True,
warmup_steps=3,
)
same_shape_codes = torch.randint_like(code_tensor, high=1024)
waveform = runner(same_shape_codes)
The default target="auto" uses the same architecture declaration as
torch.compile. For Qwen3-TTS and EnCodec, it resolves to the declared
validation-free decoder tensor boundary; callers must validate codes or frame
metadata before capture. A multi-stage codec such as Chatterbox returns an
ambiguity error until the caller selects a declared label or method such as
flow_inference or hift_inference. MOSS and Higgs are intentionally absent
from the CUDA Graph catalog until their active boundaries no longer perform
host-synchronized scalar extraction.
Every replay input must retain:
- the same nested tuple, list, mapping, or dataclass structure;
- identical tensor shape, stride, dtype, and CUDA device;
- identical non-tensor argument values; and
- one shared CUDA device for all tensors.
At least one tensor input is required. Length, batch, codebook count, and
streaming chunk variations therefore need separate graph buckets. Set
clone_outputs=False only when the caller understands that the returned
static output storage will be overwritten by the next replay.
These constraints follow PyTorch's
CUDA Graphs documentation.
For variable shapes, use dynamic torch.compile or explicit static buckets
rather than padding every request without measuring the extra decoder work.
Control stochastic VAE inputs¶
A VAE encode or full forward commonly performs:
PyTorch's default CUDA generator is graph-aware, so captured randn operations
advance correctly across replay. VoiceHub permits that native path. Passing
epsilon explicitly remains useful when the application needs direct,
request-by-request control:
audio = audio.cuda()
epsilon = torch.randn(
(audio.shape[0], 64, 100), # Use the checkpoint's posterior shape.
device=audio.device,
dtype=audio.dtype,
)
vae_runner = capture_codec_cuda_graph(
vae,
(audio,),
target="forward",
epsilon=epsilon,
stochastic_vae=True,
)
new_epsilon = torch.randn_like(epsilon)
reconstruction = vae_runner(audio, epsilon=new_epsilon)
The callable must actually accept the epsilon keyword. If it does not,
omit explicit epsilon or capture the deterministic decoder after sampling:
decoder_runner = capture_codec_cuda_graph(
vae,
(sampled_latents,),
target="decode",
decoder_only=True,
)
waveform = decoder_runner(new_latents)
Native codecs can declare is_stochastic_vae, stochastic_vae, or
uses_stochastic_bottleneck; structural detection also recognizes common
Gaussian/VAE bottlenecks. A VAE encoder that only returns posterior parameters
can list its method in deterministic_codec_targets; VoxCPM and VibeVoice use
this target-level declaration. Explicit non-default torch.Generator inputs
are registered with the graph and must live on the capture device. Hidden
custom generators cannot be registered by a generic wrapper and should be
made explicit by the codec.
What the fast-codec research changed¶
The shared design was informed by
fast-snac at e669190,
the upstream SNAC implementation,
fast-dacvae at 406f2e5,
and the upstream DACVAE implementation.
Those research repositories are valuable measurements, but their fastest
benchmark paths are not all interchangeable with a reusable training and
serving API.
Techniques adopted as exact system capabilities¶
| Research observation | VoiceHub treatment |
|---|---|
| Decoder-only compilation removes unused encode work from LLM TTS inference | Structural decode discovery and independently resolvable encoder/decoder plans |
| Small periodic Snake/SnakeBeta activations are useful custom-kernel boundaries | Architecture-owned same-formula Torch, Triton, and CUDA selectors run before compilation; accelerator transcendental math requires a relaxed policy |
torch.compile benefits a stable decoder method without requiring a new checkpoint wrapper |
Reversible method compilation retains the original model and state-dict keys |
| CUDA Graph replay helps fixed, repeated shapes | A reusable runner copies each new input into owned static buffers and validates the complete shape/stride/device contract |
| VAE encoder, bottleneck, and decoder have different optimization and randomness requirements | A non-owning component view, graph-aware default RNG, and optional explicit epsilon control |
See the reviewed
fast-snac optimization code
and
fast-dacvae optimization code
for the experiments behind these boundaries. The general compilation API is
documented by
torch.compile.
Techniques kept opt-in or rejected as defaults¶
| Research technique | Why it is not an automatic exact optimization |
|---|---|
fast_sinf or polynomial Snake |
Both replace the native transcendental calculation. They belong to a measured relaxed or approximate profile, not exact. |
| Caching SNAC decoder noise | Reusing one random sample changes stochastic behavior. Exact execution must retain explicit, request-specific randomness. |
| Freezing one DACVAE epsilon | It turns stochastic sampling into replay of one realization. VoiceHub uses graph-aware CUDA RNG by default and accepts explicit epsilon when request-level control is needed. |
| Replacing every Conv1d/ConvTranspose1d with channels-last 2D modules | Performance is hardware- and shape-dependent, and structural replacement changes the module/checkpoint topology. It requires an isolated, reversible implementation and parity benchmarks. |
| Removing weight normalization in place | Materialization can be mathematically equivalent for frozen weights, but mutating the training graph and canonical parameter names is unsafe as a universal pass. |
| Omitting or simplifying the watermark path | This changes externally visible model behavior and cannot be called an execution-only optimization. |
| Shape-specialized replay that never copies a new input | It benchmarks one sample rather than providing a correct serving callable. VoiceHub copies and validates every replay input. |
| Custom operations with only a fake kernel and no backward | They are inference-only. Training support requires registered autograd and gradient-parity tests. |
| FP16/BF16 results | Reduced precision can be worthwhile, but it is a relaxed numerical policy and must be reported separately from FP32 parity. |
The reviewed fast-dacvae commit also has inconsistent license signals between
its root license and package/README metadata. VoiceHub does not copy that
implementation as a new license authority; source-derived work must follow
the upstream DACVAE Apache-2.0 terms and retain applicable notices.
Validate before enabling a backend¶
Report compile cold start separately from steady-state latency. For each backend and shape bucket, compare:
- encoder, quantizer, decoder, and end-to-end latency;
- p50 and p90 latency, real-time factor, and peak memory;
- code-index agreement for an encoder path;
- waveform length, SNR or SI-SDR, and log-mel error;
- offline and streaming-chunk parity, including state reset; and
- eager versus optimized gradients for every training kernel.
An accelerator path should remain disabled when it does not beat PyTorch for the measured shapes. Exact parity tests must use the same checkpoint, inputs, codebook depth, and explicit VAE epsilon.