VibeVoice Acoustic Tokenizer โ€” ONNX

ONNX conversion of microsoft/VibeVoice-AcousticTokenizer โ€” the continuous acoustic speech tokenizer (VAE codec) shared by VibeVoice TTS and ASR. It operates at an ultra-low 7.5 Hz frame rate (hop = 3200 samples @ 24 kHz), encoding waveform to a 64-dim latent per frame and decoding it back.

Exported with Olive (dynamo โ†’ ONNX). No PyTorch / transformers needed at runtime โ€” only onnxruntime + numpy (+ soundfile/librosa for audio I/O).

Files

This tokenizer is shared across the VibeVoice family โ€” the same acoustic codec feeds the TTS and ASR models โ€” so inference_tokenizer.py is the general encode/decode driver, usable standalone or as the codec front/back-end of any VibeVoice ONNX pipeline.

file in โ†’ out notes
acoustic_encoder.onnx audio [1,1,24000] โ†’ latents [1,7,64] fixed 1 s input (24000 samples โ†’ 7 frames)
acoustic_decoder.onnx latents [B,frames,64] โ†’ audio [B,1,3200*frames] dynamic frame count
inference_tokenizer.py โ€” encode/decode driver (prints the encoderโ†’latentsโ†’decoder flow)

Two precisions: fp32/ (1.3 GB, reference) and fp16/ (โ‰ˆ0.7 GB). int4 is not provided โ€” the tokenizer is a conv VAE with no MatMul weights to quantize (int4 would be a no-op = fp32).

Encoder length is baked at 24000 samples. The PyTorch model accepts arbitrary length; this ONNX encoder processes exactly 1 s per call, so longer audio is encoded in 1 s chunks (zero-padded to a whole number of chunks) and the latents concatenated. The decoder is dynamic โ€” it decodes any number of frames in one pass. acoustic_inference.py does the chunking for you.

Setup

pip install onnxruntime numpy soundfile librosa
# GPU: pip install onnxruntime-gpu   (and pass --cuda)

Encoding and decoding (ONNX)

The ONNX equivalent of the model card's model.encode(...) / model.decode(...):

import numpy as np, onnxruntime as ort, soundfile as sf, librosa

SR, CHUNK = 24_000, 24_000                       # 24 kHz; encoder input is fixed at 1 s

def sess(p):
    so = ort.SessionOptions(); so.log_severity_level = 3
    return ort.InferenceSession(p, so, providers=["CPUExecutionProvider"])

enc, dec = sess("fp32/acoustic_encoder.onnx"), sess("fp32/acoustic_decoder.onnx")
npdt = np.float16 if "float16" in enc.get_inputs()[0].type else np.float32

# load + resample to 24 kHz, mono
wav, sr = sf.read("en-Alice_woman.wav", dtype="float32", always_2d=False)
if wav.ndim > 1: wav = wav.mean(1)
if sr != SR: wav = librosa.resample(wav, orig_sr=sr, target_sr=SR)

# ENCODE โ€” 1 s chunks (encoder is fixed at 24000 samples) -> concat latents
n = int(np.ceil(len(wav) / CHUNK))
wav = np.pad(wav, (0, n * CHUNK - len(wav)))
latents = np.concatenate(
    [enc.run(["latents"], {"audio": wav[i*CHUNK:(i+1)*CHUNK][None, None, :].astype(npdt)})[0]
     for i in range(n)], axis=1)                 # [1, 7*n, 64]
print("Latent shape:", latents.shape)

# DECODE โ€” dynamic, one pass -> 3200 samples per frame
audio = dec.run(["audio"], {"latents": latents.astype(npdt)})[0].ravel()
sf.write("reconstructed.wav", audio, SR, subtype="PCM_16")
print("Reconstructed:", audio.shape)

Or just use the driver (prints the encoder โ†’ latents โ†’ decoder flow):

python inference_tokenizer.py --models-dir fp32 --input en-Alice_woman.wav --output reconstructed.wav
python inference_tokenizer.py --models-dir fp32 --input in.wav  --latents-out codes.npy --encode-only
python inference_tokenizer.py --models-dir fp32 --latents-in codes.npy --output out.wav --decode-only
python inference_tokenizer.py --models-dir fp16 --input in.wav --output out.wav        # fp16

Example flow output:

[encode] 24000 samples (1.00s) @ 24000 Hz  ->  1 x 24000 chunk(s), fp32
  encoder: audio[1,1,24000]  ->  latents[1, 7, 64]   (chunk 1/1)
[decode] decoder: latents[1,7,64]  ->  audio[1,1,22400]  (0.93s, 3200/frame)
saved -> reconstructed.wav   round-trip corr=+0.999

Parity vs PyTorch (verified)

Component parity (ONNX vs VibeVoiceAcousticTokenizerModel) and codec round-trip on a synthetic tone:

encoder decoder codec round-trip
fp32 cos 0.999 cos 0.94ยน corr +0.999, SNR +22.8 dB
fp16 cos 0.999 cos 0.94ยน corr +0.998, SNR +22.7 dB

ยน the decoder's cosine is measured on near-silent output (random latents โ‰ˆ silence), where max|ฮ”| โ‰ˆ 1e-10 โ€” numerically identical, cosine is just noise-dominated (standard neural-codec eval caveat).

Notes

  • fp16 was exported with an op_block_list (ConstantOfShape/ConvTranspose/Resize/Range) kept in fp32 โ€” those shape/resample ops produce invalid graphs under fp16 conversion and gain nothing.
  • Streaming (use_cache / padding_cache in the PyTorch API) is not exported here โ€” these are fixed-context encode/decode graphs. Streaming would need the cached-state variants exported separately.
  • Multi-chunk round-trip: a 1 s window reconstructs at corr ~0.999, but the fixed encoder emits 22400 samples per 24000-sample chunk (7 frames ร— 3200), so naively encoding >1 s in chunks and decoding the concatenated latents accumulates a small per-chunk time offset that lowers waveform corr. For downstream use the latents are the product (fed to / produced by the LLM); exact full-length reconstruction of long audio wants the streaming (padding_cache) path.
  • The upstream AutoFeatureExtractor just formats a mono 24 kHz waveform (pad_to_multiple_of=3200); the ONNX encoder takes the raw [1,1,24000] tensor directly, so no feature-extractor is required.
Downloads last month

-

Downloads are not tracked for this model. How to track
Inference Providers NEW
This model isn't deployed by any Inference Provider. ๐Ÿ™‹ Ask for provider support

Model tree for Prince-1/VibeVoice-AcousticTokenizer-Onnx

Quantized
(1)
this model