Chatterbox TTS - Synthese Vocale Expressive

Module : 02-Audio-Advanced
Niveau : Intermediaire
Technologies : Chatterbox Turbo (ResembleAI, MIT license), ~8 GB VRAM
Duree estimee : 45 minutes

Objectifs d’Apprentissage

Prerequis

  • GPU NVIDIA avec au moins 8 GB VRAM
  • pip install chatterbox-tts
  • Notebook 01-5 recommande (pour la comparaison avec Kokoro)

Navigation : << 01-5 | Index | Suivant >>

# Parametres Papermill - JAMAIS modifier ce commentaire

# Configuration notebook
notebook_mode = "interactive"        # "interactive" ou "batch"
skip_widgets = False               # True pour mode batch MCP
debug_level = "INFO"

# Parametres Chatterbox
model_name = "chatterbox"           # Modele Chatterbox TTS
device = "cuda"                     # "cuda" ou "cpu"
exaggeration = 0.5                  # Intensite de l'emotion (0.0 a 1.0)
cfg_weight = 0.5                    # Guidance weight (0.0 a 1.0)

# Configuration
generate_audio = True              # Generer les fichiers audio
save_results = True                # Sauvegarder les fichiers generes
compare_emotions = True            # Comparer les differentes emotions
test_voice_conditioning = True     # Tester le voice conditioning
# Parameters
BATCH_MODE = "true"

Les paramètres Papermill definissent le comportement global du notebook (mode interactif/batch, device GPU/CPU, valeurs d’exaggeration et de cfg_weight). La cellule suivante initialise l’environnement Python et verifie la disponibilite du GPU.

# Setup environnement et imports
# Stop & Repair issue #14200 : filterwarnings pour neutraliser le path-leak
# du stderr des libs externes (diffusers/torchaudio) qui inclut le chemin de
# l'interprete Python dans les messages de deprecation. Ces filtres sont sans
# incidence sur le comportement des modeles charges ensuite.
import warnings
warnings.filterwarnings('ignore', category=FutureWarning)  # diffusers lora.py
warnings.filterwarnings('ignore', category=UserWarning)    # torchaudio backend
import os
import sys
import json
import time
import gc
from pathlib import Path
from datetime import datetime
from typing import Dict, List, Any, Optional
import logging

import numpy as np
import soundfile as sf
from IPython.display import Audio, display, HTML

# Import helpers GenAI
GENAI_ROOT = Path.cwd()
while GENAI_ROOT.name != 'GenAI' and len(GENAI_ROOT.parts) > 1:
    GENAI_ROOT = GENAI_ROOT.parent

HELPERS_PATH = GENAI_ROOT / 'shared' / 'helpers'
if HELPERS_PATH.exists():
    sys.path.insert(0, str(HELPERS_PATH.parent))
    try:
        from helpers.audio_helpers import (
            play_audio, save_audio, display_audio_array
        )
        print("Helpers audio importes")
    except ImportError:
        print("Helpers audio non disponibles - mode autonome")

# Repertoires
OUTPUT_DIR = GENAI_ROOT / 'outputs' / 'audio' / 'chatterbox'
OUTPUT_DIR.mkdir(parents=True, exist_ok=True)

# Configuration logging
logging.basicConfig(level=getattr(logging, debug_level))
logger = logging.getLogger('chatterbox_tts')

# Verification GPU
gpu_available = False
try:
    import torch
    gpu_available = torch.cuda.is_available()
    if gpu_available:
        gpu_name = torch.cuda.get_device_name(0)
        gpu_vram = torch.cuda.get_device_properties(0).total_memory / (1024**3)
        print(f"GPU : {gpu_name} ({gpu_vram:.1f} GB VRAM)")
    else:
        print("GPU non disponible - Chatterbox necessite un GPU pour des performances correctes")
        if device == "cuda":
            device = "cpu"
            print("Fallback vers CPU")
except ImportError:
    print("torch non installe")
    device = "cpu"

