# 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 TTS narration
tts_model = "tts-1" # "tts-1" ou "tts-1-hd"
narrator_voice = "nova" # Voix de narration
# Parametres video
video_resolution = (640, 480) # Resolution de la video de test
video_fps = 24 # Images par seconde
video_duration = 15 # Duree de la video de test (secondes)
# Configuration pipeline
generate_audio = True
save_output_files = True
background_music_volume = -20 # Volume musique de fond en dB (relatif)Synchronisation Audio-Video (Passerelle)
Module : 04-Audio-Applications
Niveau : Applications
Technologies : OpenAI TTS, moviepy, pydub, ffmpeg-python
VRAM estimee : ~10 GB
Duree estimee : 55 minutes
Objectifs d’Apprentissage
Prerequis
- Notebooks Foundation (01-1 TTS, 01-3 Audio Opérations) completes
- Notebook 04-2 (Pipeline de Transcription) pour les sous-titres
- Cle API OpenAI configuree (
OPENAI_API_KEYdans.env) - moviepy, pydub, ffmpeg installes
Note : Ce notebook est la passerelle entre la serie Audio et la serie Video. Il utilise des techniques audio pour enrichir du contenu video. La serie Video (
Video/01-Foundation/) approfondit la generation et l’edition video.
Navigation : Index | << Précédent | Serie Video >>
# Parameters
BATCH_MODE = "true"Configuration du pipeline
Ce notebook utilise des paramètres configurables pour s’adapter a différents scénarios d’exécution.
Modes d’exécution - interactive : Mode normal avec widgets et affichages - batch : Mode automatise (Papermill) sans interaction
Paramètres TTS - tts_model : Modèle de synthese vocale (tts-1 ou tts-1-hd) - narrator_voice : Voix utilisee pour la narration (nova, alloy, echo, fable, onyx, shimmer)
Paramètres video - video_resolution : Taille de la video (640x480 pour les tests) - video_fps : Images par seconde (24 standard, 30 pour video fluide) - video_duration : Duree totale en secondes (15s pour demo rapide)
Paramètres de mixage - background_music_volume : Volume relatif de la musique (-20dB standard)
Flags de contrôle - generate_audio : Active/desactive la generation TTS (pour tests rapides) - save_output_files : Sauvegarde les fichiers audio generes - skip_widgets : Desactive les widgets interactifs en mode batch
Ces paramètres permettent d’adapter le notebook a différents cas d’usage : - Prototype rapide : Duree courte, resolution basse - Production : Haute resolution, tts-1-hd, musique personnalisee - Test : generate_audio=False pour verifier le code sans appels API
L’environnement de travail est configure avec les paramètres Papermill. La cellule suivante charge les bibliotheques Python necessaires : traitement video, synthese audio TTS et utilitaires de synchronisation.
# Setup environnement et imports
import os
import sys
import json
import time
from pathlib import Path
from datetime import datetime, timedelta
from typing import Dict, List, Any, Optional, Tuple
from io import BytesIO
import logging
import numpy as np
from IPython.display import Audio, display, HTML, Video as IPVideo
# Resolution GENAI_ROOT
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 (
synthesize_openai, play_audio_bytes, display_audio_bundle,
estimate_audio_duration, get_audio_info
)
print("Helpers audio importes")
except ImportError as e:
print(f"Helpers audio non disponibles : {e}")
# Repertoires de sortie
OUTPUT_DIR = GENAI_ROOT / 'outputs' / 'audio' / 'av_sync'
OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
logging.basicConfig(level=getattr(logging, debug_level))
logger = logging.getLogger('av_sync')
print(f"Synchronisation Audio-Video")
print(f"Date : {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
print(f"Mode : {notebook_mode}, TTS : {tts_model}")
print(f"Sortie : {OUTPUT_DIR.relative_to(GENAI_ROOT)}")Helpers audio importes
Synchronisation Audio-Video
Date : 2026-08-16 01:50:59
Mode : interactive, TTS : tts-1
Sortie : outputs\audio\av_sync
Verification de l’environnement
Le notebook necessite plusieurs dependances pour fonctionner correctement. Verifions leur disponibilite.
Bibliotheques requises
| Bibliotheque | Usage | Installation |
|---|---|---|
moviepy |
Manipulation video | pip install moviepy |
pydub |
Manipulation audio | pip install pydub |
ffmpeg-python |
Encodage multimedia | pip install ffmpeg-python |
openai |
API TTS | pip install openai |
dotenv |
Configuration .env | pip install python-dotenv |
Système externe - ffmpeg : Encodeur video (requis par moviepy et pydub) - Windows : choco install ffmpeg - Linux : sudo apt install ffmpeg - macOS : brew install ffmpeg
Verification API La cle OpenAI est chargee depuis le fichier .env a la racine du dossier GenAI. Sans cette cle, la generation TTS ne fonctionnera pas.
Helpers audio Des fonctions utilitaires sont importees depuis shared/helpers/audio_helpers : - synthesize_openai() : Generation TTS via API OpenAI - play_audio_bytes() : Lecture audio dans le notebook - estimate_audio_duration() : Estimation de la duree - get_audio_info() : Metadonnees audio
Si les helpers ne sont pas disponibles, le notebook fonctionnera quand même avec des implementations de secours.
Les modules Python sont importes. Cette cellule charge les cles API depuis le fichier .env en remontant l’arborescence de repertoires pour garantir la compatibilite avec les différents environnements d’exécution.
# 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}")
# Configuration API OpenAI (pattern canonique de la serie : TTS ne passe pas par OpenRouter)
openai_key = os.environ.get('OPENAI_DIRECT_API_KEY') or os.environ.get('OPENAI_API_KEY', '')
openai_available = bool(openai_key and not openai_key.startswith('sk-or-'))
if openai_available:
print("Cle OpenAI directe valide - API disponible")
from openai import OpenAI
client = OpenAI(api_key=openai_key, base_url="https://api.openai.com/v1")
else:
print("OPENAI_DIRECT_API_KEY non disponible ou invalide (cle OpenRouter)")
openai_key = "dummy_key_for_validation"
client = None
# Verification des dependances video
moviepy_available = False
try:
from moviepy import VideoFileClip, AudioFileClip, concatenate_videoclips, ColorClip, TextClip, CompositeVideoClip
moviepy_available = True
print("moviepy disponible")
except ImportError:
print("moviepy non installe - pip install moviepy")
# pydub pour manipulation audio
try:
from pydub import AudioSegment
print("pydub disponible")
except ImportError:
print("pydub non installe")
print("\nConfiguration prete").env charge depuis: .env
Cle OpenAI directe valide - API disponible
moviepy disponible
pydub disponible
Configuration prete
Section 1 : Creation d’une video de test et extraction audio
Avant de travailler sur la synchronisation, nous creons une video de test simple et montrons comment extraire sa piste audio.
| Opération | Outil | Entree | Sortie |
|---|---|---|---|
| Creation video | moviepy | Paramètres | Fichier .mp4 |
| Extraction audio | moviepy / ffmpeg | Video .mp4 | Audio .wav/.mp3 |
| Analyse audio | pydub | Audio | Metadonnees |
Creation de contenu video avec moviepy
moviepy est une bibliotheque Python pour la manipulation video qui simplifie les opérations courantes : - Creation de clips a partir d’images ou de couleurs - Concatenation de plusieurs segments - Ajout de textes et titres - Export vers des formats standards (MP4, AVI, GIF)
Structure d’une video Une video est composee de : 1. Visuels : Images, couleurs, animations, textes 2. Audio : Voix, musique, effets sonores 3. Metadonnees : Resolution, FPS, codec, duree
Workflow de creation
1. Définir les segments (label, couleur, duree)
2. Créer un clip par segment (ColorClip + TextClip)
3. Concatener les segments (concatenate_videoclips)
4. Configurer les paramètres (FPS, resolution)
5. Exporter (write_videofile)
Extraction audio L’extraction de la piste audio d’une video existante se fait en une ligne :
from moviepy import VideoFileClip
video = VideoFileClip("video.mp4")
audio = video.audio # Extraire la piste audio
audio.write_audiofile("audio.wav") # SauvegarderCette opération est essentielle pour : - Transcrire une video (Whisper) - Remplacer l’audio (doublage) - Analyser la bande sonore
Le code suivant créé une video de test avec 3 segments colores.
# Creation d'une video de test et extraction audio
print("CREATION VIDEO DE TEST ET EXTRACTION AUDIO")
print("=" * 50)
test_video_path = OUTPUT_DIR / "test_video.mp4"
extracted_audio_path = OUTPUT_DIR / "extracted_audio.wav"
if generate_audio and moviepy_available:
# Creer une video de test avec des scenes colorees
print("Creation de la video de test...")
# Definir les segments de la video
video_segments = [
{"label": "Introduction", "color": (30, 60, 120), "duration": 5},
{"label": "Concepts cles", "color": (60, 120, 30), "duration": 5},
{"label": "Conclusion", "color": (120, 30, 60), "duration": 5}
]
clips = []
for seg in video_segments:
# Clip de couleur unie
color_clip = ColorClip(
size=video_resolution,
color=seg['color'],
duration=seg['duration']
)
# Ajouter un titre
try:
txt_clip = TextClip(
text=seg['label'],
font_size=40,
color='white'
).with_position('center').with_duration(seg['duration'])
clip = CompositeVideoClip([color_clip, txt_clip])
except Exception:
# Si TextClip echoue (ImageMagick non disponible), utiliser le clip brut
clip = color_clip
clips.append(clip)
# Concatener les clips
final_video = concatenate_videoclips(clips)
final_video = final_video.with_fps(video_fps)
# Sauvegarder la video
final_video.write_videofile(
str(test_video_path),
fps=video_fps,
codec='libx264',
audio=False,
logger=None
)
final_video.close()
print(f"Video creee : {test_video_path.name}")
print(f"Resolution : {video_resolution[0]}x{video_resolution[1]}")
print(f"Duree : {video_duration}s | FPS : {video_fps}")
print(f"Segments : {len(video_segments)}")
for i, seg in enumerate(video_segments):
start = sum(s['duration'] for s in video_segments[:i])
end = start + seg['duration']
print(f" [{start:2.0f}s - {end:2.0f}s] {seg['label']}")
# Demonstration de l'extraction audio
print(f"\n(La video de test n'a pas de piste audio - nous en ajouterons une)")
elif not moviepy_available:
print("moviepy non disponible - pip install moviepy")
else:
print("Generation desactivee")CREATION VIDEO DE TEST ET EXTRACTION AUDIO
==================================================
Creation de la video de test...
Video creee : test_video.mp4
Resolution : 640x480
Duree : 15s | FPS : 24
Segments : 3
[ 0s - 5s] Introduction
[ 5s - 10s] Concepts cles
[10s - 15s] Conclusion
(La video de test n'a pas de piste audio - nous en ajouterons une)
Interpretation : Video de test
| Segment | Debut | Fin | Contenu |
|---|---|---|---|
| Introduction | 0s | 5s | Fond bleu |
| Concepts cles | 5s | 10s | Fond vert |
| Conclusion | 10s | 15s | Fond rouge |
Points cles : 1. moviepy permet de créer et manipuler des videos en Python 2. L’extraction audio utilise la même API (video.audio) 3. La video de test servira de base pour la synchronisation
Exercice : Planificateur de segments video
Duree estimee : 15-20 minutes
Objectif
Créer une fonction qui planifie automatiquement les segments video et calcule la duree optimale du texte de narration pour chaque segment.
Contexte
Pour synchroniser audio et video, il faut que le texte de narration tienne dans la duree du segment. A 130 mots/min (vitesse standard), un segment de 5 secondes ne peut contenir que ~11 mots. Cette fonction aide a calibrer les scripts.
Instructions
- Définir une liste de segments video avec duree et label
- Calculer le nombre maximal de mots par segment
- Generer des suggestions de texte de narration adaptees
- Verifier que chaque texte respecte la contrainte de duree
Indices : -
# Étape 1: Vitesse standard = 2.17 mots/seconde, mots_max = duree * 2.17 * 0.85 (marge) -# Étape 2: Pour chaque segment, calculer le budget de mots -# Indice: Prevoir 15% de marge pour les pauses naturelles -# Indice: Si speed > 1.0 dans le TTS, le budget augmente proportionnellement
# TODO: Implementer le planificateur de segments video
def plan_video_segments(segments: list, words_per_sec: float = 2.17,
safety_margin: float = 0.85) -> list:
"""
Planifie les segments video et calcule le budget de mots pour chacun.
Args:
segments: Liste de dicts {"label": str, "duration": float}
words_per_sec: Vitesse de parole standard (mots/seconde)
safety_margin: Marge de securite (0.85 = 15% de marge)
Returns:
Liste enrichie avec max_words, suggested_text_length
"""
planned = []
for seg in segments:
# Etape 1 : Calculer le budget de mots
# Indice: max_words = int(seg["duration"] * words_per_sec * safety_margin)
max_words = 0 # TODO etudiant
# Etape 2 : Estimer la longueur de texte (en caracteres)
# Indice: longueur_moyenne_mot = 5 caracteres, + espaces
max_chars = 0 # TODO etudiant
planned.append({
"label": seg["label"],
"duration": seg["duration"],
"max_words": max_words,
"max_chars": max_chars,
"status": "Exercice a completer"
})
return planned
# Test avec des segments de demonstration
demo_segments = [
{"label": "Introduction", "duration": 5},
{"label": "Concepts cles", "duration": 8},
{"label": "Conclusion", "duration": 5},
]
# planned = plan_video_segments(demo_segments)
# print(f"{'Segment':<20} {'Duree':<8} {'Max mots':<10} {'Max chars':<10}")
# print("-" * 48)
# for p in planned:
# print(f"{p['label']:<20} {p['duration']:<8.0f} {p['max_words']:<10} {p['max_chars']:<10}")
print("Exercice a completer")Exercice a completer
Section 2 : Narration TTS synchronisee
Pour chaque segment video, nous generons une narration TTS adaptee. Le texte est calibre pour correspondre a la duree du segment.
| Segment | Duree cible | Texte adapte | Vitesse TTS |
|---|---|---|---|
| Introduction | 5s | Court, accueillant | 1.0 |
| Concepts cles | 5s | Dense, informatif | 0.95 |
| Conclusion | 5s | Recapitulatif | 1.0 |
Synthese vocale pour la narration
La narration TTS (Text-to-Speech) transforme des scripts textuels en audio parle. Pour une video, plusieurs contraintes s’appliquent.
Paramètres TTS clés
| Paramètre | Valeurs | Impact sur la video |
|---|---|---|
model |
tts-1, tts-1-hd | Qualite audio (hd = meilleure qualite) |
voice |
alloy, echo, fable, onyx, nova, shimmer | Ton, genre, style vocal |
speed |
0.25 a 4.0 | Duree audio (1.0 = normal, 2.0 = 2x plus rapide) |
response_format |
mp3, opus, aac, flac | Taille fichier et compatibilite |
Calcul de la duree cible Pour un segment de D secondes, la narration doit respecter : - Vitesse normale (130 mots/min) : ≈ 2.17 mots/seconde - Duree maximale : D secondes - Texte ideal : ≤ D × 2.17 mots
Exemple : Pour un segment de 5 secondes - Texte ideal : 5 × 2.17 ≈ 11 mots - Avec speed=1.5 : 5 × 2.17 × 1.5 ≈ 16 mots - Marge securite : retirer 10-15% pour les pauses
Stratégies d’adaptation
- Texte trop long : Reduire de 30% ou augmenter speed a 1.3-1.5x
- Texte trop court : Ajouter des transitions ou des exemples
- Voix inadaptee : Tester les 6 voix disponibles
- Qualite insuffisante : Passer a tts-1-hd (+20% de taille)
Le code genere des narrations pour chaque segment video en respectant les contraintes de timing.
# Generation de narration TTS synchronisee aux segments video
print("NARRATION TTS SYNCHRONISEE")
print("=" * 50)
# Scripts de narration pour chaque segment video
narration_scripts = [
{
"segment": "Introduction",
"start": 0, "end": 5,
"text": "Bienvenue dans cette presentation sur l'intelligence artificielle generative. Nous allons decouvrir ensemble les concepts fondamentaux.",
"speed": 1.0
},
{
"segment": "Concepts cles",
"start": 5, "end": 10,
"text": "Les modeles generatifs apprennent a creer du contenu nouveau. Ils analysent des exemples existants pour en deduire des patterns.",
"speed": 0.95
},
{
"segment": "Conclusion",
"start": 10, "end": 15,
"text": "En resume, l'IA generative ouvre de nouvelles possibilites creatives. Explorez les notebooks suivants pour aller plus loin.",
"speed": 1.0
}
]
narration_audio_segments = []
pydub_available = False
# Verifier si pydub/ffmpeg fonctionne
try:
test_audio = AudioSegment.silent(duration=100)
pydub_available = True
except Exception as e:
print(f"pydub/ffmpeg non disponible : {e}")
print("Utilisation de la duree estimee pour la synchronisation")
if generate_audio and openai_key != "dummy_key_for_validation":
print(f"Generation de la narration pour {len(narration_scripts)} segments :")
for script in narration_scripts:
print(f"\n--- {script['segment']} [{script['start']}s - {script['end']}s] ---")
print(f"Texte : {script['text'][:70]}...")
start_time = time.time()
response = client.audio.speech.create(
model=tts_model,
voice=narrator_voice,
input=script['text'],
response_format="mp3",
speed=script['speed']
)
audio_bytes = response.content
gen_time = time.time() - start_time
# Estimer la duree audio (approximation: 130 mots/min = 2.17 mots/sec)
word_count = len(script['text'].split())
estimated_duration = word_count / 2.17 / script['speed']
# Tenter de charger avec pydub si disponible
audio_seg = None
audio_duration = estimated_duration
if pydub_available:
try:
audio_seg = AudioSegment.from_mp3(BytesIO(audio_bytes))
audio_duration = len(audio_seg) / 1000 # ms -> s
except Exception as e:
print(f" pydub erreur, utilisation estimation : {e}")
segment_duration = script['end'] - script['start']
narration_audio_segments.append({
"segment": script['segment'],
"start": script['start'],
"end": script['end'],
"audio_bytes": audio_bytes,
"audio_segment": audio_seg,
"audio_duration": audio_duration,
"target_duration": segment_duration
})
fit_status = "OK" if audio_duration <= segment_duration else "DEPASSE"
print(f" Duree audio : {audio_duration:.1f}s / cible {segment_duration}s [{fit_status}]")
print(f" Taille : {len(audio_bytes)/1024:.1f} KB | Generation : {gen_time:.1f}s")
display_audio_bundle(audio_bytes)
# Recapitulatif
print(f"\nRecapitulatif synchronisation :")
print(f"{'Segment':<18} {'Audio (s)':<12} {'Cible (s)':<12} {'Statut':<10}")
print("-" * 52)
for seg in narration_audio_segments:
status = "OK" if seg['audio_duration'] <= seg['target_duration'] else "A ajuster"
print(f"{seg['segment']:<18} {seg['audio_duration']:<12.1f} {seg['target_duration']:<12.0f} {status:<10}")
else:
print("Generation desactivee")NARRATION TTS SYNCHRONISEE
==================================================
Generation de la narration pour 3 segments :
--- Introduction [0s - 5s] ---
Texte : Bienvenue dans cette presentation sur l'intelligence artificielle gene...
Duree audio : 8.2s / cible 5s [DEPASSE]
Taille : 127.5 KB | Generation : 1.9s
--- Concepts cles [5s - 10s] ---
Texte : Les modeles generatifs apprennent a creer du contenu nouveau. Ils anal...
Duree audio : 8.3s / cible 5s [DEPASSE]
Taille : 129.8 KB | Generation : 2.6s
--- Conclusion [10s - 15s] ---
Texte : En resume, l'IA generative ouvre de nouvelles possibilites creatives. ...
Duree audio : 7.4s / cible 5s [DEPASSE]
Taille : 115.1 KB | Generation : 3.4s
Recapitulatif synchronisation :
Segment Audio (s) Cible (s) Statut
----------------------------------------------------
Introduction 8.2 5 A ajuster
Concepts cles 8.3 5 A ajuster
Conclusion 7.4 5 A ajuster
Interpretation : Narration synchronisee
| Aspect | Valeur | Signification |
|---|---|---|
| Synchronisation | Audio <= duree segment | La narration doit tenir dans le segment |
| Depassement | Possible | Ajuster vitesse ou couper le texte |
| Silence residuel | Normal | Comble par la musique de fond |
Note technique : Si la narration depasse la duree du segment, deux solutions : augmenter la vitesse TTS (max 4.0x) ou raccourcir le texte.
Section 3 : Alignement audio sur la timeline video
L’alignement consiste a placer chaque segment audio au bon moment sur la timeline de la video.
Video: [====Introduction====][==Concepts cles==][====Conclusion====]
Audio: [narration_1][silence][narration_2][ sil ][narration_3][ sil]
0s 5s 5s 10s 10s 15s
Chaque narration est placee avec un padding de silence pour remplir exactement la duree du segment.
Concept de timeline audio
Une timeline est une representation lineaire du temps ou chaque segment audio est positionne a un moment précis.
Representation visuelle
Temps (s) : 0 2 4 6 8 10 12 14 16
|----|----|----|----|----|----|----|----|----|
Video: [Intro rouge] [Concepts verts] [Conclusion bleue]
Audio: [Narr1][sil][Narr2][sil][Narr3][sil]
Opérations sur la timeline - Overlay : Superposer un segment a un temps précis - Concatenate : Coller les segments bout a bout - Silence : Créer des espaces vides (padding) - Trim : Couper un segment a une duree precise
Stratégies d’alignement
| Cas | Solution | Avantages | Inconvenients |
|---|---|---|---|
| Audio < Duree segment | Silence residuel | Simple, naturel | Espace vide |
| Audio = Duree segment | Alignement parfait | Optimal | Rare en pratique |
| Audio > Duree segment | Troncation + fade out | Garde la synchro | Perte de fin |
| Audio >> Duree segment | Accelerer TTS (speed >1.0) | Contenu complet | Qualite vocale |
Le code suivant implemente la stratégie “troncation + fade out” pour gerer les depassements.
# Alignement audio sur la timeline video
print("ALIGNEMENT AUDIO - TIMELINE")
print("=" * 50)
def align_audio_to_timeline(segments: List[Dict],
total_duration_ms: int) -> Optional[AudioSegment]:
"""Aligne les segments audio sur une timeline video.
Chaque segment est place a son timestamp de debut,
avec du silence pour remplir les espaces.
"""
timeline = AudioSegment.silent(duration=total_duration_ms)
for seg in segments:
start_ms = int(seg['start'] * 1000)
audio = seg['audio_segment']
if audio is None:
# Pas d'audio_segment disponible (pydub/ffmpeg non fonctionnel)
# On cree un silence pour la duree estimee
target_ms = int(seg['target_duration'] * 1000)
continue
# Tronquer si l'audio depasse la fin du segment
target_ms = int(seg['target_duration'] * 1000)
if len(audio) > target_ms:
# Ajouter un fade out avant de tronquer
audio = audio[:target_ms].fade_out(200)
# Superposer sur la timeline
timeline = timeline.overlay(audio, position=start_ms)
return timeline
narration_timeline = None
if generate_audio and narration_audio_segments:
total_duration_ms = video_duration * 1000
print(f"Duree totale video : {video_duration}s ({total_duration_ms}ms)")
print(f"Segments a aligner : {len(narration_audio_segments)}")
# Verifier si on a des audio_segments valides
has_valid_audio = any(seg['audio_segment'] is not None for seg in narration_audio_segments)
if has_valid_audio:
narration_timeline = align_audio_to_timeline(
narration_audio_segments,
total_duration_ms
)
print(f"\nTimeline narration :")
print(f" Duree : {len(narration_timeline)/1000:.1f}s")
print(f" Canaux : {narration_timeline.channels}")
print(f" Sample rate : {narration_timeline.frame_rate}Hz")
print(f" Volume : {narration_timeline.dBFS:.1f} dBFS")
# Sauvegarder la timeline narration
if save_output_files:
narration_path = OUTPUT_DIR / "narration_timeline.wav"
narration_timeline.export(str(narration_path), format="wav")
print(f" Sauvegarde : {narration_path.name}")
# Ecouter
narration_bytes = BytesIO()
narration_timeline.export(narration_bytes, format="mp3")
print(f"\nEcoute de la timeline narration :")
display_audio_bundle(narration_bytes.getvalue())
else:
print("\npydub/ffmpeg non disponible - alignement audio impossible")
print("Les segments TTS ont ete generes mais ne peuvent pas etre combines")
for seg in narration_audio_segments:
print(f" - {seg['segment']}: {seg['audio_duration']:.1f}s (audio_bytes dispo)")
else:
print("Alignement desactive (pas de segments narration)")ALIGNEMENT AUDIO - TIMELINE
==================================================
Duree totale video : 15s (15000ms)
Segments a aligner : 3
Timeline narration :
Duree : 15.0s
Canaux : 1
Sample rate : 24000Hz
Volume : -28.3 dBFS
Sauvegarde : narration_timeline.wav
Ecoute de la timeline narration :
Exercice : Mixage audio avec ducking automatique
Duree estimee : 25-30 minutes
Objectif
Implementer un algorithme de ducking qui reduit automatiquement le volume de la musique de fond lorsque la voix est detectee.
Contexte
Le ducking est une technique de mixage essentielle pour les podcasts et videos narrees. Plutot que de regler manuellement le volume, un algorithme detecte les moments ou la voix est active et reduit la musique en consequence.
Instructions
- Generer une piste de musique et une piste de voix (ou utiliser des samples existants)
- Detecter les zones de voix active via l’energie du signal
- Appliquer une reduction de volume sur la musique dans ces zones
- Lisser les transitions avec des fades (200-500ms)
Indices : -
# Étape 1: Calculer l’energie RMS par fenêtre glissante (50ms) sur la voix -# Étape 2: Si energie > seuil, appliquer un gain reduit sur la musique -# Étape 3: Lisser les transitions avec np.convolve() ou un filtre exponentiel -# Indice: Seuil de voix ~ -30 dBFS, reduction musique ~ -15 dB quand voix active -# Indice: Utiliser np.interp() pour créer une courbe de gain lisse
# TODO: Implementer le ducking automatique
def auto_duck(voice_audio: np.ndarray, music_audio: np.ndarray,
sr: int = 44100, window_ms: int = 50,
voice_threshold: float = 0.02,
duck_reduction_db: float = -15) -> np.ndarray:
"""
Reduit automatiquement le volume de la musique quand la voix est active.
Args:
voice_audio: Signal de la voix
music_audio: Signal de la musique
sr: Sample rate
window_ms: Taille de fenetre d'analyse en ms
voice_threshold: Seuil de detection de voix (energie RMS)
duck_reduction_db: Reduction en dB quand voix active
Returns:
Musique avec ducking applique
"""
# Etape 1 : Calculer l'energie de la voix par fenetre
# Indice: window_samples = int(sr * window_ms / 1000)
# Indice: Parcourir par fenetres, calculer RMS
pass # TODO etudiant
# Etape 2 : Creer la courbe de gain pour la musique
# Indice: gain = 1.0 si voix inactive, 10**(duck_reduction_db/20) si voix active
pass # TODO etudiant
# Etape 3 : Lisser la courbe de gain et appliquer
# Indice: np.convolve(gain_curve, smoothing_window, mode='same')
# Indice: music_ducked = music_audio * gain_curve
pass # TODO etudiant
return music_audio # TODO etudiant : retourner la musique traitee
# Test avec des signaux de demonstration
sr_test = 44100
duration_test = 5
# voix_test = np.random.randn(sr_test * duration_test).astype(np.float32) * 0.1
# musique_test = np.sin(2 * np.pi * 440 * np.linspace(0, duration_test, sr_test * duration_test)).astype(np.float32) * 0.3
# result = auto_duck(voix_test, musique_test, sr_test)
# print(f"Ducking applique : {len(result)} samples")
print("Exercice a completer")Exercice a completer
Section 4 : Musique de fond sous la narration
La superposition de musique de fond et de narration necessite un contrôle précis des niveaux :
| Piste | Volume relatif | Raison |
|---|---|---|
| Narration | 0 dB (reference) | Voix au premier plan |
| Musique de fond | -15 a -25 dB | Ambiance sans couvrir la voix |
| Effets sonores | -5 a -10 dB | Ponctuels, perceptibles |
La technique du “ducking” baisse automatiquement la musique quand la voix est presente.
Théorie du mixage audio
Le mixage de plusieurs pistes audio necessite de comprendre les niveaux relatifs :
Echelle dBFS (Decibels Full Scale) - 0 dBFS = Maximum numérique (clip) - -6 dBFS = Niveau “hot” (pop, radio) - -12 dBFS = Niveau standard (broadcast) - -18 dBFS = Niveau confortable (podcast) - -24 dBFS = Niveau faible (ambiance)
Equilibrage des pistes
| Piste | Volume cible | Ratio |
|---|---|---|
| Narration | -12 a -18 dBFS | Reference (0 dB relatif) |
| Musique de fond | -35 a -45 dBFS | -20 dB sous narration |
| Effets sonores | -20 a -25 dBFS | -5 a -10 dB sous narration |
Ducking automatique Le ducking baisse le volume de la musique quand la voix est presente : - Voice active : musique a -25 dB - Voice inactive : musique a -15 dB - Transition : 200-500 ms
Cette technique evite d’avoir a couper manuellement la musique.
# Superposition musique de fond + narration
print("SUPERPOSITION MUSIQUE DE FOND")
print("=" * 50)
def generate_ambient_music(duration_ms: int, sr: int = 44100) -> Optional[AudioSegment]:
"""Genere une musique d'ambiance simple (sinusoides harmoniques)."""
try:
t = np.linspace(0, duration_ms / 1000, int(sr * duration_ms / 1000))
# Accords doux (Do majeur : C4, E4, G4)
freqs = [261.63, 329.63, 392.00] # C4, E4, G4
audio = np.zeros_like(t)
for f in freqs:
audio += 0.15 * np.sin(2 * np.pi * f * t)
# Modulation lente pour un effet d'ambiance
modulation = 0.5 + 0.5 * np.sin(2 * np.pi * 0.1 * t) # 0.1 Hz
audio *= modulation
# Normaliser en int16
audio = (audio / np.max(np.abs(audio)) * 16000).astype(np.int16)
return AudioSegment(
data=audio.tobytes(),
sample_width=2,
frame_rate=sr,
channels=1
)
except Exception as e:
print(f"Erreur generation musique : {e}")
return None
mixed_audio = None
if generate_audio and narration_timeline:
total_duration_ms = len(narration_timeline)
# Generer la musique de fond
print("Generation de la musique d'ambiance...")
background_music = generate_ambient_music(total_duration_ms)
if background_music:
# Ajuster le volume de la musique
background_music = background_music + background_music_volume # dB
# Fade in/out sur la musique
background_music = background_music.fade_in(1000).fade_out(2000)
print(f"Musique de fond :")
print(f" Duree : {len(background_music)/1000:.1f}s")
print(f" Volume : {background_music.dBFS:.1f} dBFS (reduit de {background_music_volume}dB)")
# Superposer narration + musique
print(f"\nSuperposition narration + musique...")
# S'assurer que les deux ont la meme duree
if len(background_music) > len(narration_timeline):
background_music = background_music[:len(narration_timeline)]
elif len(background_music) < len(narration_timeline):
background_music = background_music + AudioSegment.silent(
duration=len(narration_timeline) - len(background_music)
)
mixed_audio = narration_timeline.overlay(background_music)
print(f"Mix final :")
print(f" Duree : {len(mixed_audio)/1000:.1f}s")
print(f" Volume : {mixed_audio.dBFS:.1f} dBFS")
# Sauvegarder et ecouter
if save_output_files:
mix_path = OUTPUT_DIR / "narration_plus_music.wav"
mixed_audio.export(str(mix_path), format="wav")
print(f" Sauvegarde : {mix_path.name}")
mix_bytes = BytesIO()
mixed_audio.export(mix_bytes, format="mp3")
print(f"\nEcoute du mix (narration + musique de fond) :")
display_audio_bundle(mix_bytes.getvalue())
else:
print("Musique de fond non disponible")
elif not narration_timeline:
print("Mix desactive (pas de narration timeline)")
else:
print("Mix desactive")SUPERPOSITION MUSIQUE DE FOND
==================================================
Generation de la musique d'ambiance...
Musique de fond :
Duree : 15.0s
Volume : -37.8 dBFS (reduit de -20dB)
Superposition narration + musique...
Mix final :
Duree : 15.0s
Volume : -27.8 dBFS
Sauvegarde : narration_plus_music.wav
Ecoute du mix (narration + musique de fond) :
Interpretation : Mix audio
| Piste | Volume (dBFS) | Rôle |
|---|---|---|
| Narration | ~-15 a -20 | Premier plan |
| Musique de fond | ~-35 a -45 | Ambiance discrete |
| Mix final | ~-15 a -20 | Equilibre narration/musique |
Points cles : 1. Le volume de la musique (-20dB) la rend perceptible sans couvrir la voix 2. Les fades evitent les transitions brutales 3. En production, utiliser du ducking dynamique pour un résultat plus naturel
Section 5 : Assemblage video + audio final
L’assemblage final combine la video, la narration et la musique de fond en un seul fichier.
Pipeline d'assemblage :
Video (.mp4, sans audio)
+ Narration TTS (timeline alignee)
+ Musique de fond (volume reduit)
= Video finale (.mp4, avec audio mixe)
moviepy gere la combinaison des flux video et audio.
# Assemblage video + audio final
print("ASSEMBLAGE VIDEO + AUDIO")
print("=" * 50)
final_video_path = OUTPUT_DIR / "video_finale_narree.mp4"
if generate_audio and moviepy_available and mixed_audio and test_video_path.exists():
# Sauvegarder l'audio mixe en WAV pour moviepy
mixed_audio_path = OUTPUT_DIR / "mixed_audio_temp.wav"
mixed_audio.export(str(mixed_audio_path), format="wav")
print("Assemblage en cours...")
start_time = time.time()
# Charger la video et l'audio
video_clip = VideoFileClip(str(test_video_path))
audio_clip = AudioFileClip(str(mixed_audio_path))
# Ajuster la duree de l'audio a celle de la video
if audio_clip.duration > video_clip.duration:
audio_clip = audio_clip.subclipped(0, video_clip.duration)
# Combiner video + audio
video_with_audio = video_clip.with_audio(audio_clip)
# Exporter
video_with_audio.write_videofile(
str(final_video_path),
fps=video_fps,
codec='libx264',
audio_codec='aac',
logger=None
)
assembly_time = time.time() - start_time
# Fermer les clips
video_clip.close()
audio_clip.close()
video_with_audio.close()
# Nettoyage fichier temporaire
if mixed_audio_path.exists():
mixed_audio_path.unlink()
print(f"Assemblage termine en {assembly_time:.1f}s")
print(f"\nVideo finale :")
print(f" Fichier : {final_video_path.name}")
print(f" Taille : {final_video_path.stat().st_size/1024:.1f} KB")
print(f" Resolution : {video_resolution[0]}x{video_resolution[1]}")
print(f" FPS : {video_fps}")
print(f" Duree : {video_duration}s")
print(f" Audio : narration TTS + musique de fond")
# Afficher la video dans le notebook (si possible)
try:
display(IPVideo(str(final_video_path), embed=True, width=480))
except Exception as e:
print(f"Affichage video non disponible : {str(e)[:80]}")
print(f"Ouvrez le fichier : {final_video_path.name}")
else:
if not moviepy_available:
print("moviepy requis pour l'assemblage")
elif not mixed_audio:
print("Pas d'audio mixe disponible")
elif not test_video_path.exists():
print("Video de test non disponible")
else:
print("Assemblage desactive")ASSEMBLAGE VIDEO + AUDIO
==================================================
Assemblage en cours...
Assemblage termine en 1.0s
Video finale :
Fichier : video_finale_narree.mp4
Taille : 262.9 KB
Resolution : 640x480
FPS : 24
Duree : 15s
Audio : narration TTS + musique de fond
Preparation de l’assemblage
Avant de combiner video et audio, verifions les composants :
| Composant | Statut | Fichier | Duree |
|---|---|---|---|
| Video de base | Créée | test_video.mp4 |
15s |
| Narration timeline | Generee | narration_timeline.wav |
15s |
| Mix audio | Prepare | narration_plus_music.wav |
15s |
Prerequis techniques - moviepy pour la manipulation video - ffmpeg pour l’encodage des codecs - Espace disque suffisant pour la video finale (≈1-5 MB)
Processus d’assemblage 1. Charger la video sans audio 2. Charger l’audio mixe (WAV) 3. Synchroniser les durees 4. Combiner les pistes 5. Exporter avec codecs standards
Interpretation : Assemblage final
| Aspect | Valeur | Signification |
|---|---|---|
| Codec video | H.264 (libx264) | Standard universel |
| Codec audio | AAC | Compression efficace |
| Temps d’assemblage | Variable | Depend de la duree et resolution |
Points cles : 1. moviepy simplifie considerablement le pipeline d’assemblage 2. Les codecs H.264/AAC sont compatibles avec tous les lecteurs 3. Pour de la production, utiliser codec='libx264' avec -preset slow pour une meilleure qualite
Exercice : Assembleur video avec transitions personnalisables
Duree estimee : 25-30 minutes
Objectif
Créer une fonction qui assemble une video a partir de segments audio et visuels en appliquant différents types de transitions (fade, crossfade, cut sec).
Contexte
L’assemblage video ne se limite pas a la concatenation. Le choix des transitions (fade noir, fondu enchaine, cut sec) influence le rendu final. Cet exercice vous amene a implementer un assembleur flexible.
Instructions
- Définir une structure de segments avec transition souhaitee
- Implementer les 3 types de transitions (fade, crossfade, cut)
- Appliquer les transitions entre les segments
- Normaliser le volume global et exporter
Indices : -
# Étape 1: Structure :[{"audio": np.array, "transition": "fade", "duration": 0.5}]-# Étape 2: Fade = np.linspace(0, 1, samples) en entree, np.linspace(1, 0, samples) en sortie -# Étape 3: Crossfade = superposer les derniers echantillons d’un segment avec les premiers du suivant -# Indice: Le cut sec ne necessite aucune transition, juste np.concatenate
# TODO: Implementer l'assembleur avec transitions
def assemble_with_transitions(segments: list, sr: int = 44100) -> np.ndarray:
"""
Assemble des segments audio avec transitions personnalisees.
Args:
segments: Liste de dicts {"audio": np.array, "transition": str, "duration": float}
transition: "fade", "crossfade", ou "cut"
duration: duree de la transition en secondes
sr: Sample rate
Returns:
Audio assemble avec transitions
"""
result = np.array([], dtype=np.float32)
for i, seg in enumerate(segments):
audio = seg["audio"].copy()
transition = seg.get("transition", "cut")
trans_dur = seg.get("duration", 0.5)
trans_samples = int(sr * trans_dur)
if i == 0:
# Premier segment : fade in
# Indice: audio[:trans_samples] *= np.linspace(0, 1, trans_samples)
pass # TODO etudiant
else:
# Appliquer la transition avec le segment precedent
if transition == "fade":
# Indice: fade out du resultat + fade in du nouveau
pass # TODO etudiant
elif transition == "crossfade":
# Indice: superposer les trans_samples derniers samples
pass # TODO etudiant
else: # "cut"
# Indice: simple concatenation
pass # TODO etudiant
# result = np.concatenate([result, audio]) # adapter selon la transition
# Normalisation finale
# Indice: peak = np.max(np.abs(result))
# Indice: if peak > 0: result = result / peak * 0.95
return result # TODO etudiant : retourner l'audio assemble
# Test avec 3 segments de demonstration
# seg1 = np.sin(2 * np.pi * 261 * np.linspace(0, 2, 88200)).astype(np.float32) * 0.3 # Do
# seg2 = np.sin(2 * np.pi * 329 * np.linspace(0, 2, 88200)).astype(np.float32) * 0.3 # Mi
# seg3 = np.sin(2 * np.pi * 392 * np.linspace(0, 2, 88200)).astype(np.float32) * 0.3 # Sol
# demo = [
# {"audio": seg1, "transition": "fade", "duration": 0.3},
# {"audio": seg2, "transition": "crossfade", "duration": 0.5},
# {"audio": seg3, "transition": "cut", "duration": 0.0},
# ]
# result = assemble_with_transitions(demo)
# print(f"Audio assemble : {len(result)/44100:.1f}s")
print("Exercice a completer")Exercice a completer
Passerelle vers la serie Video
Ce notebook constitue le point de jonction entre les series Audio et Video du cours GenAI. Il demontre comment les techniques audio apprises dans les notebooks précédents s’appliquent directement aux workflows video.
Mapping Audio -> Video
| Serie Audio (cette serie) | Serie Video | Lien technique |
|---|---|---|
| TTS (narration) | Video narree | Audio enrichit la video avec une voix off |
| Whisper (STT) | Sous-titrage video | Transcription -> SRT -> Embed subs |
| MusicGen (composition) | Bande sonore video | Musique de fond et ambiance |
| Demucs (separation) | Post-production audio | Remixage, isolation de pistes, nettoyage |
| Opérations audio | Montage video | Coupes, fades, transitions |
Exemples d’applications
- Doublage automatique : TTS multi-langue pour internationaliser des videos
- Accessibilite : Whisper pour sous-titres automatiques (SDH)
- Marketing : MusicGen pour generer des musiques adaptees au mood
- Educational : Narration TTS pour tutoriels et MOOCs
- Social media : Generer rapidement du contenu video narre
Pour continuer dans la serie Video
Video Foundation (Video/01-Foundation/) - 01-1-Video-Opérations-Basics.ipynb - Chargement, decoupage, encodage video - 01-2-ComfyUI-Video-Basics.ipynb - Introduction a ComfyUI pour video - 01-3-Qwen-VL.ipynb - Vision-Language models pour video understanding
Video Advanced (Video/02-Advanced/) - 02-1-HunyuanVideo.ipynb - Generation video avec HunyuanVideo - 02-2-LTX-Video.ipynb - Video generation temps reel - 02-3-Wan.ipynb - Modèle Wan pour video haute qualite
Video Orchestration (Video/03-Orchestration/) - Pipelines ComfyUI avances - Multi-modèles video + audio - Production et optimisation
Competences transferees
Les competences acquises dans cette serie Audio sont directement reutilisables : - Manipulation de timelines - Audio et video utilisent le même concept de temps - Gestion de pistes multiples - Mixage audio = composition video - Format de fichiers - Conteneurs (MP4, WAV), codecs (H.264, AAC) - Outils Python - moviepy, pydub, ffmpeg pour l’audio et la video
Les techniques audio apprises ici constituent la base solide pour aborder la creation video avec l’IA generative.
# Mode interactif - Personnaliser le pipeline
if notebook_mode == "interactive" and not skip_widgets:
print("MODE INTERACTIF - PIPELINE PERSONNALISE")
print("=" * 50)
print("\nEntrez un texte de narration pour l'ajouter a la video :")
print("(Laissez vide pour passer)")
try:
user_text = input("\nVotre narration : ")
if user_text.strip():
print(f"\nGeneration TTS...")
response = client.audio.speech.create(
model=tts_model,
voice=narrator_voice,
input=user_text,
response_format="mp3"
)
print(f"Narration generee ({len(response.content)/1024:.1f} KB) :")
display_audio_bundle(response.content)
if save_output_files:
ts = datetime.now().strftime('%Y%m%d_%H%M%S')
filepath = OUTPUT_DIR / f"custom_narration_{ts}.mp3"
with open(filepath, 'wb') as f:
f.write(response.content)
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 - PIPELINE PERSONNALISE
==================================================
Entrez un texte de narration pour l'ajouter a la video :
(Laissez vide pour passer)
Mode interactif non disponible (execution automatisee)
Section 6 : Mode interactif - Experimentation
Cette section permet d’experimenter librement avec le pipeline de synchronisation.
Workflow interactif
- Saisir un texte de narration - L’API TTS genere l’audio instantanement
- Ecouter le résultat - Verification de la qualite et de la duree
- Ajuster les paramètres - Vitesse, voix, volume selon les besoins
- Sauvegarder - Les fichiers sont stockes dans
outputs/audio/av_sync/
Cas d’usage
- Test de différentes voix pour un projet
- Ajustement de la vitesse pour un timing précis
- Generation rapide de narrations pour prototypes
Note : En mode batch (Papermill), cette section est automatiquement desactivee pour eviter les blocages.
Le module de statistiques recapitule les résultats de la session : videos créées, pistes audio generees, durees traitees et ressources consommees par chaque étape du pipeline de synchronisation.
# Statistiques de session
print("STATISTIQUES DE SESSION")
print("=" * 50)
print(f"Date : {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
print(f"Mode : {notebook_mode}")
print(f"TTS : {tts_model}, Voix : {narrator_voice}")
if narration_audio_segments:
total_narration = sum(s['audio_duration'] for s in narration_audio_segments)
print(f"Segments narration : {len(narration_audio_segments)} ({total_narration:.1f}s)")
if narration_timeline:
print(f"Timeline narration : {len(narration_timeline)/1000:.1f}s")
if mixed_audio:
print(f"Mix final : {len(mixed_audio)/1000:.1f}s")
if final_video_path.exists():
print(f"Video finale : {final_video_path.name} ({final_video_path.stat().st_size/1024:.1f} KB)")
if save_output_files:
saved_files = list(OUTPUT_DIR.glob('*'))
print(f"Fichiers sauvegardes : {len(saved_files)} dans {OUTPUT_DIR.relative_to(GENAI_ROOT)}")
print(f"\nSERIE AUDIO TERMINEE")
print(f"Felicitations ! Vous avez parcouru l'ensemble de la serie Audio :")
print(f" 01-Foundation : TTS, STT, operations audio, Whisper local, Kokoro")
print(f" 02-Advanced : Chatterbox, XTTS, MusicGen, Demucs")
print(f" 03-Orchestration : Multi-modeles, pipelines, voix temps reel")
print(f" 04-Applications : Contenu educatif, transcription, composition, A/V sync")
print(f"\nPROCHAINE SERIE")
print(f" -> Video/01-Foundation/01-1-Video-Operations-Basics.ipynb")
print(f"\nNotebook termine - {datetime.now().strftime('%H:%M:%S')}")STATISTIQUES DE SESSION
==================================================
Date : 2026-08-16 01:51:20
Mode : interactive
TTS : tts-1, Voix : nova
Segments narration : 3 (23.8s)
Timeline narration : 15.0s
Mix final : 15.0s
Video finale : video_finale_narree.mp4 (262.9 KB)
Fichiers sauvegardes : 4 dans outputs\audio\av_sync
SERIE AUDIO TERMINEE
Felicitations ! Vous avez parcouru l'ensemble de la serie Audio :
01-Foundation : TTS, STT, operations audio, Whisper local, Kokoro
02-Advanced : Chatterbox, XTTS, MusicGen, Demucs
03-Orchestration : Multi-modeles, pipelines, voix temps reel
04-Applications : Contenu educatif, transcription, composition, A/V sync
PROCHAINE SERIE
-> Video/01-Foundation/01-1-Video-Operations-Basics.ipynb
Notebook termine - 01:51:20
Interpretation : Session completee
Cette session a demontre l’integration complete du pipeline audio-video :
| Metrique | Valeur | Signification |
|---|---|---|
| Segments narration | 3 | Couverture complete de la video |
| Duree totale audio | 24.4s | Depassement de 9.4s (63%) |
| Timeline ajustee | 15.0s | Troncage pour synchronisation |
| Mix final | 15.0s | Narration + musique alignees |
| Fichiers generes | 2 | narration_timeline.wav, narration_plus_music.wav |
Analyse du depassement audio - La narration TTS depasse systematiquement les segments cibles (5s) - Causes possibles : texte trop long, vitesse de parole standard (1.0) - Solutions : reduire le texte de 30% ou augmenter la vitesse a 1.3-1.5x
Points cles 1. Le pipeline fonctionne de bout en bout malgre le depassement 2. La troncature audio preserve la synchronisation 3. Le mixage maintient la comprehensibilite de la narration
Note technique : En production, toujours generer une marge de 10-15% sur la duree audio pour absorber les variations de vitesse TTS.
Exercice : Video Narree avec Sous-Titres
Duree estimee : 40-45 minutes
Objectif
Créer une video narree complete avec narration TTS synchronisee, musique de fond, et sous-titres generes automatiquement.
Instructions
- Créer une video simple (3-4 segments visuels)
- Generer des scripts de narration pour chaque segment
- Produire la narration TTS synchronisee avec les segments
- Ajouter une musique de fond appropriee
- Generer des sous-titres a partir de la narration
- Assembler le tout en une video finale
Indices : - Pour la video: utilisez
moviepy.ColorClipetTextClippour des segments simples - Pour la narration TTS: synchronisez la duree audio avec la duree du segment video - Pour la musique de fond: utilisez un volume de -20dB pour ne pas couvrir la voix - Pour les sous-titres: utilisez les timestamps de la narration TTS
Synthese des apprentissages
Ce notebook vous a permis de decouvrir le pipeline complet de synchronisation audio-video :
Concepts maitrises
| Concept | Description | Application |
|---|---|---|
| Extraction audio | Separer la piste audio d’une video | Pre-processing pour transcription |
| Narration TTS | Synthese vocale timelinee | Doublage automatique, accesibilite |
| Alignement temporel | Synchroniser audio et video | Lip-sync, sous-titres synchronises |
| Mixage audio | Superposer plusieurs pistes | Narration + musique + effets |
| Assemblage final | Combiner video et audio | Production video complete |
Workflow industriel
1. Pre-production
- Script et segmentation
- Preparation des assets visuels
- Sélection musicale
2. Production
- Generation TTS par segment
- Alignement sur la timeline
- Mixage audio (narration + musique)
3. Post-production
- Assemblage video + audio
- Generation des sous-titres
- Export et compression
Bonnes pratiques
- Toujours calibrer la duree audio a la duree du segment video
- Utiliser le ducking pour eviter que la musique ne couvre la voix
- Generer des sous-titres pour l’accesibilite et le SEO
- Tester sur différents appareils pour verifier la compatibilite
Passerelle vers la serie Video
Les techniques audio apprises ici sont fondamentales pour la creation de contenu video. Dans la serie Video, vous apprendrez a : - Generer des videos avec des modèles comme HunyuanVideo et LTX-Video - Utiliser ComfyUI pour des pipelines video avances - Créer des effets visuels et des transitions - Optimiser les formats pour différentes plateformes
Exercice : Video Narree avec Sous-Titres
Duree estimee : 40-45 minutes
Niveau : Intermediaire
Objectif
Créer une video narree complete avec narration TTS synchronisee, musique de fond, et sous-titres generes automatiquement.
Contexte
Dans cet exercice, vous allez mettre en pratique les concepts appris dans ce notebook : - Synchronisation audio-video - Generation TTS timelinee - Mixage audio (narration + musique) - Assemblage video final
Instructions
- Créer une video simple (3-4 segments visuels)
- Generer des scripts de narration pour chaque segment
- Produire la narration TTS synchronisee avec les segments
- Ajouter une musique de fond appropriee
- Generer des sous-titres a partir de la narration
- Assembler le tout en une video finale
Indices : - Pour la video: utilisez
moviepy.ColorClipetTextClippour des segments simples - Pour la narration TTS: synchronisez la duree audio avec la duree du segment video - Pour la musique de fond: utilisez un volume de -20dB pour ne pas couvrir la voix - Pour les sous-titres: utilisez les timestamps de la narration TTS
Critères de succès
Extensions (optionnel)
Ressources utiles
- moviepy documentation: https://zulko.github.io/moviepy/
- pydub audio manipulation: https://github.com/jiaaro/pydub
- Format SRT: https://docs.fileformat.com/video/srt/
Verification avant soumission
# Verifier que la video finale existe
from pathlib import Path
final_video = Path("outputs/audio/av_sync/video_finale.mp4")
assert final_video.exists(), "Video finale manquante"
assert final_video.stat().st_size > 1000, "Video trop petite (corrompue?)"
print(f"Video OK: {final_video.stat().st_size/1024:.1f} KB")