Views
No views yet

[cough], [laugh], [chuckle], and more to add distinct realism. While Turbo was built primarily for low-latency voice agents, it excels at narration and creative workflows.
| Model | Size | Languages | Key Features | Best For | 🤗 | Examples |
|---|---|---|---|---|---|---|
| Chatterbox-Turbo | 350M | English | Paralinguistic Tags ([laugh]), Lower Compute and VRAM | Zero-shot voice agents, Production | Demo | Listen |
| Chatterbox-Multilingual (Language list) | 500M | 23+ | Zero-shot cloning, Multiple Languages | Global applications, Localization | Demo | Listen |
| Chatterbox (Tips and Tricks) | 500M | English | CFG & Exaggeration tuning | General zero-shot TTS with creative controls | Demo | Listen |
1import onnxruntime
2from transformers import AutoTokenizer
3from huggingface_hub import hf_hub_download
4import numpy as np
5from tqdm import trange
6import librosa
7import soundfile as sf
8
9MODEL_ID = "ResembleAI/chatterbox-turbo-ONNX"
10SAMPLE_RATE = 24000
11START_SPEECH_TOKEN = 6561
12STOP_SPEECH_TOKEN = 6562
13SILENCE_TOKEN = 4299
14NUM_KV_HEADS = 16
15HEAD_DIM = 64
16
17class RepetitionPenaltyLogitsProcessor:
18 def __init__(self, penalty: float):
19 if not isinstance(penalty, float) or not (penalty > 0):
20 raise ValueError(f"`penalty` must be a strictly positive float, but is {penalty}")
21 self.penalty = penalty
22
23 def __call__(self, input_ids: np.ndarray, scores: np.ndarray) -> np.ndarray:
24 score = np.take_along_axis(scores, input_ids, axis=1)
25 score = np.where(score < 0, score * self.penalty, score / self.penalty)
26 scores_processed = scores.copy()
27 np.put_along_axis(scores_processed, input_ids, score, axis=1)
28 return scores_processed
29
30def download_model(name: str, dtype: str = "fp32") -> str:
31 filename = f"{name}{'' if dtype == 'fp32' else '_quantized' if dtype == 'q8' else f'_{dtype}'}.onnx"
32 graph = hf_hub_download(MODEL_ID, subfolder="onnx", filename=filename) # Download graph
33 hf_hub_download(MODEL_ID, subfolder="onnx", filename=f"{filename}_data") # Download weights
34 return graph
35
36# Download models
37## dtype options: fp32, fp16, q8, q4, q4f16
38conditional_decoder_path = download_model("conditional_decoder", dtype="fp32")
39speech_encoder_path = download_model("speech_encoder", dtype="fp32")
40embed_tokens_path = download_model("embed_tokens", dtype="fp32")
41language_model_path = download_model("language_model", dtype="fp32")
42
43# Create ONNX sessions
44speech_encoder_session = onnxruntime.InferenceSession(speech_encoder_path)
45embed_tokens_session = onnxruntime.InferenceSession(embed_tokens_path)
46language_model_session = onnxruntime.InferenceSession(language_model_path)
47cond_decoder_session = onnxruntime.InferenceSession(conditional_decoder_path)
48
49# Generation parameters
50text = "Oh, that's hilarious! [chuckle] Um anyway, how are you doing today?"
51target_voice_path = "path/to/voice.wav"
52output_file_name = "output.wav"
53max_new_tokens = 1024
54repetition_penalty = 1.2
55apply_watermark = False
56
57# Prepare audio input
58audio_values, _ = librosa.load(target_voice_path, sr=SAMPLE_RATE)
59audio_values = audio_values[np.newaxis, :].astype(np.float32)
60
61# Prepare text input
62tokenizer = AutoTokenizer.from_pretrained(MODEL_ID)
63input_ids = tokenizer(text, return_tensors="np")["input_ids"].astype(np.int64)
64
65# Generation loop
66repetition_penalty_processor = RepetitionPenaltyLogitsProcessor(penalty=repetition_penalty)
67generate_tokens = np.array([[START_SPEECH_TOKEN]], dtype=np.int64)
68for i in trange(max_new_tokens, desc="Sampling", dynamic_ncols=True):
69 inputs_embeds = embed_tokens_session.run(None, {"input_ids": input_ids})[0]
70
71 if i == 0:
72 ort_speech_encoder_input = {"audio_values": audio_values}
73 cond_emb, prompt_token, speaker_embeddings, speaker_features = speech_encoder_session.run(None, ort_speech_encoder_input)
74 inputs_embeds = np.concatenate((cond_emb, inputs_embeds), axis=1)
75
76 # Initialize cache and LLM inputs
77 batch_size, seq_len, _ = inputs_embeds.shape
78 past_key_values = {
79 i.name: np.zeros([batch_size, NUM_KV_HEADS, 0, HEAD_DIM], dtype=np.float16 if i.type == 'tensor(float16)' else np.float32)
80 for i in language_model_session.get_inputs()
81 if "past_key_values" in i.name
82 }
83 attention_mask = np.ones((batch_size, seq_len), dtype=np.int64)
84 position_ids = np.arange(seq_len, dtype=np.int64).reshape(1, -1).repeat(batch_size, axis=0)
85
86 logits, *present_key_values = language_model_session.run(None, dict(
87 inputs_embeds=inputs_embeds,
88 attention_mask=attention_mask,
89 position_ids=position_ids,
90 **past_key_values,
91 ))
92
93 logits = logits[:, -1, :]
94 next_token_logits = repetition_penalty_processor(generate_tokens, logits)
95
96 input_ids = np.argmax(next_token_logits, axis=-1, keepdims=True).astype(np.int64)
97 generate_tokens = np.concatenate((generate_tokens, input_ids), axis=-1)
98 if (input_ids.flatten() == STOP_SPEECH_TOKEN).all():
99 break
100
101 # Update values for next generation loop
102 attention_mask = np.concatenate([attention_mask, np.ones((batch_size, 1), dtype=np.int64)], axis=1)
103 position_ids = position_ids[:, -1:] + 1
104 for j, key in enumerate(past_key_values):
105 past_key_values[key] = present_key_values[j]
106
107# Decode audio
108speech_tokens = generate_tokens[:, 1:-1]
109silence_tokens = np.full((speech_tokens.shape[0], 3), SILENCE_TOKEN, dtype=np.int64) # Add silence at the end
110speech_tokens = np.concatenate([prompt_token, speech_tokens, silence_tokens], axis=1)
111
112wav = cond_decoder_session.run(None, dict(
113 speech_tokens=speech_tokens,
114 speaker_embeddings=speaker_embeddings,
115 speaker_features=speaker_features,
116))[0].squeeze(axis=0)
117
118# Optional: Apply watermark
119if apply_watermark:
120 import perth
121 watermarker = perth.PerthImplicitWatermarker()
122 wav = watermarker.apply_watermark(wav, sample_rate=SAMPLE_RATE)
123
124sf.write(output_file_name, wav, SAMPLE_RATE)1import perth
2import librosa
3
4AUDIO_PATH = "YOUR_FILE.wav"
5
6# Load the watermarked audio
7watermarked_audio, sr = librosa.load(AUDIO_PATH, sr=None)
8
9# Initialize watermarker (same as used for embedding)
10watermarker = perth.PerthImplicitWatermarker()
11
12# Extract watermark
13watermark = watermarker.get_watermark(watermarked_audio, sample_rate=sr)
14print(f"Extracted watermark: {watermark}")
15# Output: 0.0 (no watermark) or 1.0 (watermarked)@misc{chatterboxtts2025,
author = {{Resemble AI}},
title = {{Chatterbox-TTS}},
year = {2025},
howpublished = {\url{https://github.com/resemble-ai/chatterbox}},
note = {GitHub repository}
}