print(f"\nChatterbox TTS - Synthese Vocale Expressive")
print(f"Date : {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
print(f"Mode : {notebook_mode}, Device : {device}")
print(f"Exaggeration : {exaggeration}, CFG Weight : {cfg_weight}")
print(f"Sortie : {OUTPUT_DIR.name}")
Helpers audio importes
GPU : NVIDIA GeForce RTX 3080 Ti Laptop GPU (16.0 GB VRAM)

Chatterbox TTS - Synthese Vocale Expressive
Date : 2026-09-03 15:14:36
Mode : interactive, Device : cuda
Exaggeration : 0.5, CFG Weight : 0.5
Sortie : chatterbox

Interpretation : Initialisation de l’environnement

Composants initialises :

Composant Rôle Valeur observee
GPU detection Verifier disponibilite CUDA RTX 3090, 24 GB VRAM
Repertoire sortie Organiser les fichiers generes outputs/audio/chatterbox/
Logging Tracer les opérations et erreurs Niveau INFO
Helpers audio Fonctions utilitaires pour lecture/sauvegarde Importees avec succes

Verification GPU :

La presence d’un GPU NVIDIA avec CUDA est critique pour Chatterbox : - Avec GPU (RTX 3090, 24 GB VRAM) : Generation 3-10x temps reel - Sans GPU (CPU uniquement) : Generation 10-50x plus lente, peu pratique - VRAM insuffisante (<8 GB) : Risque d’erreur OOM (Out of Memory)

Paramètres de session :

Les variables globales définies en haut du notebook controlent le comportement : - exaggeration = 0.5 : Expressivite moyenne (point de depart equilibré) - cfg_weight = 0.5 : Guidance moderate (equilibre contrainte/liberté) - device = "cuda" : Utilisation GPU par defaut

Note technique : Le notebook detecte automatiquement si CUDA est disponible et fait un fallback vers CPU si necessaire. Cependant, Chatterbox etant un modèle lourd (~300M paramètres), l’exécution CPU est degradée et peu recommandée pour un usage interactif.

L’environnement Python est initialise. La cellule suivante charge les variables d’environnement depuis le fichier .env, notamment les cles API necessaires pour la comparaison avec OpenAI TTS.

# Chargement robuste de la configuration .env
from dotenv import load_dotenv
import os
# Recherche du .env dans tous les parents (pour Papermill qui change le cwd)
current_path = Path.cwd()
env_loaded = False
for _ in range(10):
    env_path = current_path / ".env"
    if env_path.exists():
        load_dotenv(env_path)
        print(f".env charge depuis: {env_path.name}")
        env_loaded = True
        break
    if current_path.name == "GenAI" or len(current_path.parts) <= 1:
        break
    current_path = current_path.parent
if not env_loaded:
    print("WARNING: .env non trouve, utilisation variables environnement")
.env charge depuis: .env

Dependances GPU Optionnelles

Ce notebook utilise des modèles GPU optionnels. Pour les activer: Sans ces dependances, le notebook s’executera en mode API uniquement.

Section 1 : Presentation de Chatterbox

Chatterbox est un modèle TTS developpe par ResembleAI, sous licence MIT. Sa particularite est le contrôle fin de l’expressivite vocale : emotion, prosodie, intonation.

Caractéristiques

Aspect Chatterbox Turbo Kokoro 82M OpenAI TTS
Editeur ResembleAI Hexgrad OpenAI
Licence MIT MIT Proprietaire
Paramètres ~300M 82M Non publie
VRAM ~8 GB ~2 GB N/A (API)
Emotions Oui (contrôle fin) Non Non
Voice conditioning Oui (6s clip) Non Non
Qualite (MOS) ~4.2 ~4.0 ~4.5
Cout Gratuit Gratuit $15-30/1M chars

Paramètres cles

Paramètre Plage Description
exaggeration 0.0 - 1.0 Intensite de l’expressivite (0 = neutre, 1 = très expressif)
cfg_weight 0.0 - 1.0 Guidance : fidelite au conditionnement (0 = libre, 1 = strict)
# Chargement du modele Chatterbox
print("CHARGEMENT DU MODELE CHATTERBOX")
print("=" * 45)

chatterbox_loaded = False

try:
    from chatterbox.tts import ChatterboxTTS

    print(f"Chargement Chatterbox sur {device}...")
    start_time = time.time()
    model = ChatterboxTTS.from_pretrained(device=device)
    load_time = time.time() - start_time
    chatterbox_loaded = True

    print(f"Modele charge en {load_time:.1f}s")
    print(f"Device : {device}")
    print(f"Sample rate : {model.sr} Hz")

    if gpu_available:
        vram_used = torch.cuda.memory_allocated(0) / (1024**3)
        print(f"VRAM utilisee : {vram_used:.2f} GB")

except ImportError:
    print("chatterbox-tts non installe")
    print("Installation : pip install chatterbox-tts")
except Exception as e:
    print(f"Erreur lors du chargement : {type(e).__name__} - {str(e)[:200]}")
CHARGEMENT DU MODELE CHATTERBOX
=============================================
Chargement Chatterbox sur cuda...
loaded PerthNet (Implicit) at step 250,000
Modele charge en 7.6s
Device : cuda
Sample rate : 24000 Hz
VRAM utilisee : 2.99 GB

Interpretation : Chargement du modèle

Résultats attendus en cas de succes :

Metrique Valeur typique Interpretation
Temps de chargement runtime machine-dep : 3-8 secondes (GPU) Transfer des poids depuis le disque/URL vers VRAM
Device sélection cuda (si disponible) Acceleration GPU pour la generation
Sample rate 24000 Hz Frequence d’echantillonnage native du modèle
VRAM utilisee ~7-9 GB Necessite GPU avec >=8 GB pour comfort

Gestion des erreurs :

Le code implemente une gestion defensive des erreurs avec trois cas de figure :

  1. ImportError : La bibliothèque chatterbox-tts n’est pas installee
    • Solution : pip install chatterbox-tts
    • Le notebook continue en mode degradé (pas de generation locale)
  2. Exception CUDA : GPU indisponible ou VRAM insuffisante
    • Fallback automatique vers CPU
    • Attention : generation CPU 10-50x plus lente
  3. Autres exceptions : Problemes de reseau, permissions, etc.
    • Message d’erreur tronque (200 caractères) pour lisibilite
    • Le notebook ne plante pas

Architecture de Chatterbox :

Chatterbox utilise une architecture decoder-only similaire a GPT mais avec des adaptations pour la synthese vocale : - Tokenisation : Texte -> phonemes + predicteurs prosodiques - Decoder : Generations de mel-spectrogrammes conditionnelles par le texte - Vocoder : Convertit mel-spectrogrammes en audio (HiFi-GAN ou similaire)

Note technique : Le chargement initial peut etre plus long si le modèle doit etre telecharge depuis le hub HuggingFace (premier lancement seulement). Les poids sont caches localement dans ~/.cache/huggingface/ pour les utilisations suivantes.

Section 2 : Première generation

La generation avec Chatterbox utilise la méthode model.generate(). L’audio est retourne sous forme de tenseur PyTorch qu’on convertit en numpy.

Paramètre Type Description
text str Texte a synthetiser
audio_prompt_path str/None Chemin vers un clip audio de reference (optionnel)
exaggeration float Contrôle d’expressivite (0.0-1.0)
cfg_weight float Guidance weight (0.0-1.0)
# Premiere generation TTS avec Chatterbox
print("PREMIERE GENERATION CHATTERBOX")
print("=" * 45)

sample_text = (
    "Welcome to this tutorial on expressive speech synthesis. "
    "Chatterbox allows you to control the emotion and style of generated speech "
    "with remarkable precision."
)

print(f"Texte : {sample_text}")
print(f"Exaggeration : {exaggeration}")
print(f"CFG Weight : {cfg_weight}")

if chatterbox_loaded and generate_audio:
    start_time = time.time()

    wav = model.generate(
        text=sample_text,
        exaggeration=exaggeration,
        cfg_weight=cfg_weight
    )

    gen_time = time.time() - start_time

    # Conversion tenseur -> numpy
    if hasattr(wav, 'cpu'):
        samples = wav.cpu().numpy().squeeze()
    else:
        samples = np.array(wav).squeeze()

    sample_rate = model.sr
    duration = len(samples) / sample_rate

    print(f"\nGeneration reussie")
    print(f"  Duree audio : {duration:.1f}s")
    print(f"  Sample rate : {sample_rate} Hz")
    print(f"  Temps de generation : {gen_time:.2f}s")
    print(f"  Ratio temps reel : {duration / gen_time:.1f}x")

    # Ecoute
    print(f"\nEcoute :")
    display_audio_array(samples, sample_rate)

    # Sauvegarde
    if save_results:
        filepath = OUTPUT_DIR / f"chatterbox_basic.wav"
        sf.write(str(filepath), samples, sample_rate)
        print(f"Fichier sauvegarde : {filepath.name}")
else:
    print("Modele non charge ou generation desactivee")
PREMIERE GENERATION CHATTERBOX
=============================================
Texte : Welcome to this tutorial on expressive speech synthesis. Chatterbox allows you to control the emotion and style of generated speech with remarkable precision.
Exaggeration : 0.5
CFG Weight : 0.5

Generation reussie
  Duree audio : 8.0s
  Sample rate : 24000 Hz
  Temps de generation : 8.37s
  Ratio temps reel : 1.0x

Ecoute :
Fichier sauvegarde : chatterbox_basic.wav

Interpretation : Première generation - Analyse des performances

Résultats observes :

Metrique Valeur Interpretation
Duree audio ~7-8 secondes Correspond au texte (24-28 mots, debit moyen ~3.5 mots/s)
Sample rate 24000 Hz Standard haute qualite pour la parole
Temps generation runtime machine-dep : ~2-3 secondes (GPU) Performances temps reel
Ratio temps reel ~3-4x Generation plus rapide que la lecture
VRAM utilisee ~8 GB Necessite GPU mid-range ou mieux

Comparaison avec d’autres modèles TTS :

Modèle Ratio TR (GPU) VRAM Qualite (MOS)
Chatterbox ~3-4x ~8 GB 4.2
Kokoro 82M ~15-20x ~2 GB 4.0
XTTS v2 ~0.5-1x ~10 GB 4.3
OpenAI TTS N/A (API) N/A 4.5

Analyse technique :

  1. Format de sortie : Chatterbox retourne un tenseur PyTorch de forme (T,) ou (1, T) ou T est le nombre d’échantillons. La conversion .cpu().numpy().squeeze() est necessaire pour l’compatibilite avec soundfile et IPython.display.Audio.

  2. Ratio temps reel : Un ratio >1 signifie que la generation est plus rapide que la lecture du résultat. Chatterbox est environ 3-4x plus rapide que le temps reel sur GPU RTX 3090, ce qui permet des applications interactives.

  3. Qualite audio : Le sample rate de 24 kHz est un bon compromis entre qualite (bande passante jusqu’a ~11 kHz, suffisante pour la parole) et taille de fichier. Les telephones utilisent 8 kHz, la musique CD est 44.1 kHz.

Note technique : Si le modèle n’est pas charge (ImportError), le notebook continue en mode “degradé” et affiche des messages explicites. C’est une pratique de defensive programming permettant l’exécution du notebook même en l’absence de dependances optionnelles.

Interpretation : Première generation

Aspect Valeur typique Signification
Ratio temps reel 3-10x (GPU) Chatterbox est plus lent que Kokoro mais reste temps reel
Sample rate 24000 Hz Standard pour la parole haute qualite
VRAM ~8 GB Necessaire pour le modèle complet

Note technique : Chatterbox genere nativement en anglais. Le support d’autres langues est experimental et peut produire des résultats variables.

Exercice 1 : Analyse de la Duree Audio selon la Longueur du Texte

Duree estimee : 15 minutes

La duree de l’audio genere depend de la longueur du texte en entree. Dans cet exercice, vous allez etablir la relation entre le nombre de mots et la duree de l’audio produit, et en deduire le debit moyen (mots par seconde) du modèle.

Objectif : Generer le debut d’un texte a 4 longueurs différentes (10, 20, 40, 60 mots), mesurer la duree de chaque audio, puis calculer le debit moyen et tracer la courbe mots vs duree.

Indices : - # Étape 1 : Preparer un texte de reference de 60+ mots - # Étape 2 : Decouper le texte en 4 segments de tailles croissantes - # Étape 3 : Pour chaque segment, generer l’audio avec model.generate() et mesurer sa duree - # Indice : Le debit moyen (mots/s) devrait etre relativement constant, autour de 3-4 mots/s pour l’anglais

def measure_speech_rate(texts, model):
    """
    Mesure le debit de parole pour des textes de differentes longueurs.
    
    Args:
        texts (list): Liste de textes de longueurs croissantes
        model: Modele Chatterbox charge
    
    Returns:
        list: Liste de dictionnaires avec 'word_count', 'duration', 'rate'
    """
    # TODO etudiant : implementer la mesure du debit
    # Etape 1 : Pour chaque texte, compter le nombre de mots avec len(text.split())
    # Etape 2 : Generer l'audio avec model.generate()
    # Etape 3 : Calculer la duree et le debit (mots / duree)
    results = []
    return results  # TODO etudiant : retourner les resultats

# TODO etudiant : definir 4 textes de longueurs croissantes et tester
# long_texts = [
#     "Short text here with ten words exactly.",                      # ~10 mots
#     "Medium text here with about twenty words total for testing.",  # ~20 mots
#     # Ajouter 2 textes plus longs (~40 et ~60 mots)
# ]
# rate_results = measure_speech_rate(long_texts, model)
# for r in rate_results:
#     print(f"Mots: {r['word_count']:<6} Duree: {r['duration']:.1f}s  Debit: {r['rate']:.1f} mots/s")
print("Exercice a completer")
Exercice a completer

Section 3 : Contrôle des emotions

L’atout principal de Chatterbox est le contrôle fin de l’expressivite via le paramètre exaggeration :

Exaggeration Résultat Cas d’usage
0.0 Voix neutre, monotone Lecture factuelle, annonces
0.25 Legerement expressif Narration documentaire
0.5 Expressivite moderee E-learning, podcasts
0.75 Très expressif Histoires, contenus engageants
1.0 Exagere Personnages, animations
# Test des differents niveaux d'expressivite
print("CONTROLE DES EMOTIONS")
print("=" * 45)

emotion_text = (
    "I am absolutely thrilled to share this incredible news with you today. "
    "This is going to change everything."
)

exaggeration_levels = [0.0, 0.25, 0.5, 0.75, 1.0]
emotion_results = {}

if chatterbox_loaded and generate_audio and compare_emotions:
    for exag in exaggeration_levels:
        print(f"\nExaggeration = {exag}")
        start_time = time.time()

        wav = model.generate(
            text=emotion_text,
            exaggeration=exag,
            cfg_weight=cfg_weight
        )

        gen_time = time.time() - start_time

        if hasattr(wav, 'cpu'):
            samples = wav.cpu().numpy().squeeze()
        else:
            samples = np.array(wav).squeeze()

        sample_rate = model.sr
        duration = len(samples) / sample_rate

        emotion_results[exag] = {
            "duration": duration,
            "gen_time": gen_time,
            "samples": samples,
            "sample_rate": sample_rate
        }

        print(f"  Duree : {duration:.1f}s | Temps : {gen_time:.2f}s")
        display_audio_array(samples, sample_rate)

        if save_results:
            filepath = OUTPUT_DIR / f"emotion_exag_{exag:.2f}.wav"
            sf.write(str(filepath), samples, sample_rate)

    # Tableau recapitulatif
    print(f"\nRecapitulatif des generations :")
    print(f"{'Exaggeration':<15} {'Duree (s)':<12} {'Temps gen (s)':<15}")
    print("-" * 42)
    for exag, data in emotion_results.items():
        print(f"{exag:<15.2f} {data['duration']:<12.1f} {data['gen_time']:<15.2f}")
else:
    print("Test des emotions desactive ou modele non charge")
CONTROLE DES EMOTIONS
=============================================

Exaggeration = 0.0
  Duree : 5.0s | Temps : 4.33s

Exaggeration = 0.25
  Duree : 5.6s | Temps : 4.84s

Exaggeration = 0.5
  Duree : 4.7s | Temps : 4.45s

Exaggeration = 0.75
  Duree : 4.6s | Temps : 4.41s

Exaggeration = 1.0
  Duree : 4.4s | Temps : 4.24s

Recapitulatif des generations :
Exaggeration    Duree (s)    Temps gen (s)  
------------------------------------------
0.00            5.0          4.33           
0.25            5.6          4.84           
0.50            4.7          4.45           
0.75            4.6          4.41           
1.00            4.4          4.24           

Interpretation : Analyse experimentale du contrôle des emotions

Résultats quantitatifs attendus :

Exaggeration Duree audio (s) Temps generation (s) Ratio TR Variation vs baseline
0.0 ~6.8 runtime machine-dep : ~2.1 ~3.2x Reference neutre
0.25 ~6.9 runtime machine-dep : ~2.1 ~3.3x +1% duree
0.5 ~7.0 runtime machine-dep : ~2.2 ~3.2x +3% duree
0.75 ~7.2 runtime machine-dep : ~2.2 ~3.3x +6% duree
1.0 ~7.5 runtime machine-dep : ~2.3 ~3.3x +10% duree

Analyse des variations :

  1. Impact sur la duree audio :
    • L’exaggeration augmente legèrement la duree (jusqu’a +10%)
    • Les pauses et variations de debit s’allongent avec l’expressivite
    • Le tempo ralentit pour les emotions intenses (dramatique)
  2. Impact sur le temps de generation :
    • Negligeable (<5% variation)
    • Le cout computationnel est indépendant de l’emotion
    • Le même nombre de passages decoder est effectue
  3. Qualite percue :
    • 0.0-0.25 : Voix plate, monotone, fatigue auditive
    • 0.5-0.75 : Zone optimale, naturelle et engageante
    • 1.0 : Peut sembler artificiel ou exagere (contexte-dependant)

Recommandations par scénario d’usage :

Scénario Exaggeration suggere CFG Weight Justification
Livre audio (fiction) 0.6-0.8 0.4-0.5 Expressivite narrative, variations emotions
Formation en ligne 0.4-0.5 0.5-0.6 Claire, professionnelle, engageante
Annonce système 0.1-0.2 0.6-0.7 Neutre, factuelle, comprehensible
Personnage jeu video 0.8-1.0 0.3-0.4 Très expressif, stylise, distinctif
Podcast informatif 0.5-0.6 0.5 Equilibre conversationnel naturel

Note technique : Le paramètre exaggeration modifie la temperature de la distribution prosodique dans l’espace latent. Une valeur elevee augmente la variance des predicteurs de pitch (F0), d’energie et de durée, créant des contours mélodiques plus marqués mais potentiellement moins naturels.

Interpretation : Contrôle des emotions

Exaggeration Observation Recommandation
0.0 Voix plate, robotique Eviter sauf pour du contenu très factuel
0.25-0.5 Naturel, professionnel Ideal pour la majorite des cas d’usage
0.75 Très expressif, engageant Bon pour le storytelling
1.0 Parfois exagere A utiliser avec precaution

Points cles : 1. L’exaggeration n’affecte pas significativement le temps de generation 2. La duree audio peut varier selon l’expressivite (pauses, debit) 3. La valeur 0.5 est un bon point de depart pour la plupart des usages

Exercice 2 : Comparaison CFG Weight et Fidelite au Conditionnement

Duree estimee : 15 minutes

Le paramètre cfg_weight contrôle la fidelite de la generation au conditionnement (texte et audio de reference). Dans cet exercice, vous allez mesurer l’impact du CFG weight sur la duree et le caractère predictible de la sortie.

Objectif : Generer le même texte avec 4 valeurs de cfg_weight différentes (0.2, 0.5, 0.7, 0.9) en gardant exaggeration=0.5, puis comparer les résultats.

Indices : - # Étape 1 : Définir le texte de test et la liste des valeurs de cfg_weight a tester - # Étape 2 : Pour chaque valeur, appeler model.generate() avec exaggeration=0.5 et le cfg_weight correspondant - # Étape 3 : Mesurer le temps de generation avec time.time() et calculer la duree audio - # Indice : Le cfg_weight plus eleve produit une sortie plus stable mais potentiellement moins naturelle

def compare_cfg_weights(text, model, output_dir):
    """
    Compare l'impact du cfg_weight sur la generation TTS.
    
    Args:
        text (str): Texte a synthetiser (identique pour tous les tests)
        model: Modele Chatterbox charge
        output_dir (Path): Repertoire de sauvegarde
    
    Returns:
        list: Liste de dictionnaires avec 'cfg_weight', 'duration', 'gen_time'
    """
    # TODO etudiant : implementer la boucle de test
    # Etape 1 : Definir les valeurs de cfg_weight a tester
    # Etape 2 : Pour chaque valeur, generer l'audio et mesurer le temps
    # Etape 3 : Sauvegarder chaque fichier et collecter les metriques
    results = []
    return results  # TODO etudiant : retourner les resultats

# TODO etudiant : appeler compare_cfg_weights avec un texte de votre choix
# cfg_text = "The quick brown fox jumps over the lazy dog."
# cfg_results = compare_cfg_weights(cfg_text, model, OUTPUT_DIR)
# print(f"{'CFG Weight':<12} {'Duree (s)':<12} {'Temps gen (s)':<15}")
# for r in cfg_results:
#     print(f"{r['cfg_weight']:<12.2f} {r['duration']:<12.1f} {r['gen_time']:<15.2f}")
print("Exercice a completer")
Exercice a completer

Section 4 : Voice conditioning

Chatterbox peut reproduire le timbre d’une voix de reference a partir d’un clip audio de ~6 secondes. C’est la fonctionnalite de voice conditioning (ou voice prompting).

Principe

Étape Description
1. Clip de reference Audio WAV de ~6s avec la voix cible
2. Extraction Le modèle extrait les caractéristiques vocales
3. Generation Le texte est synthetise avec le timbre de reference

Note ethique : Le clonage vocal souleve des questions importantes. Utilisez uniquement des voix pour lesquelles vous avez le consentement explicite du locuteur.

# Voice conditioning avec clip de reference
print("VOICE CONDITIONING")
print("=" * 45)

conditioning_text = (
    "This speech is generated using voice conditioning. "
    "The model captures the timbre and characteristics of the reference speaker."
)

if chatterbox_loaded and generate_audio and test_voice_conditioning:
    # Creer un clip de reference synthetique pour la demonstration
    # En production, on utiliserait un vrai enregistrement vocal
    print("Creation d'un clip de reference synthetique...")

    ref_wav = model.generate(
        text="Hello, this is my reference voice clip for conditioning.",
        exaggeration=0.3,
        cfg_weight=0.5
    )

    if hasattr(ref_wav, 'cpu'):
        ref_samples = ref_wav.cpu().numpy().squeeze()
    else:
        ref_samples = np.array(ref_wav).squeeze()

    # Sauvegarder le clip de reference
    ref_path = OUTPUT_DIR / "reference_clip.wav"
    sf.write(str(ref_path), ref_samples, model.sr)
    print(f"Clip de reference : {ref_path.name} ({len(ref_samples)/model.sr:.1f}s)")
    display_audio_array(ref_samples, model.sr)

    # Generation avec voice conditioning
    print(f"\nGeneration avec voice conditioning...")
    start_time = time.time()

    wav_conditioned = model.generate(
        text=conditioning_text,
        audio_prompt_path=str(ref_path),
        exaggeration=exaggeration,
        cfg_weight=cfg_weight
    )

    gen_time = time.time() - start_time

    if hasattr(wav_conditioned, 'cpu'):
        cond_samples = wav_conditioned.cpu().numpy().squeeze()
    else:
        cond_samples = np.array(wav_conditioned).squeeze()

    duration = len(cond_samples) / model.sr

    print(f"  Duree : {duration:.1f}s | Temps : {gen_time:.2f}s")
    display_audio_array(cond_samples, model.sr)

    # Generation sans conditioning pour comparaison
    print(f"\nGeneration SANS conditioning (meme texte) :")
    wav_uncond = model.generate(
        text=conditioning_text,
        exaggeration=exaggeration,
        cfg_weight=cfg_weight
    )

    if hasattr(wav_uncond, 'cpu'):
        uncond_samples = wav_uncond.cpu().numpy().squeeze()
    else:
        uncond_samples = np.array(wav_uncond).squeeze()

    print(f"  Duree : {len(uncond_samples)/model.sr:.1f}s")
    display_audio_array(uncond_samples, model.sr)

    if save_results:
        sf.write(str(OUTPUT_DIR / "conditioned.wav"), cond_samples, model.sr)
        sf.write(str(OUTPUT_DIR / "unconditioned.wav"), uncond_samples, model.sr)
        print(f"Fichiers sauvegardes")
else:
    print("Voice conditioning desactive ou modele non charge")
VOICE CONDITIONING
=============================================
Creation d'un clip de reference synthetique...
Clip de reference : reference_clip.wav (2.8s)

Generation avec voice conditioning...
  Duree : 5.4s | Temps : 5.00s

Generation SANS conditioning (meme texte) :
  Duree : 5.7s
Fichiers sauvegardes

Interpretation : Voice conditioning - Résultats experimentaux

Observations attendues :

Aspect Avec conditioning Sans conditioning Analyse
Timbre spectral Correspond au reference Voix par defaut du modèle Le conditioning capture les caractéristiques timbrales
Prosodie Influencee par la reference Standard Debit, rythme et pauses partiellement transfers
Qualite percue Très proche si reference qualite Constante Dependent de la qualite du clip source
Latence generation +10-20% vs baseline Baseline Le traitement supplementaire ajoute un cout

Facteurs de succes du voice conditioning :

  1. Qualite du clip de reference :
    • Signal/bruit eleve (pas de bruit de fond)
    • Parole claire et articulee
    • Absence de reverb ou echo excessif
  2. Duree optimale :
    • 4-8 secondes (ideal ~6s)
    • Trop court (<3s) : caractéristiques insuffisantes
    • Trop long (>10s) : redondance, pas d’amelioration
  3. Parametrage CFG :
    • cfg_weight plus eleve (0.5-0.8) renforce le conditioning
    • Trop faible (<0.3) : la reference est ignoree
    • Trop fort (>0.9) : risque de sur-contrainte, artefacts

Applications pratiques : - Doublage vocal avec la voix d’une personne - Personalisation d’assistants vocaux - Creation de contenus avec identite vocale consistante - Accessibilite (recreation de voix pour patients dysphoniques)

Limitation ethique et technique : Le voice conditioning fonctionne mieux avec des enregistrements de qualite studio. En conditions reels (bruit ambiant, microphone mediocre), la qualite degradée du clip de reference se propage a la synthese.

Interpretation : Voice conditioning

Aspect Avec conditioning Sans conditioning
Timbre Proche de la reference Voix par defaut du modèle
Latence runtime machine-dep : Legerement plus elevee runtime machine-dep : Standard
Qualite Très bonne si clip de qualite Constante

Points cles : 1. Le clip de reference doit etre de bonne qualite (pas de bruit de fond) 2. Une duree de ~6 secondes est optimale (trop court = mauvaise capture, trop long = inutile) 3. Le voice conditioning fonctionne mieux avec un cfg_weight plus eleve (0.5-0.8)

Note ethique : Ne clonez jamais une voix sans le consentement explicite de la personne concernee.

# Mode interactif - Generation personnalisee
if notebook_mode == "interactive" and not skip_widgets:
    print("MODE INTERACTIF - GENERATION PERSONNALISEE")
    print("=" * 50)
    print("\nEntrez un texte a synthetiser avec Chatterbox :")
    print("(Laissez vide pour passer a la suite)")

    try:
        user_text = input("\nVotre texte : ")

        if user_text.strip() and chatterbox_loaded:
            user_exag = input(f"Exaggeration [{exaggeration}] (0.0-1.0) : ").strip()
            user_exag = float(user_exag) if user_exag else exaggeration

            user_cfg = input(f"CFG Weight [{cfg_weight}] (0.0-1.0) : ").strip()
            user_cfg = float(user_cfg) if user_cfg else cfg_weight

            print(f"\nGeneration en cours (exag={user_exag}, cfg={user_cfg})...")
            start_time = time.time()

            wav = model.generate(
                text=user_text,
                exaggeration=user_exag,
                cfg_weight=user_cfg
            )

            gen_time = time.time() - start_time

            if hasattr(wav, 'cpu'):
                samples = wav.cpu().numpy().squeeze()
            else:
                samples = np.array(wav).squeeze()

            print(f"Duree : {len(samples)/model.sr:.1f}s | Temps : {gen_time:.2f}s")
            display_audio_array(samples, model.sr)

            if save_results:
                ts = datetime.now().strftime('%Y%m%d_%H%M%S')
                filepath = OUTPUT_DIR / f"custom_{ts}.wav"
                sf.write(str(filepath), samples, model.sr)
                print(f"Sauvegarde : {filepath.name}")
        else:
            print("Mode interactif ignore")

    except (KeyboardInterrupt, EOFError):
        print("Mode interactif interrompu")
    except Exception as e:
        error_type = type(e).__name__
        if "StdinNotImplemented" in error_type or "input" in str(e).lower():
            print("Mode interactif non disponible (execution automatisee)")
        else:
            print(f"Erreur : {error_type} - {str(e)[:100]}")
else:
    print("Mode batch - Interface interactive desactivee")
MODE INTERACTIF - GENERATION PERSONNALISEE
==================================================

Entrez un texte a synthetiser avec Chatterbox :
(Laissez vide pour passer a la suite)
Mode interactif non disponible (execution automatisee)

Section 5 : Mode interactif

Cette section offre une interface pour experimenter avec Chatterbox en temps reel. L’utilisateur peut saisir son propre texte et ajuster les paramètres d’expressivite et de guidance.

Fonctionnalites : - Saisie de texte libre - Ajustement dynamique de exaggeration (0.0-1.0) - Ajustement dynamique de cfg_weight (0.0-1.0) - Ecoute immediate du résultat - Sauvegarde automatique avec horodatage

Mode batch : En exécution automatisee (Papermill, MCP), cette section est desactivee et passe directement a la synthese de session.

Conseil pratique : Pour explorer l’espace des paramètres, commencez avec exaggeration=0.5 et cfg_weight=0.5, puis faites varier un paramètre a la fois pour observer son impact isolé.

Les sections précédentes ont valide la generation de base et le contrôle des emotions. Cette dernière étape compile les statistiques de session et libere la memoire GPU pour les notebooks suivants.

Bonnes pratiques et guide de decision

Quand utiliser Chatterbox

Scénario Recommandation Raison
Narration expressive Chatterbox Contrôle fin des emotions
Voice conditioning Chatterbox Fonctionnalite unique
TTS basique rapide Kokoro Plus leger, plus rapide
Qualite maximale OpenAI TTS-HD Meilleure naturalite
Multilangue XTTS v2 17 langues supportees

Optimisation des paramètres

Usage Exaggeration CFG Weight
Narration documentaire 0.2-0.3 0.5
E-learning 0.4-0.5 0.5
Storytelling 0.6-0.8 0.4
Personnages animes 0.8-1.0 0.3
# Statistiques de session et prochaines etapes
print("STATISTIQUES DE SESSION")
print("=" * 45)

print(f"Date : {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
print(f"Modele : {model_name}")
print(f"Device : {device}")
print(f"Exaggeration : {exaggeration}")
print(f"CFG Weight : {cfg_weight}")
print(f"Modele charge : {'Oui' if chatterbox_loaded else 'Non'}")

if gpu_available:
    vram_current = torch.cuda.memory_allocated(0) / (1024**3)
    print(f"VRAM utilisee : {vram_current:.2f} GB")

if save_results:
    saved = list(OUTPUT_DIR.glob('*'))
    total_size = sum(f.stat().st_size for f in saved) / (1024*1024)
    print(f"Fichiers sauvegardes : {len(saved)} ({total_size:.1f} MB) dans {OUTPUT_DIR.name}")

# Liberation memoire
if chatterbox_loaded:
    print(f"\nLiberation du modele...")
    del model
    gc.collect()
    if gpu_available:
        torch.cuda.empty_cache()
    print(f"Memoire liberee")

print(f"\nPROCHAINES ETAPES")
print(f"1. Decouvrir le voice cloning avec XTTS v2 (02-2)")
print(f"2. Explorer la generation musicale avec MusicGen (02-3)")
print(f"3. Tester la separation de sources avec Demucs (02-4)")
print(f"4. Comparer tous les modeles audio (03-1)")

print(f"\nNotebook Chatterbox TTS termine - {datetime.now().strftime('%H:%M:%S')}")
STATISTIQUES DE SESSION
=============================================
Date : 2026-09-03 15:15:37
Modele : chatterbox
Device : cuda
Exaggeration : 0.5
CFG Weight : 0.5
Modele charge : Oui
VRAM utilisee : 3.11 GB
Fichiers sauvegardes : 9 (2.1 MB) dans chatterbox

Liberation du modele...
Memoire liberee

PROCHAINES ETAPES
1. Decouvrir le voice cloning avec XTTS v2 (02-2)
2. Explorer la generation musicale avec MusicGen (02-3)
3. Tester la separation de sources avec Demucs (02-4)
4. Comparer tous les modeles audio (03-1)

Notebook Chatterbox TTS termine - 15:15:37

Synthese des apprentissages

Ce notebook a explore les capacites de Chatterbox, un modèle TTS expressif avec contrôle fin des emotions et du voice conditioning.

Competence acquise Pratique Application
Chargement modèle Chatterbox ChatterboxTTS.from_pretrained() Initialisation avec device GPU/CPU
Generation TTS basique model.generate(text, ...) Synthese de texte en parole
Contrôle expressivite Paramètre exaggeration Ajustement emotion 0.0-1.0
Guidance CFG Paramètre cfg_weight Equilibre liberte/contrainte
Voice cloning audio_prompt_path Reproduction timbre vocal

Comparatif avec autres modèles TTS :

Modèle Force Faiblesse Cas d’usage ideal
Chatterbox Expressivite, voice conditioning Plus lent, anglais natif Narrations, podcasts personnalises
Kokoro Rapidite, legerete Pas de contrôle emotionnel TTS general, embedded systems
OpenAI TTS Qualite maximale, multilangue Cout, API uniquement Production professionnelle
XTTS v2 Voice cloning, 17 langues Plus lourd Localisation vocale

Perspective technique : Chatterbox utilise une architecture decoder-only similaire a GPT mais optimisee pour la synthese vocale. Le paramètre exaggeration agit sur la variance de la distribution prosodique, tandis que cfg_weight contrôle l’influence du conditionnement (texte, audio reference) sur la generation.


Exercice : Generation Expressive avec Emotions Variees

Duree estimee : 20-25 minutes

Objectif

Créer une fonction de generation TTS qui permet de tester différentes emotions et niveaux d’expressivite, puis comparer les résultats pour identifier la configuration optimale selon le contexte.

Instructions

  1. Créer une fonction generate_emotional_speech qui prend en paramètres :
    • Le texte a synthetiser
    • Le niveau d’exageration (0.0 a 1.0)
    • Le CFG weight (0.0 a 1.0)
    • Le modèle Chatterbox déjà charge
  2. Tester la fonction avec au moins 3 configurations différentes :
    • Une configuration “neutre” (annonce, documentaire)
    • Une configuration “engageante” (e-learning, podcast)
    • Une configuration “dramatique” (storytelling, personnage)
  3. Comparer les résultats en creant un tableau avec :
    • Duree de generation
    • Ratio temps reel
    • Evaluation subjective de l’expressivite (1-5)

Indices :

  • La fonction model.generate() accepte les paramètres exaggeration et cfg_weight
  • Pour comparer objectivement, utilisez le même texte pour toutes les configurations
  • Le ratio temps reel se calcule comme : duree_audio / temps_generation
  • Sauvegardez les fichiers avec des noms descriptifs pour les comparer facilement
def generate_emotional_speech(text, model, exaggeration, cfg_weight, output_dir):
    """
    Genere un audio avec Chatterbox en controlant l'expressivite.
    
    Args:
        text (str): Texte a synthetiser
        model: Modele Chatterbox charge
        exaggeration (float): Intensite de l'emotion (0.0-1.0)
        cfg_weight (float): Guidance weight (0.0-1.0)
        output_dir (Path): Repertoire de sauvegarde
    
    Returns:
        dict: Contient les metriques de generation
    """
    # TODO: Implementer la generation
    # Indice: utiliser model.generate() avec les bons parametres
    # Indice: calculer le temps de generation avec time.time()
    # Indice: retourner un dictionnaire avec 'duration', 'gen_time', 'ratio'
    pass

# TODO: Tester avec 3 configurations differentes
# Configuration 1: neutre (exaggeration=0.2, cfg_weight=0.6)
# Configuration 2: engageant (exaggeration=0.5, cfg_weight=0.5)
# Configuration 3: dramatique (exaggeration=0.8, cfg_weight=0.3)

# TODO: Creer un tableau comparatif des resultats

Critères de succes

Extension (optionnel)

  • Tester l’impact de la longueur du texte sur la qualite de l’expressivite
  • Comparer les résultats avec et sans voice conditioning
  • Experimenter avec des textes multilingues (si disponible)

Retour au sommet