rlx-orpheus
Orpheus TTS on RLX — Canopy Orpheus speech LM (Llama-3B GGUF) + SNAC 24 kHz neural codec decoder.
Quick start
Architecture
| Stage | Default (--device metal) |
--device coreml |
|---|---|---|
| Llama-3B LM | Metal GGUF prefill + KV decode | Metal GGUF (same) |
| SNAC quantizer | CPU eager (safetensors) | CPU eager |
| SNAC conv decoder | CPU eager (ndarray) | Native RLX Device::Ane via rlx-ir |
CoreML is native RLX (no ONNX). The RVQ lookup stays on the host; only the conv decoder stack is compiled per latent-length bucket and cached (ORPHEUS_SNAC_AOT).
Cargo features
| Feature | Enables |
|---|---|
llama (default) |
GGUF backbone + CLI |
codec (default) |
SNAC decoder (eager) |
coreml |
SNAC on Device::Ane (macOS; pulls rlx-ir, rlx-runtime/coreml) |
metal / mlx / cuda / … |
Forwarded to rlx-llama32 for LM backends |
apple-silicon |
metal + mlx + gpu + coreml |
hf-download |
--download flags via hf-hub |
Build examples:
Audio samples
Bundled MP4 clips (mono AAC, 24 kHz) under assets/:
| File | Description |
|---|---|
sample_hi_eager.mp4 |
Golden SNAC codes → host eager decoder (~0.34 s) |
sample_hi_coreml.mp4 |
Same codes → native RLX CoreML SNAC |
sample_hello_rlx.mp4 |
“Hello from RLX.” |
sample_longer.mp4 |
“Hello from RLX. This is a longer test.” |
Regenerate:
# optional: refresh sentence MP4s from new synthesis WAVs
Requires ffmpeg on PATH. Hi clips only need exported SNAC weights; sentence clips need WAVs from just orpheus-demo (or any synthesis run).
Weights
| Component | Source |
|---|---|
| LM backbone | unsloth/orpheus-3b-0.1-ft-GGUF (Q4_K_M recommended) |
| SNAC decoder | hubertsiuzdak/snac_24khz → scripts/export_snac_decoder.py |
CLI download (hf-download feature): --download, --download-orpheus [--quant Q4_K_M], --download-snac.
Manual SNAC export:
&&
CLI
--device |
LM | SNAC |
|---|---|---|
auto |
Best GPU (Metal on Apple Silicon, else CUDA/ROCm/wgpu/Vulkan) | Eager CPU |
metal / cuda / rocm / gpu / wgpu / vulkan |
Selected RLX backend (KV decode on GPU) | Eager CPU |
coreml |
Metal (or best Apple GPU) | Native RLX Device::Ane |
CoreML needs --features coreml at build time:
Built-in voices: tara, leah, jess, leo, dan, mia, zac, zoe.
Sampling defaults: temperature 0.6, top-p 0.8, repetition penalty 1.3, stop token 49158. Override via --temperature, --top-p, --greedy, etc.
Voice cloning (pretrained checkpoint)
Finetune-prod GGUF (just fetch-orpheus) supports named voices only. Zero-shot clone needs a pretrained GGUF plus a reference JSON from SNAC encoding.
Convert canopylabs/orpheus-3b-0.1-pretrained to GGUF before use. Emotive tags: <laugh>, <chuckle>, <sigh>, … (upstream Orpheus docs).
Library
use ;
let runtime = resolve_orpheus_device?;
let backbone_opts = for_device;
let mut tts = load_on_with?;
tts.config = GenerationConfig ;
let out = tts.synthesize?;
// out.samples — mono f32 PCM at 24 kHz
SNAC-only decode (no LM):
use ;
let snac = open?;
let pcm = decode_orpheus_codes?;
// CoreML SNAC path (feature `coreml`, macOS):
let snac = open?;
Voice clone API: [VoiceCloneReference::load_json] + [OrpheusTts::synthesize_voice_clone].
Streaming: [OrpheusTts::synthesize_stream] — ~2048-sample PCM chunks after 4 SNAC frames; tail appended from full decode.
Just recipes
| Recipe | Purpose |
|---|---|
just fetch-orpheus |
Download finetune GGUF (Q4_K_M) |
just fetch-orpheus-snac |
Download SNAC PyTorch weights |
just export-orpheus-snac |
Export safetensors from SNAC checkpoint |
just export-orpheus-audio-assets |
Regenerate bundled MP4 samples |
just orpheus -- … |
Run CLI (pass flags after --) |
just orpheus-demo |
Short Metal synthesis demo |
just orpheus-wgpu-demo |
wgpu LM synthesis (--features gpu) |
just orpheus-coreml-demo |
CoreML SNAC demo |
just orpheus-demos |
Batch demo WAVs |
just orpheus-encode-ref WAV TEXT |
Reference clip → clone JSON |
just orpheus-voice-clone |
Voice-clone example |
just test-orpheus-whisper |
SNAC golden → Whisper (fast) |
just test-orpheus-whisper-e2e |
Full LM → Whisper (#[ignore]) |
just test-orpheus-backends-whisper |
Per-backend + Whisper |
just bench-orpheus / bench-orpheus-all-devices |
Timing + optional Whisper |
Tests
# SNAC decoder vs Python reference (needs ref_noise_*.npy under ORPHEUS_SNAC_REF_DIR)
ORPHEUS_SNAC_PATH=/tmp/rlx-weights/snac/snac_24khz_decoder.safetensors \
ORPHEUS_SNAC_REF_DIR=/tmp/rlx-weights/snac \
# Native CoreML SNAC vs eager (macOS, feature coreml)
ORPHEUS_SNAC_PATH=/tmp/rlx-weights/snac/snac_24khz_decoder.safetensors \
ORPHEUS_SNAC_REF_DIR=/tmp/rlx-weights/snac \
# Per-backend LM + Whisper (includes wgpu when built with --features gpu)
Environment
| Variable | Role |
|---|---|
ORPHEUS_SNAC_PATH |
SNAC snac_24khz_decoder.safetensors (required) |
ORPHEUS_GGUF_PATH |
Default GGUF for tests / examples |
ORPHEUS_SNAC_REF_DIR |
Python parity fixtures (ref_codes.json, ref_noise_*.npy) |
ORPHEUS_SNAC_AOT |
AOT compile cache dir for CoreML SNAC graphs |
ORPHEUS_SNAC_COREML=1 |
decode_fixture example: Ane SNAC path |
ORPHEUS_LOW_MEM=1 |
CPU F32 LM prefill + decode (lower RAM) |
ORPHEUS_METAL_PREFILL |
metal | packed | cpu — Metal GGUF prefill mode |
ORPHEUS_COMPILE_SEQ_CAP |
Compile bucket cap (default tuned for synthesis) |
ORPHEUS_BUCKET_DECODE=1 |
Faster multi-step LM decode when RAM allows |
ORPHEUS_MLX_KV=1 |
Opt-in MLX LM KV decode |
ORPHEUS_MASK_LOGITS |
Restrict LM logits to SNAC slot (1 force on, 0 off; default on for wgpu/Vulkan native GPU decode only) |
ORPHEUS_WHISPER_E2E=1 |
Enable full LM e2e Whisper test |
Notes
- Memory: default [
BackboneLoadOptions::for_device] uses GPU prefill on Metal / CUDA / ROCm without eager F32 drain. wgpu/Vulkan use CPU GGUF parity. AvoidORPHEUS_PACKED_LM=1(O(n²) recompute).ORPHEUS_HIGH_MEM=1raises compile cap to 256. - wgpu / Vulkan:
--device gpuselects the portable GPU backend; the 3B GGUF LM runs CPU prefill + CPU decode (GGUF parity, same sampling as Metal). SNAC stays CPU eager. Native wgpu LM decode is pending upstream KV parity (ORPHEUS_MASK_LOGITS=1only needed for that path). - MLX: not selected by
--device auto; setORPHEUS_MLX_KV=1to enable KV decode on MLX. - Batch decode: [
decode_orpheus_codes] returns the full SNAC waveform; streaming uses 4-frame windows (2048-sample centers) and appends the remaining tail automatically. - Parity: SNAC
z_q+ PCM vs Python within ~1e-3 when noise fixtures exist; CoreML SNAC vs eager within ~0.08 max abs diff (fp16 on ANE).