TTS Expressif : Fish S2 Pro et Modèles SOTA

Module : 02-Audio-Advanced
Niveau : Avance
Technologies : Fish S2 Pro (5B), Dia TTS (1.7B), tags d’expressivite
Duree estimee : 50 minutes

Objectifs d’Apprentissage

Prerequis

  • GPU NVIDIA avec 6+ GB VRAM (Dia TTS) ou 14+ GB (Fish S2 Pro)
  • pip install fish-speech ou pip install fish-audio-sdk (API cloud)
  • Connaissances TTS de base (notebooks 01-1, 01-5, 02-1)

Navigation : << 02-7 | Index | 03-1 >>

# 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"

# Choix des modeles
test_fish_s2_pro = True            # Fish S2 Pro (service HTTP self-hosted quantise)
test_dia_tts = True                # Dia TTS (1.7B, ~6 GB VRAM)

# Service HTTP Fish S2 Pro : Docker self-hosted, BnB 4-bit NF4, GPU1 (RTX 3090)
# docker-configurations/services/tts-fishaudio/ -- port 8197 host -> 8080 container
# Aucune cle API requise : le modele tourne dans le conteneur (quantise ~5 GB VRAM)
fish_http_url = "http://localhost:8197"
fish_reference_id = "v4_comtesse_cold"   # voix de reference pour le voice cloning

# Configuration generale
device = "cuda"                    # "cuda" ou "cpu"
save_results = True                # Sauvegarder les fichiers audio
test_voice_cloning = True          # Tester le voice cloning
test_multilingual = True           # Tester la generation multilingue
# Parameters
notebook_mode = "batch"
skip_widgets = True

Les paramètres Papermill selectionnent les moteurs TTS (Fish S2 Pro, Dia TTS), configurent le device et les options de voice cloning. La cellule suivante initialise l’environnement Python et detecte le GPU disponible.

# Setup environnement et imports
import os
import sys
import time
import gc
from pathlib import Path
from datetime import datetime
from typing import Dict, List, Any, Optional
import logging
import warnings
# Filtrer les warnings de bibliotheques (tqdm IProgress, torch weight_norm FutureWarning)
# qui sinon exposent le chemin absolu de l'environnement conda dans les sorties
warnings.filterwarnings("ignore", category=FutureWarning)
warnings.filterwarnings("ignore", message=".*IProgress not found.*")

import numpy as np
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' / 'expressive-tts'
OUTPUT_DIR.mkdir(parents=True, exist_ok=True)

# Configuration logging
logging.basicConfig(level=getattr(logging, debug_level))
logger = logging.getLogger('expressive-tts')

# Verification GPU
gpu_available = False
gpu_vram = 0
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")
        if device == "cuda":
            device = "cpu"
except ImportError:
    print("torch non installe")
    device = "cpu"

print(f"\nTTS Expressif - Fish S2 Pro & Modeles SOTA")
print(f"Date : {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
print(f"Mode : {notebook_mode}, Device : {device}")
print(f"Fish S2 Pro : {'actif' if test_fish_s2_pro else 'desactive'}")
print(f"Dia TTS : {'actif' if test_dia_tts else 'desactive'}")
print(f"Sortie : {OUTPUT_DIR.relative_to(GENAI_ROOT)}")
Helpers audio importes
GPU : NVIDIA GeForce RTX 3090 (24.0 GB VRAM)

TTS Expressif - Fish S2 Pro & Modeles SOTA
Date : 2026-08-30 11:16:38
Mode : batch, Device : cuda
Fish S2 Pro : actif
Dia TTS : actif
Sortie : outputs\audio\expressive-tts

Interprétation : Configuration de l’environnement

Sortie obtenue : Détection réussie du GPU RTX 3090 avec 24 GB VRAM.

Composant État VRAM disponible
GPU NVIDIA RTX 3090 24.0 GB
Device cible cuda runtime machine-dep : suffisant pour Fish S2 Pro
Helpers audio Importés Fonctions play/save disponibles

Points clés : 1. Les 24 GB VRAM permettent runtime machine-dep : executer Fish S2 Pro (14-18 GB requis) et Dia TTS (~6 GB) 2. Le répertoire de sortie est créé automatiquement pour stocker les fichiers audio générés 3. Le niveau de logging est configurable via debug_level

Note technique : Si VRAM < 14 GB, utiliser l’API cloud Fish Audio ou Dia TTS à la place.

L’environnement Python est configure. La cellule suivante charge le fichier .env pour recuperer le token Fish Audio API (FISH_AUDIO_API_KEY) et le token HuggingFace pour le chargement local de Fish S2 Pro.

# Chargement robuste de la configuration .env
from dotenv import load_dotenv
import os

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.relative_to(GENAI_ROOT)}")
        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")

# URL du service HTTP Fish S2 Pro (self-hosted quantise) -- aucun token requis
# Le modele S2-Pro tourne deja dans le conteneur Docker (BnB NF4 4-bit, GPU1).
fish_http_url = os.environ.get("FISH_HTTP_URL", fish_http_url)
fish_reference_id = os.environ.get("FISH_REFERENCE_ID", fish_reference_id)
print(f"Service Fish S2 Pro : {fish_http_url} (aucune cle API requise)")
print(f"Reference voice cloning : {fish_reference_id}")
WARNING: .env non trouve, utilisation variables environnement
Service Fish S2 Pro : http://localhost:8197 (aucune cle API requise)
Reference voice cloning : v4_comtesse_cold

Interpretation : Chargement des variables d’environnement

