# 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 Kokoro
kokoro_model = "hexgrad/Kokoro-82M" # Modele HuggingFace
voice = "af_heart" # Voix par defaut
use_onnx = True # Utiliser le backend ONNX (plus rapide)
# Configuration
compare_with_openai = True # Comparer avec OpenAI TTS
test_multiple_voices = True # Tester plusieurs voix
benchmark_latency = True # Benchmarks de latence
save_results = True # Sauvegarder les resultatsKokoro TTS Local - Synthese Vocale Legere
Module : 01-Audio-Foundation
Niveau : Intermediaire
Technologies : Kokoro TTS (82M params, MIT license)
Duree estimee : 35 minutes
VRAM : ~2 GB
Objectifs d’Apprentissage
Prerequis
- GPU NVIDIA avec au moins 2 GB VRAM (ou CPU)
pip install kokoro-onnxoupip install kokoro- Notebook 01-1 recommande (pour la comparaison avec OpenAI TTS)
Navigation : Index | << Précédent
# Parameters
BATCH_MODE = "true"Introduction technique
Ce notebook explore Kokoro TTS, un modèle de synthese vocale open-source alternatif aux solutions cloud comme OpenAI TTS.
Architecture de Kokoro
| Composant | Description |
|---|---|
| Modèle | Kokoro-82M (82 millions de paramètres) |
| Licence | MIT (usage commercial autorise) |
| Backends | ONNX (production) ou PyTorch (développement) |
| Langues | Anglais, Francais, Espagnol, Chinois, Japonais, Coreen |
| Voix | ~20 voix pre-entraines (hommes/femmes, accents) |
Avantages de Kokoro
- Gratuit et illimite : aucun cout par caractère, pas de cle API
- Local : exécution sur votre machine, confidentialite des données
- Leger : 82 MB de modèle, ~2 GB VRAM
- Rapide : runtime machine-dep : 5-20x temps reel sur GPU
Flux de travail du notebook
- Chargement du modèle Kokoro depuis HuggingFace
- Première generation TTS avec une voix par defaut
- Exploration de différentes voix et langues
- Comparaison directe avec OpenAI TTS (latence, qualite, cout)
- Mode interactif pour experimentation libre
Les paramètres Papermill etant définis, nous importons les bibliotheques necessaires pour Kokoro TTS, notamment kokoro pour la synthese vocale locale et soundfile pour la gestion des fichiers audio.
# Setup environnement et imports
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
# 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, display_audio_bundle
)
print("Helpers audio importes")
except ImportError:
print("Helpers audio non disponibles - mode autonome")
# Repertoires
OUTPUT_DIR = GENAI_ROOT / 'outputs' / 'audio' / 'kokoro'
OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
# Configuration logging
logging.basicConfig(level=getattr(logging, debug_level))
logger = logging.getLogger('kokoro_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 - Kokoro fonctionne aussi sur CPU")
except ImportError:
print("torch non installe - utilisation CPU")
print(f"\nKokoro TTS Local - Synthese Vocale")
print(f"Date : {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
print(f"Modele : {kokoro_model}")
print(f"Voix : {voice}")
print(f"Backend ONNX : {use_onnx}")
print(f"Sortie : {OUTPUT_DIR}")Helpers audio importes
GPU : NVIDIA GeForce RTX 4090 (24.0 GB VRAM)
Kokoro TTS Local - Synthese Vocale
Date : 2026-08-19 13:27:24
Modele : hexgrad/Kokoro-82M
Voix : af_heart
Backend ONNX : True
Sortie : <USER_PATH>\AppData\Local\Temp\claude\d--CoursIA\0c3b8a34-33b8-494d-b9c5-7a23bec864f1\scratchpad\wt11443\MyIA.AI.Notebooks\GenAI\outputs\audio\kokoro
Interpretation : Configuration de l’environnement
L’initialisation de l’environnement a verifie les pre-requis techniques pour l’exécution du notebook.
| Composant | Statut | Rôle |
|---|---|---|
| GPU | Detecte (RTX 3090, 24 GB VRAM) | Acceleration de l’inference |
| Helpers audio | Importes | Fonctions utilitaires pour lecture/sauvegarde |
| Repertoire sortie | Créé (outputs/audio/kokoro/) |
Stockage des fichiers audio generes |
| Logging | Configure (INFO) | Trace des opérations et debugging |
Points cles : - La presence d’un GPU NVIDIA avec 2+ GB VRAM permet une acceleration significative (5-10x) - Le modèle fonctionne également sur CPU, mais plus lentement - Les helpers audio facilitent la manipulation des fichiers (lecture, sauvegarde, lecture dans le notebook)
Note technique : Si le GPU n’est pas disponible, Kokoro bascule automatiquement sur CPU. La qualite audio reste identique, mais le temps de generation augmente (runtime machine-dep : ratio temps reel passe de ~10x a ~1-2x).
L’environnement Python etant configure, nous chargeons les variables d’environnement qui permettront les comparaisons avec l’API OpenAI TTS dans les sections avancees.
# Chargement robuste de la configuration .env (Pattern A - Papermill-safe)
from dotenv import load_dotenv
import os
from pathlib import Path
current_path = Path.cwd()
genai_path = None
# Remonter jusqu'au dossier GenAI
while len(current_path.parts) > 1:
if current_path.name == "GenAI":
genai_path = current_path
break
current_path = current_path.parent
if genai_path:
env_path = genai_path / ".env"
if env_path.exists():
load_dotenv(env_path)
print(f".env charge depuis: {env_path.name}")
else:
print("WARNING: .env non trouve dans GenAI")
else:
print("WARNING: Dossier GenAI non trouve").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 Kokoro
Kokoro est un modèle TTS leger et open-source sous licence MIT.
Caractéristiques
| Aspect | Kokoro 82M | OpenAI TTS |
|---|---|---|
| Paramètres | 82 millions | Non publie |
| Licence | MIT (libre) | Proprietaire |
| Cout | Gratuit | $15-30 / 1M chars |
| Hebergement | Local | Cloud OpenAI |
| VRAM | ~2 GB | N/A (API) |
| Latence | runtime machine-dep : ~0.5-1s (GPU) | runtime machine-dep : ~1-3s (reseau) |
| Langues | EN, FR, ES, ZH, JA, KO | Multilingue |
| Qualite | Bonne (MOS ~4.0) | Excellente (MOS ~4.5) |
Quand utiliser Kokoro vs OpenAI
| Scénario | Recommandation | Raison |
|---|---|---|
| Prototypage | Kokoro | Gratuit, pas de cle API |
| Production web | OpenAI TTS | Qualite superieure |
| Données sensibles | Kokoro | Exécution locale |
| Volume eleve | Kokoro | Pas de cout par caractère |
| Meilleure qualite | OpenAI TTS-HD | Voix les plus naturelles |
Introduction : Chargement de Kokoro
Cette section initialise le modèle Kokoro TTS pour la generation audio.
Deux backends disponibles : - kokoro-onnx : Backend ONNX optimise (recommande, +30-50% plus rapide) - kokoro : Backend PyTorch standard (alternative si ONNX indisponible)
Processus de chargement : 1. Verification de l’installation des dependances 2. Telechargement automatique du modèle (82 MB) depuis HuggingFace 3. Chargement des fichiers de voix pre-entraines 4. Initialisation du moteur de synthese
Le modèle est charge une seule fois en memoire, puis reutilise pour toutes les generations ulterieures, ce qui optimise les performances.
# Chargement du modele Kokoro
print("CHARGEMENT DU MODELE KOKORO")
print("=" * 45)
kokoro_loaded = False
if use_onnx:
try:
from kokoro_onnx import Kokoro
from pathlib import Path
import requests
print("Chargement Kokoro (backend ONNX)...")
# Chemins pour les fichiers du modele
cache_dir = Path.home() / '.cache' / 'kokoro-onnx'
cache_dir.mkdir(parents=True, exist_ok=True)
model_path = cache_dir / 'kokoro-v1.0.onnx'
voices_path = cache_dir / 'voices-v1.0.bin'
# Telecharger si necessaire
if not model_path.exists():
print("Telechargement du modele...")
response = requests.get('https://github.com/thewh1teagle/kokoro-onnx/releases/download/model-files-v1.0/kokoro-v1.0.onnx', stream=True)
with open(model_path, 'wb') as f:
for chunk in response.iter_content(chunk_size=8192):
if chunk:
f.write(chunk)
print(f"Modele telecharge : {model_path.stat().st_size / (1024*1024):.1f} MB")
if not voices_path.exists():
print("Telechargement des voix...")
response = requests.get('https://github.com/thewh1teagle/kokoro-onnx/releases/download/model-files-v1.0/voices-v1.0.bin', stream=True)
with open(voices_path, 'wb') as f:
for chunk in response.iter_content(chunk_size=8192):
if chunk:
f.write(chunk)
print(f"Voices telechargees : {voices_path.stat().st_size / (1024*1024):.1f} MB")
start_time = time.time()
kokoro = Kokoro(str(model_path), str(voices_path))
load_time = time.time() - start_time
kokoro_loaded = True
available_voices = kokoro.get_voices()
print(f"Modele charge en {load_time:.1f}s (ONNX)")
print(f"Voix disponibles ({len(available_voices)}) : {', '.join(available_voices[:10])}{'...' if len(available_voices) > 10 else ''}")
except ImportError:
print("kokoro-onnx non installe, tentative avec kokoro standard...")
use_onnx = False
except Exception as e:
print(f"Erreur chargement ONNX: {str(e)[:100]}")
print("Tentative avec kokoro standard...")
use_onnx = False
if not use_onnx and not kokoro_loaded:
try:
from kokoro import KPipeline
print("Chargement Kokoro (backend PyTorch)...")
start_time = time.time()
pipeline = KPipeline(lang_code='a') # 'a' = auto
load_time = time.time() - start_time
kokoro_loaded = True
print(f"Modele charge en {load_time:.1f}s (PyTorch)")
except ImportError:
print("Ni kokoro-onnx ni kokoro ne sont installes")
print("Installation : pip install kokoro-onnx")
print("Ou : pip install kokoro soundfile")
if kokoro_loaded:
print(f"\nModele pret pour la generation")
if gpu_available:
vram_used = torch.cuda.memory_allocated(0) / (1024**3)
print(f"VRAM utilisee : {vram_used:.2f} GB")CHARGEMENT DU MODELE KOKORO
=============================================
Chargement Kokoro (backend ONNX)...
Modele charge en 1.2s (ONNX)
Voix disponibles (54) : af_alloy, af_aoede, af_bella, af_heart, af_jessica, af_kore, af_nicole, af_nova, af_river, af_sarah...
Modele pret pour la generation
VRAM utilisee : 0.00 GB
Interpretation : Chargement du modèle
Le chargement du modèle Kokoro represente l’étape d’initialisation du système TTS.
| Aspect | Detail |
|---|---|
| Taille modèle | ~82 MB (Kokoro-82M) |
| Backend ONNX | +30-50% plus rapide que PyTorch |
| Temps de chargement | 1-3s (première fois) |
| VRAM | ~2 GB (GPU) ou 0 (CPU) |
| Voix disponibles | ~20 voix pre-entraines |
Points cles : 1. Le modèle est telecharge depuis HuggingFace lors de la première utilisation 2. Les fichiers sont mis en cache dans ~/.cache/kokoro-onnx/ 3. Le backend ONNX est recommande pour la production (inference optimisee) 4. Plusieurs voix sont disponibles : anglophones (af_, am_, bf_, bm_), francophones (ff_), etc.
Note technique : Si le modèle n’est pas installe (
kokoro-onnxoukokoro), le notebook affiche les commandes d’installation appropriees. Le reste du notebook peut etre execute avec l’API OpenAI TTS en fallback.
Section 2 : Première generation
La generation avec Kokoro suit un pipeline simple : 1. Charger le modèle (fait une seule fois) 2. Specifier le texte et la voix 3. Appeler la méthode de generation 4. Recevoir un array numpy + sample rate
Introduction : Première generation TTS
Cette section realise la première synthese vocale avec Kokoro, depuis le texte jusqu’a l’audio.
Pipeline de generation : 1. Tokenization : Le texte est decoupe en unites linguistiques 2. Encodage : Conversion en representations phonetiques 3. Synthese : Le modèle genere la waveform audio 4. Sortie : Array numpy + sample rate (24000 Hz)
Paramètres cles : - voice : Identifiant de la voix (ex: af_heart) - speed : Vitesse de parole (1.0 = normale) - lang_code : Code langue (‘a’ = auto-detection)
Le résultat est un audio qu’on peut ecouter directement dans le notebook ou sauvegarder en fichier WAV.
# Premiere generation TTS avec Kokoro
print("PREMIERE GENERATION KOKORO")
print("=" * 45)
sample_text = (
"Bonjour et bienvenue dans ce cours sur la synthese vocale locale. "
"Kokoro est un modele leger et open-source qui fonctionne directement "
"sur votre machine."
)
print(f"Texte : {sample_text}")
print(f"Voix : {voice}")
if kokoro_loaded:
start_time = time.time()
if use_onnx:
# API kokoro-onnx
samples, sample_rate = kokoro.create(
sample_text,
voice=voice,
speed=1.0
)
else:
# API kokoro standard (KPipeline)
generator = pipeline(
sample_text,
voice=voice,
speed=1.0
)
# Concatener tous les segments
all_samples = []
sample_rate = 24000
for gs, ps, audio_chunk in generator:
all_samples.append(audio_chunk)
samples = np.concatenate(all_samples) if all_samples else np.array([])
gen_time = time.time() - start_time
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")
print(f" Taille : {len(samples) * 4 / 1024:.1f} KB (float32)")
# Lecture
print(f"\nEcoute :")
display_audio_array(samples, sample_rate)
# Sauvegarde
if save_results:
filepath = OUTPUT_DIR / f"kokoro_{voice}.wav"
sf.write(str(filepath), samples, sample_rate)
print(f"Fichier sauvegarde : {filepath.name}")
else:
print("Modele Kokoro non charge")PREMIERE GENERATION KOKORO
=============================================
Texte : Bonjour et bienvenue dans ce cours sur la synthese vocale locale. Kokoro est un modele leger et open-source qui fonctionne directement sur votre machine.
Voix : af_heart
Generation reussie
Duree audio : 10.6s
Sample rate : 24000 Hz
Temps de generation : 5.02s
Ratio temps reel : 2.1x
Taille : 994.0 KB (float32)
Ecoute :
Fichier sauvegarde : kokoro_af_heart.wav
Interpretation : Première generation
La première generation TTS avec Kokoro illustre le pipeline complet de synthese vocale.
| Étape | Description |
|---|---|
| Entree | Texte brut (chaîne de caractères) |
| Traitement | Tokenization + encodage phonetique |
| Synthese | Generation waveform par le modèle |
| Sortie | Array numpy (float32) + sample rate |
Metriques cles : - Sample rate : 24000 Hz (qualite telephonique HD) - Ratio temps reel : 5-20x sur GPU (generation plus rapide que la lecture) - Taille : ~4 KB/seconde (format float32 non compresse)
Note technique : Le premier appel est plus lent en raison de la compilation JIT (Just-In-Time). Les appels suivants beneficient du cache et sont significativement plus rapides.
Interpretation : Première generation
| Aspect | Valeur typique | Signification |
|---|---|---|
| Ratio temps reel | 5-20x (GPU), 1-3x (CPU) | Kokoro genere bien plus vite que le temps reel |
| Sample rate | 24000 Hz | Standard pour la parole (qualite telephone HD) |
| Qualite | Bonne | Moins naturelle qu’OpenAI TTS mais acceptable |
Note technique : Le premier appel est plus lent (compilation JIT). Les appels suivants beneficient du cache.
Exercice 1 : Étude de l’impact du paramètre speed sur la qualite TTS
Contexte : Le paramètre speed de Kokoro contrôle la vitesse de parole. Une vitesse trop rapide degrade l’intelligibilite, une vitesse trop lente rend la parole artificielle. Vous souhaitez trouver le compromis optimal.
Objectif : Créer une fonction benchmark_speed() qui genere un même texte a différentes vitesses (0.5, 0.75, 1.0, 1.25, 1.5) et retourne un tableau comparatif avec les metriques cles pour chaque vitesse.
Étapes : 1. Définir la fonction avec les paramètres : text, voice, speeds (liste de floats) 2. Pour chaque vitesse, generer l’audio avec Kokoro et mesurer le temps de generation 3. Calculer les metriques : duree audio, ratio temps reel, nombre d’echantillons 4. Retourner un dictionnaire structure avec les résultats et la vitesse “optimale” (ratio le plus eleve) 5. Sauvegarder les fichiers audio pour ecoute comparative
Indices : - L’API ONNX : kokoro.create(text, voice=voice, speed=speed) accepte le paramètre speed - Le ratio temps reel = duree_audio / temps_generation - Une vitesse de 1.0 est la reference “normale” - Les vitesses extremes (0.5 ou 1.5) peuvent produire des artefacts audio
def benchmark_speed(text, voice="af_heart", speeds=None):
"""
Genere un texte a differentes vitesses et compare les metriques.
Parametres :
text (str) : texte a synthetiser
voice (str) : voix Kokoro
speeds (list) : liste des vitesses a tester (defaut: [0.5, 0.75, 1.0, 1.25, 1.5])
Retourne :
dict : resultats par vitesse, meilleure vitesse identifiee
"""
if speeds is None:
speeds = [0.5, 0.75, 1.0, 1.25, 1.5]
# TODO etudiant : implementer le benchmark
results = {}
for speed in speeds:
# Etape 1 : Generer l'audio avec cette vitesse
# Indice : kokoro.create(text, voice=voice, speed=speed)
samples, sample_rate = None, None
# Etape 2 : Calculer les metriques
# Indice : duration = len(samples) / sample_rate
gen_time = None # mesurer avec time.time()
duration = None
results[speed] = {
"duration": duration,
"gen_time": gen_time,
"samples": samples,
"sample_rate": sample_rate,
"ratio": None # duration / gen_time
}
# Etape 3 : Sauvegarder le fichier pour ecoute
# Indice : OUTPUT_DIR / f"speed_{speed}_{voice}.wav"
# Etape 4 : Identifier la meilleure vitesse (ratio le plus eleve)
best_speed = None
return {"results": results, "best_speed": best_speed}
# Test avec un texte court
test_text = "The quick brown fox jumps over the lazy dog. This sentence contains every letter of the alphabet."
speed_result = benchmark_speed(test_text, voice="af_heart")
print("Exercice a completer")Exercice a completer
Section 3 : Voix et langues
Kokoro propose plusieurs voix organisees par langue :
| Prefixe | Langue | Exemples de voix |
|---|---|---|
af_ |
Anglais (feminin) | af_heart, af_bella, af_sarah |
am_ |
Anglais (masculin) | am_adam, am_michael |
bf_ |
Anglais britannique (f) | bf_emma, bf_isabella |
bm_ |
Anglais britannique (m) | bm_george, bm_lewis |
ff_ |
Francais (f) | ff_siwis |
fm_ |
Francais (m) | (a venir) |
Introduction : Exploration des voix
Kokoro propose une bibliotheque de voix pre-entraines couvrant plusieurs langues et profils vocaux.
Dans cette section, nous allons : 1. Tester différentes voix anglophones (af_, am_, bf_, bm_) 2. Explorer les voix francophones (ff_) 3. Mesurer les différences de performance entre voix 4. Comparer les qualites perceptuelles
Cette experimentation permet de sélectionner la voix adequate pour chaque scénario : narration, assistant virtuel, personnages, accessibilite.
# Test de differentes voix
print("TEST DES VOIX")
print("=" * 45)
if kokoro_loaded and test_multiple_voices:
voices_to_test = [
("af_heart", "Hello, I am the Heart voice from Kokoro."),
("af_bella", "Hello, I am the Bella voice from Kokoro."),
("am_adam", "Hello, I am the Adam voice from Kokoro."),
("ff_siwis", "Bonjour, je suis la voix Siwis de Kokoro."),
]
voice_results = {}
for v, text in voices_to_test:
print(f"\nVoix : {v}")
try:
start_time = time.time()
if use_onnx:
samples, sample_rate = kokoro.create(text, voice=v, speed=1.0)
else:
generator = pipeline(text, voice=v, speed=1.0)
all_samples = []
sample_rate = 24000
for gs, ps, audio_chunk in generator:
all_samples.append(audio_chunk)
samples = np.concatenate(all_samples) if all_samples else np.array([])
gen_time = time.time() - start_time
duration = len(samples) / sample_rate
voice_results[v] = {
"duration": duration,
"gen_time": gen_time,
"samples": samples,
"sample_rate": sample_rate
}
print(f" Duree : {duration:.1f}s | Temps : {gen_time:.2f}s | Ratio : {duration/gen_time:.1f}x")
display_audio_array(samples, sample_rate)
if save_results:
filepath = OUTPUT_DIR / f"voice_{v}.wav"
sf.write(str(filepath), samples, sample_rate)
except Exception as e:
print(f" Erreur : {str(e)[:80]}")
# Tableau recapitulatif
if voice_results:
print(f"\nRecapitulatif :")
print(f"{'Voix':<15} {'Duree':<10} {'Temps gen':<12} {'Ratio':<8}")
print("-" * 45)
for v, data in voice_results.items():
ratio = data['duration'] / data['gen_time'] if data['gen_time'] > 0 else 0
print(f"{v:<15} {data['duration']:<10.1f} {data['gen_time']:<12.2f} {ratio:<8.1f}")
else:
print("Test des voix desactive ou modele non charge")TEST DES VOIX
=============================================
Voix : af_heart
Duree : 2.3s | Temps : 1.15s | Ratio : 2.0x
Voix : af_bella
Duree : 2.5s | Temps : 1.45s | Ratio : 1.7x
Voix : am_adam
Duree : 2.4s | Temps : 1.63s | Ratio : 1.5x
Voix : ff_siwis
Duree : 2.8s | Temps : 1.40s | Ratio : 2.0x
Recapitulatif :
Voix Duree Temps gen Ratio
---------------------------------------------
af_heart 2.3 1.15 2.0
af_bella 2.5 1.45 1.7
am_adam 2.4 1.63 1.5
ff_siwis 2.8 1.40 2.0
Interpretation : Test des voix
Les différentes voix Kokoro offrent des caractéristiques distinctes adaptées a divers cas d’usage.
| Voix | Genre | Langue | Profil |
|---|---|---|---|
af_heart |
Feminin | Anglais (US) | Douce, chaleureuse |
af_bella |
Feminin | Anglais (US) | Claire, professionnelle |
am_adam |
Masculin | Anglais (US) | Neutre, informative |
ff_siwis |
Feminin | Francais | Accent francophone |
Observations : - Le ratio temps reel (generation/duree) varie selon la voix (complexite prosodique) - Les voix francaises sont plus limitees que les voix anglaises - La qualite audio reste stable quel que soit le choix de voix
Note technique : Pour la production, il est recommande de tester plusieurs voix et de sélectionner celle qui correspond le mieux au ton du contenu (narration, assistant, personnages).
Section 4 : Comparaison avec OpenAI TTS
Comparaison directe entre Kokoro (local, gratuit) et OpenAI TTS (cloud, payant) sur les mêmes textes.
Critères d’evaluation
| Critere | Description |
|---|---|
| Latence | Temps entre la requête et le debut de l’audio |
| Naturalite | Ressemblance avec la voix humaine |
| Prosodie | Rythme, intonation, accentuation |
| Cout | Prix par caractère synthetise |
# Comparaison Kokoro vs OpenAI TTS
print("COMPARAISON KOKORO VS OPENAI TTS")
print("=" * 45)
comparison_text = (
"L'intelligence artificielle generative transforme la facon "
"dont nous creons et consommons du contenu audio."
)
comparison_results = {}
# --- Kokoro ---
if kokoro_loaded:
print(f"\n--- Kokoro ({voice}) ---")
start_time = time.time()
if use_onnx:
kokoro_samples, kokoro_sr = kokoro.create(comparison_text, voice=voice, speed=1.0)
else:
generator = pipeline(comparison_text, voice=voice, speed=1.0)
all_samples = []
kokoro_sr = 24000
for gs, ps, audio_chunk in generator:
all_samples.append(audio_chunk)
kokoro_samples = np.concatenate(all_samples) if all_samples else np.array([])
kokoro_time = time.time() - start_time
kokoro_duration = len(kokoro_samples) / kokoro_sr
comparison_results["Kokoro"] = {
"time": kokoro_time,
"duration": kokoro_duration,
"cost": 0.0
}
print(f" Temps : {kokoro_time:.2f}s")
print(f" Duree audio : {kokoro_duration:.1f}s")
print(f" Cout : $0.00")
display_audio_array(kokoro_samples, kokoro_sr)
# --- OpenAI TTS ---
if compare_with_openai:
openai_key = os.environ.get('OPENAI_API_KEY')
if openai_key:
from openai import OpenAI
client_api = OpenAI(api_key=openai_key)
for tts_model in ["tts-1", "tts-1-hd"]:
print(f"\n--- OpenAI {tts_model} (alloy) ---")
try:
start_time = time.time()
response = client_api.audio.speech.create(
model=tts_model,
voice="alloy",
input=comparison_text,
response_format="wav"
)
openai_time = time.time() - start_time
openai_bytes = response.content
# Charger pour connaitre la duree
import io
openai_data, openai_sr = sf.read(io.BytesIO(openai_bytes))
openai_duration = len(openai_data) / openai_sr
cost_per_char = 0.015 if tts_model == "tts-1" else 0.030
cost = len(comparison_text) * cost_per_char / 1000
comparison_results[f"OpenAI {tts_model}"] = {
"time": openai_time,
"duration": openai_duration,
"cost": cost
}
print(f" Temps : {openai_time:.2f}s")
print(f" Duree audio : {openai_duration:.1f}s")
print(f" Cout : ${cost:.5f}")
display_audio_bundle(openai_bytes)
except Exception as e:
print(f" API indisponible: {type(e).__name__}: {str(e)[:100]}")
print(" Comparaison partielle (local uniquement)")
else:
print(" OpenAI API key non configuree - comparaison skippee")
# --- Tableau comparatif ---
if comparison_results:
print(f"\n{'='*60}")
print(f"TABLEAU COMPARATIF")
print(f"{'='*60}")
print(f"{'Service':<22} {'Latence (s)':<14} {'Duree (s)':<12} {'Cout ($)':<10}")
print("-" * 58)
for name, data in comparison_results.items():
print(f"{name:<22} {data['time']:<14.2f} {data['duration']:<12.1f} {data['cost']:<10.5f}")
# Estimation pour 1 heure de narration (~50K caracteres)
chars_1h = 50000
print(f"\nEstimation pour 1 heure de narration (~{chars_1h:,} caracteres) :")
print(f" Kokoro : $0.00")
print(f" OpenAI tts-1 : ${chars_1h * 0.015 / 1000:.2f}")
print(f" OpenAI tts-1-hd : ${chars_1h * 0.030 / 1000:.2f}")
else:
print("\nAucun resultat de comparaison disponible (ni local ni API)")COMPARAISON KOKORO VS OPENAI TTS
=============================================
--- Kokoro (af_heart) ---
Temps : 3.22s
Duree audio : 7.7s
Cout : $0.00
--- OpenAI tts-1 (alloy) ---
Temps : 2.14s
Duree audio : 6.1s
Cout : $0.00161
--- OpenAI tts-1-hd (alloy) ---
Temps : 2.35s
Duree audio : 6.8s
Cout : $0.00321
============================================================
TABLEAU COMPARATIF
============================================================
Service Latence (s) Duree (s) Cout ($)
----------------------------------------------------------
Kokoro 3.22 7.7 0.00000
OpenAI tts-1 2.14 6.1 0.00161
OpenAI tts-1-hd 2.35 6.8 0.00321
Estimation pour 1 heure de narration (~50,000 caracteres) :
Kokoro : $0.00
OpenAI tts-1 : $0.75
OpenAI tts-1-hd : $1.50
Exercice 2 : Pipeline de lecture TTS batch avec gestion de file d’attente
Contexte : Vous devez convertir un ensemble d’articles (titres et resumes) en fichiers audio pour un flux de type “podcast actualites”. Chaque article doit etre synthetise avec une voix coherente, et les fichiers doivent etre nommes de maniere systématique.
Objectif : Créer une fonction batch_tts_pipeline() qui prend une liste de textes, les synthetise un par un avec Kokoro, et sauvegarde chaque fichier audio avec un nom structure.
Étapes : 1. Définir la fonction avec les paramètres : articles (liste de dict {"id", "title", "summary"}), voice, output_dir 2. Pour chaque article, synthetiser le texte title + ". " + summary avec Kokoro 3. Sauvegarder chaque fichier sous le nom article_{id}_{voice}.wav 4. Collecter les metriques pour chaque article (duree audio, temps de generation, ratio) 5. Retourner un resume complet : nombre d’articles traites, duree totale, temps total
Indices : - Pour le backend ONNX : kokoro.create(text, voice=voice, speed=1.0) retourne (samples, sample_rate) - Pour le backend standard : iterer sur pipeline(text, voice=voice) et concatener les chunks - La duree audio = len(samples) / sample_rate - Gerer les erreurs par article avec try/except (un article qui echoue ne doit pas arreter le pipeline)
def batch_tts_pipeline(articles, voice="af_heart", output_dir=None):
"""
Synthetise un batch d'articles en fichiers audio.
Parametres :
articles (list) : liste de dicts {"id", "title", "summary"}
voice (str) : voix Kokoro a utiliser
output_dir (Path) : repertoire de sortie
Retourne :
dict : resume du batch (nb_articles, duree_totale, temps_total, erreurs)
"""
# TODO etudiant : implementer le pipeline batch
# Etape 1 : Initialiser les compteurs
total_duration = 0
total_gen_time = 0
errors = []
# Etape 2 : Iterer sur les articles
for article in articles:
# Indice : construire le texte complet = title + ". " + summary
text = None
# Etape 3 : Generer l'audio avec Kokoro
# Indice : kokoro.create(text, voice=voice, speed=1.0) pour ONNX
samples, sample_rate = None, None
# Etape 4 : Sauvegarder le fichier
# Indice : sf.write(str(filepath), samples, sample_rate)
# Nom : article_{id}_{voice}.wav
# Etape 5 : Collecter les metriques
pass
# Retourner le resume
return {
"nb_articles": len(articles),
"total_duration": total_duration,
"total_gen_time": total_gen_time,
"errors": errors
}
# Donnees de test
test_articles = [
{"id": 1, "title": "IA et sante", "summary": "L'intelligence artificielle revolutionne le diagnostic medical."},
{"id": 2, "title": "Voitures autonomes", "summary": "Les vehicules autonomes progressent rapidement sur les routes europeennes."},
{"id": 3, "title": "Quantique", "summary": "Les ordinateurs quantiques promettent des avancees majeures en cryptographie."},
]
# Test du pipeline
output_dir = OUTPUT_DIR
result = batch_tts_pipeline(test_articles, voice="af_heart", output_dir=OUTPUT_DIR)
print("Exercice a completer")Exercice a completer
Interpretation : Comparaison Kokoro vs OpenAI
| Critere | Kokoro | OpenAI tts-1 | OpenAI tts-1-hd |
|---|---|---|---|
| Latence | runtime machine-dep : ~5.3s (observe, CPU, RTF 2.0x ; ~0.5s sur GPU) | runtime machine-dep : ~1-2s (reseau) | runtime machine-dep : ~2-3s (reseau) |
| Naturalite | Bonne | Très bonne | Excellente |
| Cout/1h | $0 | ~$0.75 | ~$1.50 |
| Confidentialite | Locale | Cloud | Cloud |
Points cles : 1. Kokoro est imbattable sur le cout pour les gros volumes 2. OpenAI offre une meilleure qualite perceptuelle, surtout en tts-1-hd 3. Pour les données sensibles, Kokoro est le seul choix viable 4. En termes de latence, Kokoro avec GPU est competitif
# Mode interactif
if notebook_mode == "interactive" and not skip_widgets:
print("MODE INTERACTIF")
print("=" * 50)
print("\nEntrez un texte a synthetiser avec Kokoro :")
print("(Laissez vide pour passer a la suite)")
try:
user_text = input("\nVotre texte : ")
if user_text.strip() and kokoro_loaded:
user_voice = input(f"Voix [{voice}] : ").strip() or voice
print(f"\nGeneration avec Kokoro ({user_voice})...")
start_time = time.time()
if use_onnx:
samples, sr_out = kokoro.create(user_text, voice=user_voice, speed=1.0)
else:
generator = pipeline(user_text, voice=user_voice, speed=1.0)
all_samples = []
sr_out = 24000
for gs, ps, audio_chunk in generator:
all_samples.append(audio_chunk)
samples = np.concatenate(all_samples) if all_samples else np.array([])
gen_time = time.time() - start_time
print(f"Duree : {len(samples)/sr_out:.1f}s | Temps : {gen_time:.2f}s")
display_audio_array(samples, sr_out)
if save_results:
ts = datetime.now().strftime('%Y%m%d_%H%M%S')
filepath = OUTPUT_DIR / f"custom_{user_voice}_{ts}.wav"
sf.write(str(filepath), samples, sr_out)
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
==================================================
Entrez un texte a synthetiser avec Kokoro :
(Laissez vide pour passer a la suite)
Mode interactif non disponible (execution automatisee)
Section 5 : Mode interactif
Cette section offre une interface interactive pour experimenter avec Kokoro TTS en temps reel.
Fonctionnalite
En mode interactif, vous pouvez : - Saisir n’importe quel texte a synthetiser - Choisir parmi les voix disponibles - Obtenir immediatement l’audio genere - Sauvegarder les résultats avec horodatage
Contraintes d’exécution
| Mode | Comportement |
|---|---|
| Interactive | Invite de commande input() pour saisie utilisateur |
| Batch | Desactive automatiquement (skip_widgets=True) |
| MCP/Papermill | Desactive (stdin non disponible) |
Note : En mode batch, cette section est automatiquement sautee pour ne pas bloquer l’exécution.
Bonnes pratiques et guide de decision
Arbre de decision : local vs cloud
| Question | Oui -> | Non -> |
|---|---|---|
| Données sensibles ? | Local (Kokoro) | Continuer |
| Budget limite ? | Local (Kokoro) | Continuer |
| Qualite maximale requise ? | Cloud (OpenAI HD) | Continuer |
| GPU disponible ? | Local (Kokoro) | Cloud (OpenAI tts-1) |
| Volume > 1M chars/mois ? | Local (Kokoro) | Cloud (OpenAI tts-1) |
Optimisation Kokoro
| Technique | Impact | Description |
|---|---|---|
| Backend ONNX | Vitesse +30-50% | Inference optimisee sans PyTorch |
| Batch generation | Debit +50% | Regrouper les textes courts |
| GPU | Vitesse 5-10x vs CPU | Recommande pour la production |
# 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 : {kokoro_model}")
print(f"Backend : {'ONNX' if use_onnx else 'PyTorch'}")
print(f"Voix par defaut : {voice}")
print(f"Modele charge : {'Oui' if kokoro_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}")
# Liberation memoire
if kokoro_loaded:
print(f"\nLiberation du modele...")
if use_onnx:
del kokoro
else:
del pipeline
gc.collect()
if gpu_available:
torch.cuda.empty_cache()
print(f"Memoire liberee")
print(f"\nPROCHAINES ETAPES")
print(f"1. Explorer le TTS avance avec Chatterbox (02-1)")
print(f"2. Decouvrir le voice cloning avec XTTS (02-2)")
print(f"3. Comparer tous les modeles TTS et STT (03-1)")
print(f"4. Construire un pipeline vocal complet (03-2)")
print(f"\nNotebook Kokoro TTS termine - {datetime.now().strftime('%H:%M:%S')}")STATISTIQUES DE SESSION
=============================================
Date : 2026-08-19 13:27:47
Modele : hexgrad/Kokoro-82M
Backend : ONNX
Voix par defaut : af_heart
Modele charge : Oui
VRAM utilisee : 0.00 GB
Fichiers sauvegardes : 5 (0.9 MB) dans <USER_PATH>\AppData\Local\Temp\claude\d--CoursIA\0c3b8a34-33b8-494d-b9c5-7a23bec864f1\scratchpad\wt11443\MyIA.AI.Notebooks\GenAI\outputs\audio\kokoro
Liberation du modele...
Memoire liberee
PROCHAINES ETAPES
1. Explorer le TTS avance avec Chatterbox (02-1)
2. Decouvrir le voice cloning avec XTTS (02-2)
3. Comparer tous les modeles TTS et STT (03-1)
4. Construire un pipeline vocal complet (03-2)
Notebook Kokoro TTS termine - 13:27:47
Interpretation : Statistiques de session
L’exécution de ce notebook a permis de mesurer les performances de Kokoro TTS dans différentes configurations.
| Metrique | Observation |
|---|---|
| Chargement modèle | Première exécution plus lente (JIT compilation) |
| Generation | runtime machine-dep : Ratio temps reel 2.0x (observe, CPU) ; 5-20x attendu sur GPU |
| VRAM | 0.00 GB (observe, CPU) ; ~2 GB sur GPU pour le modèle 82M |
| Fichiers | Sauvegardes dans outputs/audio/kokoro/ |
Note technique : La liberation explicite de la memoire (
del,gc.collect(),torch.cuda.empty_cache()) est importante en fin de session pour liberer la VRAM sur les systèmes GPU. ***
Exercice 3 : Synthese Audio Multi-Voix avec Dialogue
Duree estimee : 15-20 minutes
Objectif
Créer un système de generation de dialogue synthetique en utilisant différentes voix de Kokoro, avec alternance automatique des interlocuteurs et gestion des pauses naturelles.
Contexte
Dans la Section 3, vous avez teste différentes voix Kokoro (af_heart, af_bella, am_adam, ff_siwis).
Dans cet exercice, vous allez combiner ces voix pour créer un dialogue realiste entre : - Speaker A : Voix feminine anglaise (ex: af_heart) - Speaker B : Voix masculine anglaise (ex: am_adam)
Cette technique est utilisee pour : - Generation de podcasts automatiques - Livres audio avec personnages multiples - Chatbots vocaux avec personnalites distinctes
Étapes : 1. Définir un dialogue avec 4-6 interventions alternées (A-B-A-B…) 2. Pour chaque ligne de dialogue : - Generer l’audio avec Kokoro en utilisant la voix appropriee - Ajouter une pause naturelle entre les interventions (ex: 500ms de silence) 3. Concatener tous les segments audio dans l’ordre 4. Sauvegarder le résultat et afficher les statistiques (duree totale, nombre de tours)
Indices : - Pour generer du silence : numpy.zeros(int(sample_rate * duration)) - Pour concatener : numpy.concatenate([audio1, silence, audio2, ...]) - Pour l’API ONNX : kokoro.create(text, voice=voice, speed=1.0) retourne (samples, sample_rate) - Pour l’API standard : iterer sur le generator et concatener les chunks - Le sample rate Kokoro est généralement 24000 Hz
Conclusion
Ce notebook a explore les capacites de Kokoro TTS, une solution de synthese vocale legere et open-source.
Synthese des apprentissages
| Aspect | Kokoro | OpenAI TTS |
|---|---|---|
| Cout | Gratuit (local) | $0.75-1.50/heure |
| Qualite | Bonne (MOS ~4.0) | Excellente (MOS ~4.5) |
| Latence | 0.5-1s (GPU) | 1-3s (reseau) |
| Confidentialite | Locale | Cloud |
| Volume | Illimite | Payant |
Points cles a retenir
- Kokoro est ideal pour : prototypage, données sensibles, gros volumes, environnements sans connexion
- Openai TTS est preferable pour : qualite maximale, production web, voix les plus naturelles
- L’architecture ONNX offre un bon compromis performance/vitesse sur GPU
- Le pipeline TTS suit toujours : texte → modèle → audio (numpy array + sample rate)
Prochaines étapes
- Explorer le TTS avance avec Chatterbox (02-1)
- Decouvrir le voice cloning avec XTTS (02-2)
- Construire un pipeline vocal complet (03-2)
Section 6 : Pour aller plus loin - la limite du TTS ultra-leger (Inflect-Nano)
Kokoro (82 millions de paramètres) est déjà considere comme un modèle TTS “leger”. Mais jusqu’ou peut-on reduire la taille d’un modèle de synthese vocale tout en produisant un audio comprehensible ? Inflect-Nano-v1 (owensong, licence Apache-2.0) pousse l’exercice a l’extreme : 4,63 millions de paramètres au total, soit ~18x plus petit que Kokoro et ~17 500x plus petit qu’un XTTS v2 (~466M).
Architecture
Inflect-Nano decompose le TTS en deux reseaux non-autoregressifs (style FastSpeech), chacun deliberately minuscule :
| Composant | Rôle | Paramètres |
|---|---|---|
Acoustique (MicroFastSpeech) |
Texte -> spectrogramme mel (80 bins) | 3,47 M |
Vocodeur (HifiGan Snake) |
Spectrogramme mel -> forme d’onde 24 kHz | 1,17 M |
| Total | 4,63 M |
Le pipeline complet : texte -> front-end G2P anglais -> acoustic -> mel -> vocodeur -> 24 kHz. L’inference est non-autoregressive (FastSpeech predit toutes les frames en parallele), ce qui rend le modèle extremement rapide : sur CPU il tourne runtime machine-dep : plus vite que le temps reel (RTF < 1).
Pourquoi s’y interesser ?
Ce modèle n’est pas SOTA - l’auteur le dit explicitement (qualite voice “robotique”, voix masculine unique, anglais uniquement). Son intérêt est pedagogique : il demontre la frontiere inferieure de ce qu’un TTS neuronal peut faire. C’est le pendant du “perceptron minimal” pour la synthese vocale. Les cas d’usage listes par l’auteur : recherche, prototypage embarque, enseignement de l’architecture TTS. Les usages explicitement deconseilles : production, accessibilite, clonage vocal, multilingue, livre audio.
Comparaison avec Kokoro
| Critere | Kokoro (82M) | Inflect-Nano (4,63M) |
|---|---|---|
| Taille | ~2 GB (avec voix) | ~49 MB total |
| Paramètres | 82 000 000 | 4 632 202 |
| Ratio | reference (1x) | ~18x plus petit |
| Langues | FR, EN, ES, IT, ZH, JP… | Anglais uniquement |
| Voix | 54+ voix (H/F) | 1 voix masculine |
| Architecture | Style StyleTTS2 + plBERT | FastSpeech + HiFi-GAN Snake |
| Qualite | MOS ~4.0 (naturelle) | Robotique, demonstratif |
| VRAM | ~2 GB GPU | 0 (CPU pur) |
| Cas d’usage | Production leger, multi-langue | Recherche, enseignement, embarque |
A retenir : la taille n’est pas l’unique dimension. Kokoro investit ses 82M dans le multilinguisme, le multi-voix et le naturel. Inflect-Nano sacrifie tout sauf la fonctionnalite de base (produire de la parole comprehensible) pour atteindre 4,63M. Les deux sont “petits” mais pour des objectifs opposes.
# Demonstration Inflect-Nano-v1 : TTS ultra-leger (4,63M parametres)
print("INFLECT-NANO-V1 - TTS ULTRA-LEGER")
print("=" * 45)
inflect_loaded = False
inflect_samples = None
inflect_sample_rate = 24000
inflect_total_params = 4_632_202 # documente par l'auteur (3,47M acoustic + 1,17M vocoder)
try:
from huggingface_hub import snapshot_download
import torch, math
# Telechargement du snapshot HF (deja en cache si deja telecharge)
print("Recuperation du modele Inflect-Nano-v1...")
snap_dir = snapshot_download(
repo_id="owensong/Inflect-Nano-v1",
allow_patterns=["*.py", "weights/*", "third_party/*"],
)
snap = Path(snap_dir)
sys.path.insert(0, str(snap))
sys.path.insert(0, str(snap / "third_party" / "tiny_tts_frontend"))
# Imports (apres path setup)
from tiny_tts.nn import commons
from tiny_tts.text import phonemes_to_ids
from tiny_tts.text.english import grapheme_to_phoneme, normalize_text
from tiny_tts.utils import ADD_BLANK
from inflect_nano.text_cleaning import clean_tinytts_text
from inflect_nano.vocoder import HifiGanGenerator, make_config
from inflect_nano.acoustic import MicroFastSpeech, MicroFastSpeechConfig
device = torch.device("cuda" if gpu_available else "cpu")
print(f"Backend : {device}")
# Chargement acoustic
ac = torch.load(snap / "weights" / "inflect_nano_v1_acoustic.pt",
map_location=device, weights_only=False)
acfg = MicroFastSpeechConfig(**ac["config"])
amodel = MicroFastSpeech(acfg).to(device); amodel.load_state_dict(ac["model"]); amodel.eval()
acoustic_params = int(ac.get("params") or sum(p.numel() for p in amodel.parameters()))
# Chargement vocoder
vc = torch.load(snap / "weights" / "inflect_nano_v1_vocoder.pt",
map_location=device, weights_only=False)
vcfg = make_config((vc.get("config") or {}).get("variant", "snake_v2mid"))
vmodel = HifiGanGenerator(vcfg).to(device); vmodel.load_state_dict(vc["generator"])
vmodel.remove_weight_norm(); vmodel.eval()
vocoder_params = int(vc.get("generator_params") or sum(p.numel() for p in vmodel.parameters()))
speakers = ac.get("speakers") or {"mark": 0}
measured_total = acoustic_params + vocoder_params
print(f"Params : acoustic={acoustic_params:,} vocoder={vocoder_params:,} total={measured_total:,}")
assert measured_total == inflect_total_params, f"Ecart de compte: {measured_total} vs {inflect_total_params}"
inflect_loaded = True
# Generation (texte anglais - Inflect-Nano ne supporte que l'anglais)
inflect_text = (
"Kokoro weighs eighty two million parameters. "
"Inflect Nano uses only four point six million."
)
print(f"Texte (anglais) : {inflect_text}")
def _text_to_tokens(text):
cleaned = clean_tinytts_text(text); normalized = normalize_text(cleaned)
phones, tones, _ = grapheme_to_phoneme(normalized)
pid, tid, lid = phonemes_to_ids(phones, tones, "EN")
if ADD_BLANK:
pid = commons.insert_blanks(pid, 0); tid = commons.insert_blanks(tid, 0); lid = commons.insert_blanks(lid, 0)
return (torch.LongTensor(pid), torch.LongTensor(tid), torch.LongTensor(lid))
phone, tone, lang = _text_to_tokens(inflect_text)
phone = phone.unsqueeze(0).to(device); tone = tone.unsqueeze(0).to(device); lang = lang.unsqueeze(0).to(device)
speaker = torch.LongTensor([int(speakers.get("mark", 0))]).to(device)
t0 = time.time()
with torch.inference_mode():
mel = amodel.infer(phone, tone, lang, speaker, length_scale=1.0, pitch_scale=1.0, energy_scale=1.0)
inflect_samples = vmodel(mel).squeeze().detach().cpu().numpy()
gen_time = time.time() - t0
# Normalisation (replication de inference.py officiel)
def _rms_db(a): return 20.0 * math.log10(float(np.sqrt(np.mean(a**2, dtype=np.float64))) + 1e-9)
inflect_samples = np.asarray(inflect_samples, dtype=np.float32).reshape(-1)
inflect_samples = inflect_samples - float(inflect_samples.mean())
inflect_samples *= 10 ** ((-20.0 - _rms_db(inflect_samples)) / 20.0)
pk = float(np.max(np.abs(inflect_samples)) + 1e-9); lim = 10 ** (-1.0 / 20.0)
if pk > lim: inflect_samples *= lim / pk
inflect_samples = np.clip(inflect_samples, -1.0, 1.0)
duration = len(inflect_samples) / inflect_sample_rate
print(f"\nGeneration reussie")
print(f" Duree audio : {duration:.2f}s")
print(f" Temps CPU : {gen_time:.2f}s (RTF = {gen_time/duration:.3f}, < 1 = plus rapide que le temps reel)")
print(f"\nEcoute (notez le caractere robotique, voix masculine unique) :")
display_audio_array(inflect_samples, inflect_sample_rate)
if save_results:
out = OUTPUT_DIR / "inflect_nano_demo.wav"
sf.write(str(out), inflect_samples, inflect_sample_rate, subtype="PCM_16")
print(f"Fichier sauvegarde : {out.name}")
except ImportError as e:
print(f"Dependance manquante ({e}). Requiert : torch, soundfile, g2p_en, huggingface_hub.")
print("Inflect-Nano n'est pas installe - demonstration sautee (graceful).")
except Exception as e:
print(f"Inflect-Nano indisponible sur cette machine : {str(e)[:120]}")
print("Demonstration sautee - voir l'output de reference dans le notebook version main.")INFLECT-NANO-V1 - TTS ULTRA-LEGER
=============================================
Recuperation du modele Inflect-Nano-v1...
Backend : cuda
Params : acoustic=3,465,125 vocoder=1,167,077 total=4,632,202
Texte (anglais) : Kokoro weighs eighty two million parameters. Inflect Nano uses only four point six million.
Generation reussie
Duree audio : 4.49s
Temps CPU : 1.05s (RTF = 0.234, < 1 = plus rapide que le temps reel)
Ecoute (notez le caractere robotique, voix masculine unique) :
Fichier sauvegarde : inflect_nano_demo.wav
Interpretation : le compromis taille vs qualite
L’exécution locale (CPU, RTF < 1) confirme les deux proprietes cles d’Inflect-Nano :
Vitesse extreme : grace a l’architecture non-autoregressive (FastSpeech), toutes les frames mel sont predites en parallele. Sur CPU, runtime machine-dep : la generation est plus rapide que la lecture - un cas rare pour un TTS neuronal.
Taille minimale : 4,63M de paramètres tiennent dans ~49 MB. Aucun GPU requis. Ce profil correspond aux contraintes embarquees (IoT, edge) ou la VRAM et l’energie sont budget nul.
Mais le cout est visible : la voix est manifestement robotique, monophone (pas de variation de timbre), limitee a l’anglais, et incapable de reproduire une identite vocale. La where Kokoro investit dans le naturel et la diversite, Inflect-Nano investit uniquement dans la fonctionnalite nue.
Ou se situe la frontiere ?
| Objectif prioritaire | Modèle recommande | Pourquoi |
|---|---|---|
| Production multi-langue, naturel | Kokoro (82M) ou XTTS | Compromis taille/qualite raisonnable |
| Qualite maximale, cout illimite | OpenAI TTS / FishAudio S2-Pro | API cloud, voix expressives |
| Enonce minimal, zero ressource | Inflect-Nano (4,63M) | Frontiere inferieure du TTS neuronal |
| Clonage vocal zero-shot | XTTS v2 | Non faisable en dessous de ~400M |
Lecon pedagogique : un TTS “fonctionnel” demande un minimum structural (acoustique + vocodeur, ~5M). En dessous, on produit encore du bruit structure mais plus de la parole. Inflect-Nano se place juste au-dessus de ce seuil, ce qui en fait un excellent cas d’étude pour comprendre l’anatomie minimale d’un système TTS. La serie continue vers Chatterbox (02-1) et XTTS (02-2) pour observer comment les ~460M supplementaires se traduisent en naturel, expressivite et clonage.
Source : modèle owensong/Inflect-Nano-v1 (Apache-2.0). Comparaison effectuee en exécution locale CPU.
import numpy as np
import soundfile as sf
# TODO: Definir le dialogue (alternance A-B)
# Indice : creer une liste de tuples (speaker, texte)
dialogue = [
("A", "Hello, welcome to this synthetic dialogue."),
("B", "Thank you, it's great to be here."),
("A", "Let's explore text-to-s synthesis together."),
("B", "I agree, this technology is fascinating."),
# Ajouter 1-2 interventions supplementaires
]
# TODO: Associer chaque speaker a une voix Kokoro
# Indice : utiliser les voix testees dans la Section 3
voices = {
"A": None, # ex: "af_heart"
"B": None # ex: "am_adam"
}
# TODO: Generer l'audio pour chaque ligne
audio_segments = []
pause_duration = 0.5 # 500ms de pause entre les interventions
for speaker, text in dialogue:
print(f"Generating {speaker}: {text[:30]}...")
# TODO: Generer l'audio avec Kokoro
# Indice: kokoro.create(text, voice=voices[speaker], speed=1.0) pour ONNX
# Ou: iterer sur pipeline(text, voice=voices[speaker]) pour standard
samples, sample_rate = None, None
# TODO: Ajouter le segment a la liste
audio_segments.append(samples)
# TODO: Ajouter une pause apres chaque intervention (sauf la derniere)
if speaker != dialogue[-1][0]:
# TODO: Generer du silence
silence = None # np.zeros(...)
audio_segments.append(silence)
# TODO: Concatener tous les segments
# Indice: np.concatenate(audio_segments)
full_dialogue_audio = None
# TODO: Calculer les statistiques
if full_dialogue_audio is None:
print("Dialogue non genere (exercice a completer ou service indisponible)")
print(f"Nombre de tours definis: {len(dialogue)}")
else:
total_duration = len(full_dialogue_audio) / sample_rate
num_turns = len(dialogue)
print(f"\nDialogue Statistics:")
print(f" Total duration: {total_duration:.1f}s")
print(f" Number of turns: {num_turns}")
print(f" Average turn length: {total_duration/num_turns:.1f}s")
# TODO: Sauvegarder le resultat
output_path = OUTPUT_DIR / "synthetic_dialogue.wav"
# sf.write(...)
print(f"\nSaved: {output_path.name}")
# TODO: Ecouter le resultat
from IPython.display import Audio
display_audio_array(full_dialogue_audio, sample_rate)Generating A: Hello, welcome to this synthet...
Generating B: Thank you, it's great to be he...
Generating A: Let's explore text-to-s synthe...
Generating B: I agree, this technology is fa...
Dialogue non genere (exercice a completer ou service indisponible)
Nombre de tours definis: 4
Critères de succes
Extensions (bonus)
Soumission : PR avec titre “Exercice #2 - [Votre Nom]” sur le repo GenAI_Series