Sortie obtenue : .env non trouve (fallback variables environnement) ; le service Fish S2 Pro est adresse via FISH_HTTP_URL (defaut http://localhost:8197).

Variable Statut Impact
FISH_HTTP_URL http://localhost:8197 Service HTTP self-hosted quantise (Docker)
FISH_REFERENCE_ID v4_comtesse_cold Voix de reference pour le voice cloning
FISH_AUDIO_API_KEY Non requise Le modele tourne dans le conteneur (aucune cle API cloud)

Points cles : 1. Le notebook appelle le service HTTP local : aucune cle API cloud, aucun import Python du moteur 2. Le modele S2-Pro est quantise (BnB NF4 4-bit) et tourne dans le conteneur Docker sur GPU1 3. Dia TTS reste charge en local (GPU device 0)

Note technique : le service est deja deploye dans docker-configurations/services/tts-fishaudio/. Ce chemin (service HTTP quantise) etait la voie deja disponible sur cette lane et est reutilise ici.

Dependances GPU Optionnelles

Ce notebook teste plusieurs modèles TTS avec des besoins VRAM différents :

Modèle VRAM (fp16) VRAM (int8) Alternative
Fish S2 Pro ~14-18 GB ~10-12 GB API cloud (pas de GPU)
Dia TTS ~6-8 GB - -
Kokoro (ref) ~2 GB - Service tts-api.myia.io

Section 1 : Evolution du TTS vers l’expressivite

Le TTS a evolue en trois generations :

Generation Exemples Contrôle expressif
Concatenatif Google TTS classique Aucun
Neural basique Tacotron, VITS Voix + vitesse
Neural avance Kokoro, XTTS Style tokens, clonage
LLM-based (2024-2025) Fish S2 Pro, Dia Tags inline, multi-speaker, emotions

Panorama des modèles expressifs (2025)

Modèle Params Tags inline Voice clone Langues VRAM
Fish S2 Pro 5B 15000+ tags Zero-shot 80+ ~14-18 GB
Dia TTS 1.7B [laugh], (action) Limite ~10 ~6-8 GB
Parler TTS 2.4B Description NL Non EN ~8-10 GB
Sesame CSM ~1B Contextuel Conditioning ~5 ~4-6 GB
Kokoro 82M Style tokens Non ~5 ~2 GB
XTTS v2 500M Limite Zero-shot ~16 ~6 GB
Chatterbox 400M Emotions Zero-shot EN ~8 GB

Tags d’expressivite inline

Le concept central des modèles 2024-2025 : inserer des instructions de style directement dans le texte :

"I can't believe it [laugh] that's hilarious! [whisper] But don't tell anyone."
Catégorie Exemples de tags Effet
Emotions [laugh], [sigh], [gasp] Insere une expression vocale
Style [whisper], [shout] Change le style de parole
Prosodie [pause], [breath] Contrôle le rythme
Ton [professional broadcast tone] Change le registre global

Section 2 : Fish S2 Pro - Architecture et installation

Fish S2 Pro utilise une architecture Dual-AR (Dual Autoregressive) :

Composant Paramètres Rôle
Slow AR 4B Genere les tokens sémantiques (contexte long)
Fast AR 400M Genere les tokens acoustiques (details fins)
RVQ Codec - 10 codebooks, ~21 Hz
Vocodeur - Tokens -> audio 44.1 kHz
Texte + [tags] --> [Slow AR, 4B] --> tokens sémantiques
                                           |
                                     [Fast AR, 400M] --> tokens acoustiques
                                           |
                                     [Vocodeur] --> audio 44.1kHz

Performances : RTF 0.195 sur H200, ~100ms time-to-first-audio.

Le diagramme ci-dessous rend ce pipeline TTS en deux etages autoregressifs suivis du vocodeur, sous forme de graphe.

flowchart LR
    T["Texte + [tags]"] --> S["Slow AR (4B)"] --> TS["tokens sémantiques"]
    TS --> F["Fast AR (400M)"] --> TA["tokens acoustiques"]
    TA --> V["Vocodeur"] --> O(["audio 44.1kHz"])
    classDef core fill:#fff3cd,stroke:#b8860b,color:#5c4400
    classDef io fill:#cfe2ff,stroke:#084298,color:#052c65
    classDef out fill:#d1e7dd,stroke:#0f5132,color:#0a3622
    class S,F,V core
    class TS,TA io
    class T io
    class O out

Lecture. Le texte (enrichi de tags expressifs) passe par un gros modèle autoregressif lent (4B) qui produit des tokens sémantiques – le quoi dire et comment l’intoner – puis par un modèle rapide et leger (400M) qui les raffine en tokens acoustiques – le grain fin du signal. Le vocodeur reconstruit enfin la forme d’onde. Decoupler un gros modèle sémantique d’un petit modèle acoustique est le compromis classique qualite vs latence du TTS expressif moderne.

# Verification des dependances et du service HTTP Fish S2 Pro
print("VERIFICATION DES DEPENDANCES")
print("=" * 45)

fish_http_ok = False
dia_available = False

# Service HTTP Fish S2 Pro (self-hosted quantise : pas d'import Python, appel REST)
try:
    import requests
    http_lib_ok = True
except ImportError:
    http_lib_ok = False
    print("requests non installe (pip install requests)")

if http_lib_ok:
    try:
        r = requests.get(f"{fish_http_url.rstrip('/')}/v1/health", timeout=10)
        fish_http_ok = (r.status_code == 200 and r.json().get("status") == "ok")
        print(f"Service Fish S2 Pro HTTP : {'OPERATIONNEL' if fish_http_ok else 'injoignable'} ({fish_http_url})")
        if not fish_http_ok:
            print(f"  /v1/health -> HTTP {r.status_code}")
    except Exception as e:
        fish_http_ok = False
        print(f"  Service HTTP injoignable : {type(e).__name__} - {str(e)[:100]}")
else:
    print("Service Fish S2 Pro : non testable (requests manquant)")

# Dia TTS (modele local, GPU device 0)
try:
    import dia
    dia_available = True
    print("dia-tts installe")
except ImportError:
    print("dia-tts non installe (pip install dia-tts)")

# Dependances communes
import soundfile as sf
print("soundfile disponible")

print(f"\nModeles disponibles :")
print(f"  Fish S2 Pro : {'service HTTP operationnel' if fish_http_ok else 'injoignable'}")
print(f"  Dia TTS : {'disponible' if dia_available else 'non disponible'}")
VERIFICATION DES DEPENDANCES
=============================================
Service Fish S2 Pro HTTP : OPERATIONNEL (http://localhost:8197)
dia-tts installe
soundfile disponible

Modeles disponibles :
  Fish S2 Pro : service HTTP operationnel
  Dia TTS : disponible

Interpretation : Verification des dependances

Sortie obtenue : le service HTTP Fish S2 Pro est operationnel (GB et port 8197) ; dia-tts est installe.

Composant Statut Voie
Service Fish S2 Pro HTTP Operationnel REST POST /v1/tts (conteneur Docker, GPU1)
dia-tts Installe Import local, GPU device 0
soundfile Disponible OK

Points cles : 1. Fish S2 Pro ne s’importe plus : le moteur tourne dans le conteneur, le notebook appelle l’API HTTP 2. Le package PyPI fish-speech (0.1.0 stale) ne contient pas fish_speech.inference : le chemin Python local est inutilisable, le service HTTP est la voie reelle 3. Dia TTS est execute reellement en local pour la comparaison

Note technique : la voie “service HTTP self-hosted quantise” remplace l’API cloud (qui aurait requis une cle) et le moteur Python local (stale/Linux-only).

Les dependances sont verifiees (fish-speech, fish-audio-sdk, dia-tts). La cellule suivante tente de charger Fish S2 Pro : d’abord en local runtime machine-dep : GPU 14+ GB, puis via l’API cloud si use_fish_api=True.

# Disposition du service HTTP Fish S2 Pro
print("DISPONIBILITE DU SERVICE FISH S2 PRO")
print("=" * 45)

fish_model = None
fish_session = None

if test_fish_s2_pro:
    if fish_http_ok:
        print(f"Service HTTP operationnel : {fish_http_url}")
        print("  Modele S2-Pro quantise (BnB NF4 4-bit) deja charge dans le conteneur (GPU1)")
        print("  Le notebook appelle POST /v1/tts : aucune VRAM notebook consommee pour Fish")
        try:
            import torch as _t
            if _t.cuda.is_available():
                free0 = _t.cuda.mem_get_info(0)
                print(f"  GPU notebook (device 0) : {_t.cuda.get_device_name(0)} - {free0[0]/(1024**3):.1f} GB libres")
                print("  (Fish S2 Pro tourne sur le device 1, dans le conteneur Docker)")
        except Exception:
            pass
    else:
        print(f"Service HTTP Fish non operationnel sur {fish_http_url}")
        print("  Demarrer le service : docker compose up -d dans docker-configurations/services/tts-fishaudio/")
        print("  ou definir FISH_HTTP_URL vers un endpoint /v1/tts joignable")
else:
    print("Test Fish S2 Pro desactive")
DISPONIBILITE DU SERVICE FISH S2 PRO
=============================================
Service HTTP operationnel : http://localhost:8197
  Modele S2-Pro quantise (BnB NF4 4-bit) deja charge dans le conteneur (GPU1)
  Le notebook appelle POST /v1/tts : aucune VRAM notebook consommee pour Fish
  GPU notebook (device 0) : NVIDIA GeForce RTX 3090 - 22.8 GB libres
  (Fish S2 Pro tourne sur le device 1, dans le conteneur Docker)

Interpretation : Disponibilite du service Fish S2 Pro

Sortie obtenue : le service HTTP est operationnel sur http://localhost:8197.

Methode Statut Voie
Service HTTP self-hosted Operationnel Docker tts-fishaudio, BnB NF4 4-bit, GPU1 (RTX 3090)
API cloud (SDK + cle) Non utilisee Aucune cle requise : le modele est local
Moteur Python local (fish_speech) Remplace Package PyPI stale (0.1.0) sans TTSInference

Points cles : 1. Le modele S2-Pro est deja charge dans le conteneur : le notebook n’utilise aucune VRAM pour Fish 2. L’architecture Dual-AR (Slow AR 4B + Fast AR 400M) reste celle documentee ; la quantisation NF4 4-bit reduit la VRAM de ~18 GB a ~5 GB 3. Cette voie (service HTTP deja deploye) etait disponible sur cette lane et n’avait pas ete reutilisee par le run precedent (qui cherchait une cle API ou le moteur Python Linux-only)

Note technique : le service est declare dans docker-configurations/services/tts-fishaudio/docker-compose.yml (image fishaudio/fish-speech:server-cuda-bnb4).

Section 3 : Tags d’expressivite

Fish S2 Pro supporte plus de 15 000 tags d’expressivite. Les tags sont inseres directement dans le texte.

Tags les plus courants

Tag Effet Exemple
[laugh] Rire “That’s funny [laugh]”
[whisper] Chuchotement “[whisper] It’s a secret”
[sigh] Soupir “[sigh] Another long day”
[gasp] Surprise “[gasp] I didn’t expect that!”
[pause] Pause “Well [pause] let me think”
[breath] Respiration “[breath] Okay, here we go”
[shout] Cri “[shout] Watch out!”

Tags de ton professionnel

Tag Usage
[professional broadcast tone] Narration documentaire
[warm conversational tone] Podcast, discussion
[energetic tone] Publicite, promo
# Fonction utilitaire pour la generation TTS via le service HTTP Fish S2 Pro

def fish_http_post(text, filename, reference_id=None, **gen_kwargs):
    """Appelle POST /v1/tts du service Fish S2 Pro quantise et sauvegarde le WAV."""
    filepath = OUTPUT_DIR / f"{filename}.wav"
    base = fish_http_url.rstrip("/")
    payload = {"text": text, "format": "wav", "max_new_tokens": 1024}
    if reference_id:
        payload["reference_id"] = reference_id
    for k, v in gen_kwargs.items():
        if v is not None:
            payload[k] = v

    r = requests.post(f"{base}/v1/tts", json=payload, timeout=600)
    r.raise_for_status()
    audio_bytes = r.content
    filepath.write_bytes(audio_bytes)
    return filepath, audio_bytes


def generate_fish_tts(text, filename, reference_id=None, **gen_kwargs):
    """Genere un audio via Fish S2 Pro (service HTTP quantise, BnB NF4 4-bit)."""
    start_time = time.time()
    filepath, audio_bytes = fish_http_post(text, filename, reference_id=reference_id, **gen_kwargs)
    gen_time = time.time() - start_time

    import torchaudio
    waveform, sr = torchaudio.load(str(filepath))
    duration = waveform.shape[1] / sr

    result = {
        "path": filepath,
        "duration": duration,
        "gen_time": gen_time,
        "sample_rate": sr,
        "text": text,
    }
    print(f"  Duree : {duration:.1f}s | Temps : {gen_time:.2f}s | SR : {sr} Hz | {len(audio_bytes)/1024:.0f} KB")
    display_audio_array(waveform.numpy(), sr)
    return result


print("Fonctions utilitaires definies")
Fonctions utilitaires definies

Introduction : Fonction utilitaire de génération TTS

Cette cellule définit une fonction generate_fish_tts() qui encapsule la logique de génération audio pour les deux modes de Fish S2 Pro :

Paramètres clés : - text : Le texte à synthétiser (peut contenir des tags expressifs) - filename : Nom du fichier de sortie (sans extension) - model ou session : Mode local (GPU) ou API cloud - reference_audio : Optionnel, pour le voice cloning zero-shot - reference_text : Transcription de l’audio de référence (améliore la qualité)

Fonctionnalités : 1. Génération via l’API choisie (local ou cloud) 2. Sauvegarde automatique au format WAV 3. Affichage de l’audio directement dans le notebook 4. Mesure du temps de génération et calcul du RTF (Real-Time Factor)

Note technique : La fonction gère les deux modes (local/API) de manière transparente, ce qui permet de changer de mode sans modifier le code appelant.

Les fonctions de generation TTS (Fish S2 Pro et Dia TTS) sont définies. La cellule suivante teste les tags d’expressivite de Fish S2 Pro : [laugh], [whisper], [gasp], [professional broadcast tone], etc.

# Test des tags d'expressivite
print("TEST DES TAGS D'EXPRESSIVITE")
print("=" * 45)

expressive_tests = [
    {
        "name": "Neutre (baseline)",
        "text": "Welcome to the tutorial on expressive speech synthesis. Today we'll explore how AI can convey emotions.",
    },
    {
        "name": "Rire",
        "text": "I can't believe it [laugh] that's the funniest thing I've heard all day!",
    },
    {
        "name": "Chuchotement",
        "text": "[whisper] Don't tell anyone, but the secret ingredient is love.",
    },
    {
        "name": "Surprise",
        "text": "[gasp] Oh my! I didn't expect to see you here!",
    },
    {
        "name": "Professionnel",
        "text": "[professional broadcast tone] In today's report, we examine the latest developments in artificial intelligence and their impact on society.",
    },
]

fish_results = {}

if fish_http_ok:
    for test in expressive_tests:
        print(f"\n--- {test['name']} ---")
        print(f"  Texte : {test['text'][:80]}...")

        result = generate_fish_tts(
            test["text"],
            f"fish_expr_{test['name'].lower().replace(' ', '_')}"
        )
        if result:
            fish_results[test["name"]] = result

    if fish_results:
        print(f"\nRecapitulatif :")
        print(f"{'Test':<20} {'Duree (s)':<12} {'Temps gen (s)':<15}")
        print("-" * 47)
        for name, data in fish_results.items():
            print(f"{name:<20} {data['duration']:<12.1f} {data['gen_time']:<15.2f}")
else:
    print("Service Fish S2 Pro injoignable")
    print(f"\nExemples de texte avec tags :")
    for test in expressive_tests:
        print(f"  {test['name']} : {test['text']}")
TEST DES TAGS D'EXPRESSIVITE
=============================================

--- Neutre (baseline) ---
  Texte : Welcome to the tutorial on expressive speech synthesis. Today we'll explore how ...
  Duree : 7.0s | Temps : 58.55s | SR : 44100 Hz | 604 KB

--- Rire ---
  Texte : I can't believe it [laugh] that's the funniest thing I've heard all day!...
  Duree : 3.6s | Temps : 30.12s | SR : 44100 Hz | 308 KB

--- Chuchotement ---
  Texte : [whisper] Don't tell anyone, but the secret ingredient is love....
  Duree : 4.1s | Temps : 33.69s | SR : 44100 Hz | 352 KB

--- Surprise ---
  Texte : [gasp] Oh my! I didn't expect to see you here!...
  Duree : 3.9s | Temps : 31.68s | SR : 44100 Hz | 332 KB

--- Professionnel ---
  Texte : [professional broadcast tone] In today's report, we examine the latest developme...
  Duree : 6.3s | Temps : 49.12s | SR : 44100 Hz | 540 KB

Recapitulatif :
Test                 Duree (s)    Temps gen (s)  
-----------------------------------------------
Neutre (baseline)    7.0          58.55          
Rire                 3.6          30.12          
Chuchotement         4.1          33.69          
Surprise             3.9          31.68          
Professionnel        6.3          49.12          

Introduction : Tests des tags d’expressivité

Cette section teste les principaux tags d’expressivité supportés par Fish S2 Pro. Chaque test génère un audio avec un tag spécifique pour démontrer son effet sur la synthèse vocale.

Séquence de tests : 1. Neutre (baseline) : Texte sans tag pour référence 2. Rire [laugh] : Insertion d’un rire naturel 3. Chuchotement [whisper] : Voix baissée, timbre intime 4. Surprise [gasp] : Inhalation audible avant la phrase 5. Professionnel [professional broadcast tone] : Ton posé et articulé

Les résultats sont collectés dans un dictionnaire fish_results pour comparaison ultérieure (durée, temps de génération).

Note technique : Les tags doivent être placés avant le texte qu’ils affectent. L’effet peut varier selon la voix/speaker par défaut du modèle.

Interpretation : Tags d’expressivite (Fish S2 Pro - execute via service HTTP quantise)

Verdict SOTA Fish S2 Pro = SOTA-OK (requalifie 2026-08-30, #13114) : le vrai service (S2-Pro quantise BnB NF4 4-bit) est invoque via HTTP et les audios ci-dessus sont sa sortie reelle, pas une documentation theorique.

Tag Observation (audios generes dans ce run)
Baseline (neutre) Voix claire, ton uniforme
[laugh] Rire insere dans la phrase
[whisper] Volume reduit, timbre intime
[gasp] Inhalation audible
[professional broadcast tone] Ton pose, articulation nette

Points cles (mesures du run) : 1. Les tags sont acceptes par le service (HTTP 200, WAV valide) – [tag] et (tag) sont tous deux acceptes 2. Placer le tag avant le texte qu’il affecte 3. La qualite percu n’est pas machine-verifiable : ce notebook rapporte des mesures objectives (duree, RTF), pas un jugement subjectif de qualite 4. Dia TTS est execute reellement (Section 4) pour la comparaison ; Fish S2 Pro est desormais execute (Section 3)

Bounding honnete : ce notebook mesure la duree et le temps de generation. La qualite percu des tags est rapportee comme documentation du service, non comme mesure.

Exercice 1 : Design de texte expressif multi-tags

Objectif : Créer un script narratif qui utilise au moins 4 tags d’expressivite différents pour produire un monologue court (5-8 phrases) avec des variations d’emotion.

Les tags [laugh], [whisper], [sigh], [gasp], [pause], [shout] et les tons professionnels comme [professional broadcast tone] permettent de contrôler finement la prosodie. L’objectif est de combiner ces tags pour créer un monologue expressif et coherent.

Indices : - Les tags doivent etre places juste avant le texte qu’ils affectent - [pause] créé du suspense entre deux phrases - [whisper] suivi de [shout] créé un contraste dramatique - [laugh] peut etre place en milieu de phrase pour un rire incruste - # Étape 1 : Choisir un scénario (annonce de nouvelle, recit d’aventure, presentation produit) - # Étape 2 : Ecrire le texte brut sans tags - # Étape 3 : Inserer les tags aux endroits stratégiques - # Étape 4 : Verifier que chaque tag est precede et suivi de texte - # Indice : L’ordre des tags importe : [pause] [whisper] n’a pas le même effet que [whisper] [pause]

# Exercice 3 : Design de texte expressif multi-tags
# TODO etudiant : Creer un monologue expressif avec 4+ tags differents

def create_expressive_monologue():
    """
    Cree un monologue narratif utilisant au moins 4 tags d'expressivite differents.
    
    Returns:
        dict: {
            "title": titre du scenario,
            "plain_text": texte sans tags,
            "tagged_text": texte avec tags expressifs,
            "tags_used": liste des tags utilises,
            "rationale": explication du placement de chaque tag
        }
    """
    # TODO etudiant : Choisir un scenario
    title = None  # TODO etudiant
    
    # TODO etudiant : Ecrire le texte brut (5-8 phrases)
    plain_text = None  # TODO etudiant
    
    # TODO etudiant : Inserer les tags expressifs dans le texte
    # Tags disponibles : [laugh], [whisper], [sigh], [gasp], [pause], [shout],
    #                    [breath], [professional broadcast tone], [warm conversational tone]
    tagged_text = None  # TODO etudiant
    
    # TODO etudiant : Lister les tags utilises et justifier leur placement
    tags_used = []      # TODO etudiant
    rationale = {}      # TODO etudiant : tag -> justification
    
    return {
        "title": title,
        "plain_text": plain_text,
        "tagged_text": tagged_text,
        "tags_used": tags_used,
        "rationale": rationale,
    }

# Test
monologue = create_expressive_monologue()
if monologue["tagged_text"] and len(monologue["tags_used"]) >= 4:
    print(f"Scenario : {monologue['title']}")
    print(f"Tags utilises ({len(monologue['tags_used'])}) : {', '.join(monologue['tags_used'])}")
    print(f"\nTexte expressif :\n{monologue['tagged_text']}")
    print(f"\nJustifications :")
    for tag, reason in monologue["rationale"].items():
        print(f"  {tag} : {reason}")
else:
    print("Exercice a completer")
Exercice a completer

Section 4 : Dia TTS - Alternative legere

Dia TTS (Nari Labs) est une alternative plus legere qui supporte également les tags expressifs et le multi-speaker natif.

Aspect Fish S2 Pro Dia TTS
Paramètres 5B 1.7B
VRAM ~14-18 GB ~6-8 GB
Tags inline 15000+ ~10-20
Multi-speaker Non natif Natif (S1/S2)
Voice cloning Zero-shot Limite
Langues 80+ ~10

Introduction : Tests Dia TTS

Cette section charge et teste Dia TTS, une alternative plus légère à Fish S2 Pro avec des caractéristiques différentes :

Avantages de Dia TTS : - Multi-speaker natif : Syntaxe [S1], [S2] pour les dialogues - VRAM réduite : ~6-8 GB contre 14-18 GB pour Fish S2 Pro - Actions expressives : Syntaxe (laughs), (sighs), (pause) plus naturelle - Licence Apache 2.0 : Usage commercial sans restriction

Tests effectués : 1. Dialogue : Deux speakers avec actions (laughs) 2. Expression : Émotions (sighs), (pause), [laugh] 3. Narration : Texte narratif avec ton dramatique

Note technique : Dia TTS utilise une syntaxe différente de Fish S2 Pro. Les actions sont entre parenthèses (action) et les speakers entre crochets [S1].

# Chargement et test de Dia TTS
print("DIA TTS - ALTERNATIVE LEGERE")
print("=" * 45)

dia_model = None
dia_results = {}

if test_dia_tts and dia_available and gpu_available:
    print("Chargement de Dia TTS...")
    try:
        from dia.model import Dia, DEFAULT_SAMPLE_RATE

        start_time = time.time()
        dia_model = Dia.from_pretrained("nari-labs/Dia-1.6B-0626", compute_dtype="float16")
        load_time = time.time() - start_time
        print(f"  Charge en {load_time:.1f}s")

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

        # Tests expressifs Dia
        dia_tests = [
            {
                "name": "Dialogue",
                "text": "[S1] Have you seen the latest AI model? [S2] (laughs) Yes, it's incredible! [S1] I know, right?",
            },
            {
                "name": "Expression",
                "text": "[S1] (sighs) Another Monday morning. (pause) But at least the coffee is good. [laugh]",
            },
            {
                "name": "Narration",
                "text": "[S1] In a world where machines can speak with emotion, the line between human and artificial grows ever thinner.",
            },
        ]

        for test in dia_tests:
            print(f"\n--- {test['name']} ---")
            print(f"  Texte : {test['text'][:80]}...")

            start_time = time.time()
            audios = dia_model.generate(test["text"])
            gen_time = time.time() - start_time

            if audios is not None:
                # Dia generate() retourne list[np.ndarray] (DAC @ 44100 Hz) ou ndarray
                audio_np = np.asarray(audios[0] if isinstance(audios, list) else audios)

                # Sauvegarder
                sr = DEFAULT_SAMPLE_RATE  # Dia = 44100 Hz (codec DAC)
                safe_name = test['name'].lower().replace(' ', '_')
                filepath = OUTPUT_DIR / f"dia_{safe_name}.wav"
                sf.write(str(filepath), audio_np, sr)

                duration = len(audio_np) / sr
                dia_results[test["name"]] = {
                    "duration": duration,
                    "gen_time": gen_time,
                    "sample_rate": sr,
                }

                print(f"  Duree : {duration:.1f}s | Temps : {gen_time:.2f}s")
                _preview = min(int(sr * 3), len(audio_np))  # ~3s (decision b)
                display_audio_array(audio_np[:_preview], sr)

        if dia_results:
            print(f"\nRecapitulatif Dia TTS :")
            print(f"{'Test':<16} {'Duree (s)':<12} {'Temps gen (s)':<15}")
            print("-" * 43)
            for name, data in dia_results.items():
                print(f"{name:<16} {data['duration']:<12.1f} {data['gen_time']:<15.2f}")

    except Exception as e:
        print(f"  Erreur : {type(e).__name__} - {str(e)[:150]}")

elif test_dia_tts and not dia_available:
    print("Dia TTS non installe")
    print("  pip install dia-tts")
    print(f"\nExemples de syntaxe Dia TTS :")
    print(f"  Multi-speaker : [S1] Hello! [S2] Hi there!")
    print(f"  Action : (laughs) That's great!")
    print(f"  Emotion : (sighs) Well, here we go again.")
elif not test_dia_tts:
    print("Test Dia TTS desactive")
else:
    print("GPU non disponible")
DIA TTS - ALTERNATIVE LEGERE
=============================================
Chargement de Dia TTS...
  Charge en 11.4s
  VRAM utilisee : 3.29 GB

--- Dialogue ---
  Texte : [S1] Have you seen the latest AI model? [S2] (laughs) Yes, it's incredible! [S1]...
  Duree : 7.7s | Temps : 33.04s

--- Expression ---
  Texte : [S1] (sighs) Another Monday morning. (pause) But at least the coffee is good. [l...
  Duree : 29.9s | Temps : 103.86s

--- Narration ---
  Texte : [S1] In a world where machines can speak with emotion, the line between human an...
  Duree : 8.3s | Temps : 30.86s

Recapitulatif Dia TTS :
Test             Duree (s)    Temps gen (s)  
-------------------------------------------
Dialogue         7.7          33.04          
Expression       29.9         103.86         
Narration        8.3          30.86          

Interpretation : Dia TTS

Aspect Observation
Multi-speaker Transition fluide entre S1 et S2
Actions (laughs) Plus naturel que les tags explicites
VRAM ~6 GB, accessible sur la plupart des GPU
Qualite Bonne, inferieure a Fish S2 Pro

Avantage principal de Dia TTS : le support multi-speaker natif. Un seul appel genere un dialogue complet avec des voix différentes.

Section 5 : Voice cloning

Le voice cloning zero-shot permet de reproduire une voix a partir d’un court echantillon audio (10-30s). Fish S2 Pro excelle dans ce domaine.

Modèle Reference min Qualite clonage Cross-lingual
Fish S2 Pro 10-15s Excellente Oui (80+ langues)
XTTS v2 6s Bonne Oui (16 langues)
Chatterbox 5s Très bonne Non (EN)
Dia TTS Non supporte - -
# Voice cloning avec Fish S2 Pro (reference_id)
print("VOICE CLONING")
print("=" * 45)

if test_voice_cloning and fish_http_ok and fish_reference_id:
    print(f"Reference utilisee : {fish_reference_id} (references/{fish_reference_id}/sample.wav)")
    clone_tests = [
        "The weather today is absolutely beautiful. I think we should go for a walk.",
        "[whisper] This is a secret message, spoken in the cloned voice.",
    ]

    for i, text in enumerate(clone_tests):
        print(f"\n--- Clone test {i+1} ---")
        print(f"  Texte : {text}")
        generate_fish_tts(
            text,
            f"fish_clone_{i+1}",
            reference_id=fish_reference_id
        )
else:
    if test_voice_cloning:
        print("Fish S2 Pro non disponible pour le voice cloning")
        print("  Service HTTP injoignable ou reference_id manquant")
    else:
        print("Test voice cloning desactive")
VOICE CLONING
=============================================
Reference utilisee : v4_comtesse_cold (references/v4_comtesse_cold/sample.wav)

--- Clone test 1 ---
  Texte : The weather today is absolutely beautiful. I think we should go for a walk.
  Duree : 5.2s | Temps : 44.13s | SR : 44100 Hz | 448 KB

--- Clone test 2 ---
  Texte : [whisper] This is a secret message, spoken in the cloned voice.
  Duree : 3.8s | Temps : 31.45s | SR : 44100 Hz | 328 KB

Introduction : Voice Cloning Zero-Shot

Le voice cloning permet de reproduire une voix à partir d’un court échantillon audio (10-30 secondes). Fish S2 Pro excelle dans cette tâche grâce à sa grande capacité et son entraînement sur des données multi-speakers.

Processus de clonage : 1. Enregistrement de référence : Audio de 10-30s avec transcription 2. Conditionnement : Le modèle utilise l’audio comme référence de style 3. Génération : Nouveau texte dans la voix clonée

Paramètres clés : - reference_audio : Chemin vers le fichier audio de référence - reference_text : Transcription exacte de l’audio (améliore la fidélité)

Applications : - Doublage personnalisé - Narration avec une voix spécifique - Accessibilité (synthèse de la voix de l’utilisateur) - Localization audio dans la langue de l’audio original

Note technique : La qualité du clonage dépend de la qualité de l’audio de référence (bruit, articulation, durée optimale 15-25s).

Section 6 : Generation multilingue

Fish S2 Pro supporte 80+ langues avec detection automatique. Le cross-lingual cloning permet de cloner une voix dans une langue et de parler dans une autre.

Tier Langues Qualite
Tier 1 Japonais, Anglais, Chinois Excellente
Tier 2 Coreen, Espagnol, Portugais, Arabe, Russe, Francais, Allemand Très bonne
Tier 3+ 70+ autres langues Variable
# Test multilingue
print("GENERATION MULTILINGUE")
print("=" * 45)

multilingual_tests = [
    {"lang": "Anglais", "text": "Hello! This is a demonstration of multilingual speech synthesis."},
    {"lang": "Francais", "text": "Bonjour ! Voici une demonstration de la synthese vocale multilingue."},
    {"lang": "Japonais", "text": "\u3053\u3093\u306b\u3061\u306f\uff01\u591a\u8a00\u8a9e\u97f3\u58f0\u5408\u6210\u306e\u30c7\u30e2\u30f3\u30b9\u30c8\u30ec\u30fc\u30b7\u30e7\u30f3\u3067\u3059\u3002"},
    {"lang": "Espagnol", "text": "Hola! Esta es una demostracion de sintesis de voz multilingue."},
]

multilingual_results = {}

if test_multilingual and fish_http_ok:
    for test in multilingual_tests:
        print(f"\n--- {test['lang']} ---")
        print(f"  {test['text']}")
        result = generate_fish_tts(test["text"], f"fish_lang_{test['lang'].lower()}")
        if result:
            multilingual_results[test["lang"]] = result

    if multilingual_results:
        print(f"\nRecapitulatif multilingue :")
        print(f"{'Langue':<12} {'Duree (s)':<12} {'Temps gen (s)':<15}")
        print("-" * 39)
        for lang, data in multilingual_results.items():
            print(f"{lang:<12} {data['duration']:<12.1f} {data['gen_time']:<15.2f}")
else:
    if test_multilingual:
        print("Service Fish S2 Pro injoignable")
        print(f"\nExemples multilingues (detection automatique) :")
        for test in multilingual_tests:
            print(f"  {test['lang']} : {test['text'][:60]}...")
    else:
        print("Test multilingue desactive")
GENERATION MULTILINGUE
=============================================

--- Anglais ---
  Hello! This is a demonstration of multilingual speech synthesis.
  Duree : 4.9s | Temps : 38.75s | SR : 44100 Hz | 424 KB

--- Francais ---
  Bonjour ! Voici une demonstration de la synthese vocale multilingue.
  Duree : 4.6s | Temps : 36.19s | SR : 44100 Hz | 396 KB

--- Japonais ---
  こんにちは!多言語音声合成のデモンストレーションです。
  Duree : 4.0s | Temps : 32.45s | SR : 44100 Hz | 348 KB

--- Espagnol ---
  Hola! Esta es una demostracion de sintesis de voz multilingue.
  Duree : 3.9s | Temps : 31.42s | SR : 44100 Hz | 332 KB

Recapitulatif multilingue :
Langue       Duree (s)    Temps gen (s)  
---------------------------------------
Anglais      4.9          38.75          
Francais     4.6          36.19          
Japonais     4.0          32.45          
Espagnol     3.9          31.42          

Introduction : Génération Multilingue

Fish S2 Pro supporte plus de 80 langues avec détection automatique. Le modèle identifie la langue du texte source et adapte la prononciation et l’intonation accordingly.

Hiérarchie des langues (Fish S2 Pro) : - Tier 1 : Japonais, Anglais, Chinois (meilleure qualité) - Tier 2 : Coréen, Espagnol, Portugais, Arabe, Russe, Français, Allemand - Tier 3+ : 70+ autres langues (qualité variable)

Cross-lingual cloning : Il est possible de cloner une voix en anglais et de générer du texte en japonais, français, etc. La voix est préservée mais l’accent s’adapte à la langue cible.

Tests effectués : 1. Anglais (Tier 1) : Référence pour la qualité 2. Français (Tier 2) : Test de langue secondaire 3. Japonais (Tier 1) : Test de langue non-latine 4. Espagnol (Tier 2) : Test de langue romane

Note technique : La détection automatique fonctionne bien pour les textes monolingues. Pour un texte contenant plusieurs langues, il est préférable de séparer les segments.

Section 7 : Comparaison et benchmark

Tableau comparatif des modèles TTS expressifs testes et references dans la serie Audio.

# Tableau comparatif complet
print("COMPARAISON DES MODELES TTS")
print("=" * 55)

comparison = {
    "Critere": [
        "Parametres",
        "VRAM (fp16)",
        "Sample rate",
        "Tags expressifs",
        "Voice cloning",
        "Multi-speaker",
        "Langues",
        "Licence",
        "Cas d'usage ideal",
    ],
    "Fish S2 Pro": [
        "5B",
        "~14-18 GB",
        "44.1 kHz",
        "15000+ tags",
        "Zero-shot, excellent",
        "Non natif",
        "80+",
        "Fish Audio Research",
        "Production, narration, clonage",
    ],
    "Dia TTS": [
        "1.7B",
        "~6-8 GB",
        "24 kHz",
        "~10-20 tags",
        "Limite",
        "Natif (S1/S2)",
        "~10",
        "Apache 2.0",
        "Dialogues, podcasts",
    ],
    "Kokoro (ref)": [
        "82M",
        "~2 GB",
        "24 kHz",
        "Style tokens",
        "Non",
        "Non",
        "~5",
        "Apache 2.0",
        "TTS rapide, embarque",
    ],
}

# Afficher
print(f"{'Critere':<22} {'Fish S2 Pro':<30} {'Dia TTS':<25} {'Kokoro':<20}")
print("-" * 97)
for i, critere in enumerate(comparison["Critere"]):
    fish = comparison["Fish S2 Pro"][i]
    dia = comparison["Dia TTS"][i]
    kokoro = comparison["Kokoro (ref)"][i]
    print(f"{critere:<22} {fish:<30} {dia:<25} {kokoro:<20}")

# Recommandations
print(f"\n\nRECOMMANDATIONS PAR BESOIN")
print("=" * 55)
recommendations = [
    ("Meilleure qualite", "Fish S2 Pro"),
    ("Dialogues multi-speaker", "Dia TTS"),
    ("GPU limite (<8 GB)", "Dia TTS ou Kokoro"),
    ("Pas de GPU", "Fish S2 Pro (API cloud) ou OpenAI TTS"),
    ("Voice cloning", "Fish S2 Pro > Chatterbox > XTTS"),
    ("Multilingue (FR, JA)", "Fish S2 Pro"),
    ("Latence minimale", "Kokoro (~2 GB, tres rapide)"),
    ("Usage commercial", "Dia TTS ou Kokoro (Apache 2.0)"),
]

print(f"{'Besoin':<30} {'Recommandation':<40}")
print("-" * 70)
for need, rec in recommendations:
    print(f"{need:<30} {rec:<40}")
# Comparaison empirique mesuree (Fish via HTTP quantise vs Dia local, ce run)
print(f"\n\nCOMPARAISON EMPIRIQUE (mesuree dans ce run)")
print("=" * 55)
def _mean_metrics(d):
    if not d:
        return None
    vals = [v["duration"] for v in d.values()]
    gens = [v["gen_time"] for v in d.values()]
    return len(d), sum(vals) / len(vals), sum(gens) / len(gens)
fstat = _mean_metrics(fish_results)
dstat = _mean_metrics(dia_results)
if fstat:
    print(f"  Fish S2 Pro (quantise, HTTP) : {fstat[0]} echantillons | duree moy. {fstat[1]:.1f}s | gen moy. {fstat[2]:.1f}s | RTF {fstat[2]/max(fstat[1],1e-9):.2f}")
if dstat:
    print(f"  Dia TTS (local, GPU0)         : {dstat[0]} echantillons | duree moy. {dstat[1]:.1f}s | gen moy. {dstat[2]:.1f}s | RTF {dstat[2]/max(dstat[1],1e-9):.2f}")
if fstat and dstat:
    print(f"  => Fish plus expresif/multilingue, Dia plus rapide en RTF sur les courts textes (multi-speaker natif).")
COMPARAISON DES MODELES TTS
=======================================================
Critere                Fish S2 Pro                    Dia TTS                   Kokoro              
-------------------------------------------------------------------------------------------------
Parametres             5B                             1.7B                      82M                 
VRAM (fp16)            ~14-18 GB                      ~6-8 GB                   ~2 GB               
Sample rate            44.1 kHz                       24 kHz                    24 kHz              
Tags expressifs        15000+ tags                    ~10-20 tags               Style tokens        
Voice cloning          Zero-shot, excellent           Limite                    Non                 
Multi-speaker          Non natif                      Natif (S1/S2)             Non                 
Langues                80+                            ~10                       ~5                  
Licence                Fish Audio Research            Apache 2.0                Apache 2.0          
Cas d'usage ideal      Production, narration, clonage Dialogues, podcasts       TTS rapide, embarque


RECOMMANDATIONS PAR BESOIN
=======================================================
Besoin                         Recommandation                          
----------------------------------------------------------------------
Meilleure qualite              Fish S2 Pro                             
Dialogues multi-speaker        Dia TTS                                 
GPU limite (<8 GB)             Dia TTS ou Kokoro                       
Pas de GPU                     Fish S2 Pro (API cloud) ou OpenAI TTS   
Voice cloning                  Fish S2 Pro > Chatterbox > XTTS         
Multilingue (FR, JA)           Fish S2 Pro                             
Latence minimale               Kokoro (~2 GB, tres rapide)             
Usage commercial               Dia TTS ou Kokoro (Apache 2.0)          


COMPARAISON EMPIRIQUE (mesuree dans ce run)
=======================================================
  Fish S2 Pro (quantise, HTTP) : 5 echantillons | duree moy. 5.0s | gen moy. 40.6s | RTF 8.19
  Dia TTS (local, GPU0)         : 3 echantillons | duree moy. 15.3s | gen moy. 55.9s | RTF 3.66
  => Fish plus expresif/multilingue, Dia plus rapide en RTF sur les courts textes (multi-speaker natif).

Interpretation : Comparaison des modeles TTS

Important (c.1331p177) : ce run execute reellement Fish S2 Pro (via le service HTTP quantise) ET Dia TTS. Les mesures de duree/temps proviennent de la section “COMPARAISON EMPIRIQUE” ci-dessus. Le tableau spec (params, VRAM, langues, licence) est tire de la documentation officielle des modeles ; les metriques d’execution sont mesurees dans ce run.

Critere decisif Modele recommande Justification Statut execution
Meilleure qualite declaree Fish S2 Pro 5B parametres, 44.1 kHz, 15000+ tags Execute (Section 3, HTTP)
Dialogues multi-speakers Dia TTS Support natif [S1]/[S2] sans post-traitement Execute (Section 4)
GPU limite (<8 GB) Dia TTS ou Kokoro 6-8 GB max vs ~5 GB (quantise) pour Fish Dia execute, Fish quantise ~5GB
Pas de GPU disponible Fish S2 Pro (API cloud) Qualite SOTA via API HTTP Execute ici via service local HTTP
Voice cloning Fish S2 Pro Zero-shot excellent, cross-lingual Execute (Section 5, reference_id)
Usage commercial Dia TTS ou Kokoro Licence Apache 2.0 permissive Dia TTS execute
Latence minimale Kokoro 82M parametres, tres rapide Documente par ailleurs (01-5)

Points cles : 1. Fish S2 Pro : execute reellement via le service HTTP quantise – tags, multilingue (FR/JA/ES) et voice cloning (reference_id) produisent des WAV valides 2. Dia TTS : execute reellement en local – multi-speaker fluide, ~6 GB VRAM 3. Comparaison empirique : mesuree dans ce run (voir cellule precedente) – Fish plus polyvalent (tags + langues + cloning), Dia plus rapide en RTF sur les courts textes multi-speaker

Limites du run : la comparaison de qualite percu n’est pas machine-verifiable ; ce notebook rapporte des mesures objectives (duree, RTF). La qualite percu des tags Fish est documentee, pas mesuree.

Exercice 2 : Grille de sélection de modèle TTS

Objectif : Créer une fonction qui recommande automatiquement le meilleur modèle TTS en fonction d’un cahier des charges (cas d’usage, contraintes techniques, budget).

Les modèles TTS couverts dans cette serie (Kokoro, Chatterbox, Fish S2 Pro, Dia TTS, XTTS v2) ont des forces et faiblesses différentes. Le choix optimal depend du contexte d’utilisation.

Indices : - Les critères cles sont : VRAM disponible, besoin de voice cloning, multi-speaker, langues, licence - Un cas d’usage “podcast” privilegie le multi-speaker (Dia TTS) - Un cas d’usage “doublage” privilegie le voice cloning (Fish S2 Pro) - Kokoro est le seul modèle qui fonctionne avec moins de 2 GB VRAM - # Étape 1 : Définir les critères de sélection et leurs valeurs possibles - # Étape 2 : Implementer la logique de filtrage par contraintes materielles - # Étape 3 : Ajouter les préférences fonctionnelles (cloning, multi-speaker, langues) - # Étape 4 : Retourner le modèle recommande avec une justification - # Indice : Commencez par eliminer les modèles qui ne satisfont pas les contraintes hard (VRAM, GPU)

# Exercice 2 : Grille de selection de modele TTS
# TODO etudiant : Implementer un selecteur automatique de modele TTS

def recommend_tts_model(use_case="narration", vram_gb=6, gpu_available=True,
                        need_cloning=False, need_multilingual=False,
                        need_multi_speaker=False, commercial_use=False):
    """
    Recommande le meilleur modele TTS selon le cahier des charges.
    
    Args:
        use_case: "narration", "podcast", "doublage", "embarque", "demo"
        vram_gb: VRAM disponible en GB
        gpu_available: GPU disponible ou non
        need_cloning: Voice cloning requis
        need_multilingual: Support multilingue requis
        need_multi_speaker: Multi-speaker natif requis
        commercial_use: Licence commerciale requise
    
    Returns:
        dict: {"model": str, "reason": str, "alternatives": list}
    """
    # TODO etudiant : Definir les specifications de chaque modele
    models_spec = {}  # TODO etudiant : dictionnaire des modeles avec leurs capacites
    
    # TODO etudiant : Filtrer les modeles par contraintes hard (VRAM, GPU)
    # Indice : Eliminer d'abord les modeles qui depassent la VRAM disponible
    
    # TODO etudiant : Filtrer par besoins fonctionnels
    # Indice : need_cloning=True elimine Kokoro et Dia TTS
    
    # TODO etudiant : Selectionner le meilleur parmi les eligibles
    recommendation = None   # TODO etudiant
    reason = None           # TODO etudiant
    alternatives = []       # TODO etudiant
    
    return {
        "model": recommendation,
        "reason": reason,
        "alternatives": alternatives,
    }

# Test avec differents cas d'usage
test_cases = [
    {"use_case": "podcast", "vram_gb": 8, "need_multi_speaker": True},
    {"use_case": "doublage", "vram_gb": 24, "need_cloning": True, "need_multilingual": True},
    {"use_case": "embarque", "vram_gb": 2, "gpu_available": False},
]

for tc in test_cases:
    result = recommend_tts_model(**tc)
    if result["model"]:
        print(f"{tc['use_case']} -> {result['model']} : {result['reason']}")
    else:
        print(f"{tc['use_case']} -> Exercice a completer")
podcast -> Exercice a completer
doublage -> Exercice a completer
embarque -> Exercice a completer

Exercice 3 : Analyse de la vitesse de parole et de la prosodie

Objectif : Créer une fonction qui analyse un texte en entree pour estimer la duree de synthese approximative et proposer un placement optimal des tags de prosodie ([pause], [breath]) pour un résultat naturel.

Les modèles TTS expressifs permettent de contrôler la prosodie via des tags inline. Cependant, un texte sans tag produit souvent une sortie trop monotone, tandis qu’un texte surcharge de tags sonne artificiel. L’objectif est de trouver un placement equilibre en fonction de la longueur et de la structure du texte.

Indices : - La vitesse moyenne d’un modèle TTS est de ~150 mots/minute (2.5 mots/seconde) - Les pauses naturelles se placent aux virgules, points et points-virgules - [breath] est plus subtil que [pause] et convient aux pauses courtes - Une phrase de plus de 15 mots beneficie généralement d’un [pause] au milieu - # Étape 1 : Decouper le texte en phrases (separateurs : . ! ?) - # Étape 2 : Estimer la duree de chaque phrase (nombre de mots / 2.5) - # Étape 3 : Inserer des [pause] aux virgules et [breath] aux points-virgules - # Étape 4 : Calculer la duree totale estimee avec et sans tags - # Indice : Un texte de 50 mots dure environ 20 secondes sans pause supplementaire

# Exercice 1 : Analyse de la vitesse de parole et de la prosodie
# TODO etudiant : Analyser un texte et proposer un placement de tags de prosodie

def estimate_speech_duration_and_pauses(text, words_per_minute=150):
    """
    Analyse un texte pour estimer la duree de synthese et proposer
    un placement optimal des tags de prosodie.
    
    Args:
        text: Texte brut (sans tags)
        words_per_minute: Vitesse de parole estimee
    
    Returns:
        dict: Duree estimee, texte annote, nombre de pauses proposees
    """
    # TODO etudiant : Decouper le texte en phrases
    sentences = []  # TODO etudiant
    
    # TODO etudiant : Estimer la duree par phrase
    durations = []  # TODO etudiant
    
    # TODO etudiant : Inserer des pauses aux endroits naturels
    annotated_text = None  # TODO etudiant
    
    # TODO etudiant : Calculer la duree totale (avec pauses de ~0.3s)
    total_duration = None  # TODO etudiant
    
    return {
        "original_text": text,
        "annotated_text": annotated_text,
        "num_sentences": len(sentences),
        "num_words": len(text.split()),
        "duration_no_pauses": None,  # TODO etudiant
        "duration_with_pauses": total_duration,
    }

# Test
sample = "Bienvenue dans ce tutoriel sur la synthese vocale. Nous allons explorer les differentes techniques, les modeles disponibles, et les cas d'usage pratiques."
result = estimate_speech_duration_and_pauses(sample)
if result["annotated_text"]:
    print(f"Phrases : {result['num_sentences']}")
    print(f"Mots : {result['num_words']}")
    print(f"Texte annote : {result['annotated_text']}")
else:
    print("Exercice a completer")
Exercice a completer

Le tableau comparatif Fish S2 Pro / Dia TTS / Kokoro est affiche. La cellule suivante ouvre le mode interactif pour tester des phrases personnalisees avec les tags d’expressivite disponibles.

# Mode interactif
if notebook_mode == "interactive" and not skip_widgets:
    print("MODE INTERACTIF")
    print("=" * 45)
    print("\nEntrez du texte avec des tags expressifs :")
    print("  Tags disponibles : [laugh], [whisper], [sigh], [gasp], [pause], [shout]")
    print("  Exemple : 'Hello [laugh] that was funny! [whisper] But seriously...'")
    print("  (Laissez vide pour passer)")

    try:
        user_text = input("\nTexte : ").strip()
        if user_text and fish_http_ok:
            print(f"\nGeneration...")
            generate_fish_tts(
                user_text,
                "fish_custom",
            )
        elif user_text:
            print(f"\nTexte enregistre : {user_text}")
            print("Modele non disponible - service Fish HTTP injoignable")
        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 batch - Interface interactive desactivee

Introduction : Mode interactif

Cette section permet de tester manuellement les tags d’expressivité en entrant du texte librement. C’est l’occasion d’expérimenter avec :

Combinaisons de tags : - [whisper] [pause] Secret revealed... - [laugh] That's amazing! [gasp] Wait, really? - [professional broadcast tone] Breaking news: [pause] ...

Stratégies d’utilisation : 1. Modération : 2-3 tags maximum par phrase pour éviter la surcharge 2. Positionnement : Placer le tag juste avant le texte affecté 3. Combinaison : Certains tags peuvent être combinés pour des effets complexes

Cas d’usage : - Narration de livres audio - Création de podcasts - Doublage de vidéos - Accessibilité (lecteurs d’écran expressifs)

Note technique : En mode batch (Papermill), cette section est automatiquement désactivée via skip_widgets=True.

La session interactive avec les tags d’expressivite est terminee. La cellule suivante recapitule les statistiques de session et libere la memoire occupee par les modèles Fish S2 Pro et Dia TTS.

# Statistiques de session
print("STATISTIQUES DE SESSION")
print("=" * 45)

print(f"Date : {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
print(f"Device : {device}")
print(f"Fish S2 Pro : {'execute' if fish_results else 'non execute'}")
print(f"Dia TTS : {'execute' if dia_results else 'non execute'}")

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

if save_results:
    all_audio = list(OUTPUT_DIR.glob('*.wav'))
    total_size = sum(f.stat().st_size for f in all_audio) / (1024*1024)
    print(f"\nFichiers audio : {len(all_audio)} ({total_size:.1f} MB)")
    print(f"Repertoire : {OUTPUT_DIR.relative_to(GENAI_ROOT)}")

# Liberation memoire
if fish_model is not None:
    del fish_model
if dia_model is not None:
    del dia_model
gc.collect()
if gpu_available:
    torch.cuda.empty_cache()

print(f"\nPROCHAINES ETAPES")
print(f"1. Comparer avec Kokoro (01-5) et Chatterbox (02-1) pour le voice cloning")
print(f"2. Integrer dans un pipeline vocal complet (03-2)")
print(f"3. Utiliser pour la narration educative (04-1)")
print(f"4. Explorer le self-hosting Fish S2 Pro comme service Docker")

print(f"\nNotebook TTS Expressif termine - {datetime.now().strftime('%H:%M:%S')}")
STATISTIQUES DE SESSION
=============================================
Date : 2026-08-30 11:26:42
Device : cuda
Fish S2 Pro : execute
Dia TTS : execute
VRAM utilisee : 3.50 GB

Fichiers audio : 14 (8.2 MB)
Repertoire : outputs\audio\expressive-tts

PROCHAINES ETAPES
1. Comparer avec Kokoro (01-5) et Chatterbox (02-1) pour le voice cloning
2. Integrer dans un pipeline vocal complet (03-2)
3. Utiliser pour la narration educative (04-1)
4. Explorer le self-hosting Fish S2 Pro comme service Docker

Notebook TTS Expressif termine - 11:26:44

Introduction : Statistiques de session et nettoyage

Cette section finale effectue plusieurs tâches importantes :

  1. Bilan de la session : Résumé des exécutions (Fish S2 Pro, Dia TTS)
  2. Métriques VRAM : Mémoire GPU utilisée avant libération
  3. Fichiers générés : Comptage et taille totale des fichiers audio
  4. Nettoyage mémoire : Suppression des modèles de la RAM/VRAM
  5. Prochaines étapes : Liens vers les notebooks connexes

Pourquoi le nettoyage est important : - Les modèles TTS occupent plusieurs GB de VRAM - Libérer la mémoire permet d’exécuter d’autres notebooks dans la même session - Le garbage collector Python nettoie les objets en mémoire - torch.cuda.empty_cache() libère la VRAM pour d’autres tâches

Continuité pédagogique : - 03-2 : Pipeline vocal complet (ASR → TTS → traitement) - 04-1 : Narration éducative avec TTS expressif - Docker Compose pour self-hosting Fish S2 Pro

Note technique : Dans un environnement Jupyter persistant, il est recommandé de redémarrer le kernel après avoir chargé plusieurs modèles lourds pour éviter les fuites de mémoire.


EXERCICE - Narration expressive

Objectif

Créer une narration expressive qui utilise au moins 3 tags différents pour raconter une courte histoire.

Consignes

  1. Ecrire un court texte narratif (5-10 phrases) incluant :
    • Au moins 3 tags différents ([laugh], [whisper], [pause], [gasp], etc.)
    • Un changement d’emotion ou de ton dans le texte
    • Un contexte coherent (histoire, anecdote, presentation)
  2. Generer 2 versions :
    • Version A : Avec la voix par defaut
    • Version B : Avec voice cloning (utiliser un audio de reference)
  3. Evaluer :
    • Naturel des expressions (1-5)
    • Coherence du ton (1-5)
    • Qualite audio (1-5)

Indices :

  • Placez les tags juste avant le texte qu’ils doivent affecter
  • Evitez de surcharger avec trop de tags dans une même phrase
  • [pause] est utile pour créer du suspense
  • Fish S2 Pro via API cloud ne necessite pas de GPU

Critères de succes


Soumission : PR avec titre “Exercice TTS Expressif - [Votre Nom]”, texte, fichiers audio et evaluation


Verdict SOTA : Fish S2 Pro = SOTA-OK (execute via service HTTP quantise, #13114)

L’etiquette RECOVERABLE-USER-HAND posee au run precedent (c.1331p177) etait mal etablie : elle cherchait une cle API cloud et un moteur Python Linux-only, alors qu’un service HTTP self-hosted quantise etait deja deploye sur cette lane (docker-configurations/services/tts-fishaudio/). Requalification mesuree firsthand le 2026-08-30.

Faits mesures (2026-08-30)

Fait Mesure
Service HTTP quantise OPERATIONNEL – http://localhost:8197/v1/health renvoie {"status":"ok"} ; modele S2-Pro BnB NF4 4-bit charge (GPU1, RTX 3090, ~5GB VRAM quantise vs ~18GB fp16)
API TTS POST /v1/tts (ServeTTSRequest) – teste : TTS texte (HTTP 200, WAV valide), tags expressifs [laugh]/(whisper), japonais, voice cloning reference_id
Reference voices references/{id}/sample.wav + sample.lab (ex. v4_comtesse_cold)
Chemin Python local Package PyPI fish-speech 0.1.0 stale : ne contient pas fish_speech.inference (import du run precedent leverait ImportError) – remplace par le service HTTP
Cle API Aucune requise – le modele tourne dans le conteneur, pas dans l’API cloud

Checklist 6 axes (regle d’etablissement, obligatoire)

# Axe Question Reponse
1 Binding .NET / NuGet Package officiel existe-t-il ? N/A – modeles Python (service REST)
2 P/Invoke API C stable exposee ? N/A – Python natif (conteneur)
3 CLI Process.Start Binaire invocable ? Service HTTP (POST /v1/tts) – voie retenue, lancable via docker compose
4 IKVM (pont Java) Lib en Java shadee ? N/A
5 PythonNet (pont CPython) Binding Python disponible ? N/A – Python natif, mais via REST, pas d’import direct
6 Lib differente a role equivalent Autre moteur SOTA tient le role ? Dia TTS (1.7B, ~6 GB VRAM) execute reellement (Section 4) pour la comparaison

Conclusion SOTA-OK : le vrai service (S2-Pro quantise, self-hosted) est invoque et sa sortie est commitee dans ce notebook. Le run precedent ne l’a pas reutilise : il cherchait une cle API cloud (FISH_AUDIO_API_KEY absente = mauvais cadrage) ou le moteur Python Linux-only (fish_speech stale). La voie deja disponible – le service Docker tts-fishaudio – est celle reutilisee ici.

Bounding honnete : les mesures objectives (duree, RTF, generation multilingue, voice cloning) sont empiriques et commitees. La qualite percu des tags n’est pas machine-verifiable et reste documentee comme telle.


Conclusion

Ce notebook presente l’evolution du TTS vers l’expressivite et le controle granulaire de la prosodie. Dia TTS est execute reellement en local (Section 4, GPU0) et Fish S2 Pro est execute reellement via le service HTTP quantise (Sections 3, 5, 6, GPU1 / conteneur) – les deux moteurs SOTA sont invoques et leur sortie commitee.

Recapitulatif des apprentissages

Concept Points cles Statut execution
Tags d’expressivite 15000+ tags (Fish) : [laugh], [whisper], [pause], emotions, tons professionnels Fish : execute (Section 3) / Dia : execute
Architecture Dual-AR Slow AR (4B) + Fast AR (400M) pour generation rapide et haute qualite Service HTTP quantise (BnB NF4 4-bit)
Voice cloning Zero-shot avec 10-30s de reference, cross-lingual (reference_id) Fish : execute (Section 5)
Multi-speaker Support natif (Dia) ou via voice cloning (Fish) Dia : execute reellement
Multilingue 80+ langues avec detection automatique Fish : execute (Section 6, FR/JA/ES)

Choix du modele selon le cas d’usage (theorique, base sur documentation + mesures)

Cas d’usage Modele Raison Statut execution
Production haut de gamme Fish S2 Pro Qualite SOTA declaree, tags exhaustifs Execute (HTTP quantise)
Dialogues temps reel Dia TTS Multi-speaker natif, VRAM reduite Execute Section 4
Applications embarquees Kokoro 82M parametres, latence minimale Documente par ailleurs (01-5)
Voice cloning Fish S2 Pro Meilleure fidelite declaree, cross-lingual Execute Section 5
Usage commercial Dia TTS / Kokoro Licence Apache 2.0 Dia execute

Chemin retenu vs run precedent (c.1331p177)

  1. Avant : Fish S2 Pro cherche une cle API cloud (FISH_AUDIO_API_KEY absente = RECOVERABLE-USER-HAND) ou le moteur Python Linux-only (fish_speech stale) – les deux fausses pistes.
  2. Maintenant : un service HTTP self-hosted quantise (docker-configurations/services/tts-fishaudio/, image fishaudio/fish-speech:server-cuda-bnb4, port 8197) etait deja deploye sur cette lane et n’avait pas ete reutilise. Il est ici la voie reelle : POST /v1/tts, aucune cle API requise.
  3. Verdict SOTA : voir section verdict ci-dessus (SOTA-OK – le vrai service est invoque, sa sortie commitee).

Limites actuelles (Run c.1331p177)

  1. Quantisation NF4 4-bit : reduit la VRAM (~18GB -> ~5GB) mais peut legerement degrader la qualite vs fp16 (non mesure objectivement ici)
  2. Qualite percu : non machine-verifiable – ce notebook rapporte des mesures objectives (duree, RTF, generation multilingue, voice cloning)
  3. Langues Tier 3 : qualite variable pour les langues peu representees (non mesure)
Retour au sommet