OpenAI Whisper STT - Reconnaissance Vocale par API

Module : 01-Audio-Foundation
Niveau : Debutant
Technologies : OpenAI Whisper API, GPT-4o-Transcribe
Duree estimee : 35 minutes

Objectifs d’Apprentissage

Prerequis

  • Environment Setup (module 00) complete
  • Cle API OpenAI configuree (OPENAI_API_KEY dans .env)
  • Notebook 01-1 (TTS) recommande mais pas obligatoire

Navigation : Index | << Précédent | Suivant >>

# Parametres Papermill - JAMAIS modifier ce commentaire

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

# Parametres STT
stt_model = "whisper-1"            # "whisper-1" ou "gpt-4o-transcribe"
language = None                    # Code langue ISO 639-1 (None = auto-detection)
response_format = "verbose_json"   # "json", "text", "srt", "vtt", "verbose_json"

# Configuration
generate_test_audio = True         # Generer un fichier audio de test via TTS
test_translation = True            # Tester l'endpoint de traduction
compare_models = True              # Comparer whisper-1 et gpt-4o-transcribe
save_results = True                # Sauvegarder les resultats de transcription
# Parameters
BATCH_MODE = "true"

Les paramètres Papermill etant définis, nous configurons l’environnement Python et importons les bibliotheques necessaires pour la reconnaissance vocale et la gestion des fichiers audio.

# Setup environnement et imports
import os
import sys
import json
import time
import tempfile
from pathlib import Path
from datetime import datetime
from typing import Dict, List, Any, Optional
from io import BytesIO
import logging

# Lecture audio dans Jupyter
from IPython.display import Audio, display, HTML

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

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

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

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

print(f"OpenAI Whisper STT - Reconnaissance Vocale")
print(f"Date : {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
print(f"Mode : {notebook_mode}, Modele STT : {stt_model}")
print(f"Sortie : {OUTPUT_DIR.relative_to(GENAI_ROOT)}")
Helpers audio importes
OpenAI Whisper STT - Reconnaissance Vocale
Date : 2026-09-30 11:28:31
Mode : interactive, Modele STT : whisper-1
Sortie : outputs\audio\stt

Les paramètres Papermill etant définis, nous importons les bibliotheques requises pour la reconnaissance vocale et la manipulation de fichiers audio.

# 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

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}")

openai_key = os.environ.get('OPENAI_API_KEY', '')
openai_available = bool(openai_key and not openai_key.startswith('sk-or-'))

if openai_available:
    print("OPENAI_API_KEY valide - API disponible")
    from openai import OpenAI
    client = OpenAI(api_key=openai_key)
else:
    print("OPENAI_API_KEY non disponible ou invalide (cle OpenRouter)")
    openai_key = "dummy_key_for_validation"
    client = None
.env charge depuis: .env
OPENAI_API_KEY valide - API disponible

Section 1 : Generation d’un echantillon de test

Avant de transcrire, nous avons besoin d’un fichier audio. Nous utilisons l’API TTS pour generer un echantillon contrôle, ce qui nous permettra de verifier la precision de la transcription.

Cette approche “round-trip” (TTS puis STT) est utile pour : - Valider le pipeline audio de bout en bout - Mesurer la fidelite de la transcription - Avoir un texte de reference pour comparaison

# Generation d'echantillons audio de test
print("GENERATION DES ECHANTILLONS DE TEST")
print("=" * 45)

# Textes de test dans differentes langues
test_texts = {
    "fr": (
        "La reconnaissance vocale permet de convertir la parole en texte. "
        "Cette technologie utilise des reseaux de neurones profonds pour "
        "analyser le signal audio et identifier les mots prononces."
    ),
    "en": (
        "Speech recognition converts spoken language into written text. "
        "Modern systems use deep neural networks trained on thousands "
        "of hours of audio data to achieve human-level accuracy."
    ),
    "multi": (
        "Bonjour, je parle francais. Now I switch to English. "
        "Et je reviens au francais pour terminer."
    )
}

audio_samples = {}

if generate_test_audio:
    for lang_key, text in test_texts.items():
        print(f"\nGeneration echantillon '{lang_key}'...")
        print(f"  Texte : {text[:80]}...")

        response = client.audio.speech.create(
            model="tts-1",
            voice="nova",
            input=text,
            response_format="mp3"
        )

        audio_data = response.content
        filepath = SAMPLES_DIR / f"sample_{lang_key}.mp3"
        with open(filepath, 'wb') as f:
            f.write(audio_data)

        audio_samples[lang_key] = {
            "path": filepath,
            "text_original": text,
            "size_kb": len(audio_data) / 1024
        }

        print(f"  Fichier : {filepath.name} ({len(audio_data)/1024:.1f} KB)")
        display_audio_bundle(audio_data)

    print(f"\n{len(audio_samples)} echantillons generes")
else:
    # Chercher des fichiers existants
    for lang_key in test_texts:
        filepath = SAMPLES_DIR / f"sample_{lang_key}.mp3"
        if filepath.exists():
            audio_samples[lang_key] = {
                "path": filepath,
                "text_original": test_texts[lang_key],
                "size_kb": filepath.stat().st_size / 1024
            }
    print(f"{len(audio_samples)} echantillons existants trouves")
GENERATION DES ECHANTILLONS DE TEST
=============================================

Generation echantillon 'fr'...
  Texte : La reconnaissance vocale permet de convertir la parole en texte. Cette technolog...
  Fichier : sample_fr.mp3 (184.9 KB)

Generation echantillon 'en'...
  Texte : Speech recognition converts spoken language into written text. Modern systems us...
  Fichier : sample_en.mp3 (177.4 KB)

Generation echantillon 'multi'...
  Texte : Bonjour, je parle francais. Now I switch to English. Et je reviens au francais p...
  Fichier : sample_multi.mp3 (88.5 KB)

3 echantillons generes

Interpretation : Generation d’echantillons audio

Aspect Valeur Signification
Taille fichiers 88-185 KB Compression MP3 efficace pour la qualite vocale
Synthese TTS voix “nova” Modèle polyvalent, adapte aux textes courts
Round-trip TTS → STT Permet de valider la fidelite de la chaîne audio

Points cles : 1. L’approche round-trip (TTS puis STT) garantit un texte de reference connu 2. Les echantillons multilingues testent la robustesse de la detection de langue 3. La taille du fichier MP3 depend de la duree et de la complexite du texte

Note technique : La voix “nova” du modèle tts-1 est optimisee pour les textes courts et la conversation. Pour des textes plus longs, considerer tts-1-hd pour une meilleure qualite audio.

Synthese de la Section 1

Cette section a initialise notre environnement de test en generant des echantillons audio contrôles via l’API TTS.

Résultats obtenus : - 3 echantillons audio generes (francais, anglais, multilingue) - Fichiers MP3 de 88 a 185 KB stockes dans outputs/audio/samples/ - Textes de reference connus pour validation de la transcription

Transition : Nous disposons maintenant de fichiers audio de qualite connue pour tester les capacites de transcription de Whisper. La section suivante explore l’API de transcription dans ses différents formats de sortie.

Section 2 : Transcription avec Whisper API

L’API Whisper accepte un fichier audio et retourne la transcription. Les paramètres principaux :

Paramètre Description Valeurs
model Modèle STT whisper-1, gpt-4o-transcribe
file Fichier audio MP3, MP4, WAV, FLAC, etc. (max 25 MB)
language Langue source Code ISO 639-1 (fr, en, etc.) ou None
response_format Format de sortie json, text, srt, vtt, verbose_json
timestamp_granularities Precision timestamps ["word"], ["segment"], ["word", "segment"]
# Transcription de base
print("TRANSCRIPTION WHISPER")
print("=" * 45)

if "fr" in audio_samples:
    sample = audio_samples["fr"]
    print(f"Fichier source : {sample['path'].name}")
    print(f"Texte original : {sample['text_original'][:80]}...")

    # --- Format JSON simple ---
    print(f"\n--- Transcription (format json) ---")
    with open(sample['path'], 'rb') as audio_file:
        transcript_json = client.audio.transcriptions.create(
            model=stt_model,
            file=audio_file,
            response_format="json"
        )
    print(f"Resultat : {transcript_json.text}")

    # --- Format verbose_json (avec metadonnees) ---
    print(f"\n--- Transcription (format verbose_json) ---")
    with open(sample['path'], 'rb') as audio_file:
        transcript_verbose = client.audio.transcriptions.create(
            model=stt_model,
            file=audio_file,
            response_format="verbose_json",
            timestamp_granularities=["word", "segment"]
        )

    print(f"Texte : {transcript_verbose.text}")
    print(f"Langue detectee : {transcript_verbose.language}")
    print(f"Duree : {transcript_verbose.duration:.1f}s")

    # Affichage des segments
    if hasattr(transcript_verbose, 'segments') and transcript_verbose.segments:
        print(f"\nSegments ({len(transcript_verbose.segments)}) :")
        for seg in transcript_verbose.segments:
            print(f"  [{seg.start:.1f}s - {seg.end:.1f}s] {seg.text.strip()}")

    # Affichage des mots avec timestamps
    if hasattr(transcript_verbose, 'words') and transcript_verbose.words:
        print(f"\nMots avec timestamps ({len(transcript_verbose.words)}) :")
        for word in transcript_verbose.words[:15]:  # Premiers 15 mots
            print(f"  [{word.start:.2f}s - {word.end:.2f}s] {word.word}")
        if len(transcript_verbose.words) > 15:
            print(f"  ... ({len(transcript_verbose.words) - 15} mots supplementaires)")

    # Sauvegarde
    if save_results:
        result_file = OUTPUT_DIR / "transcription_fr.json"
        with open(result_file, 'w', encoding='utf-8') as f:
            json.dump({
                "text": transcript_verbose.text,
                "language": transcript_verbose.language,
                "duration": transcript_verbose.duration,
                "model": stt_model
            }, f, indent=2, ensure_ascii=False)
        print(f"\nResultat sauvegarde : {result_file.name}")
else:
    print("Aucun echantillon audio disponible")
TRANSCRIPTION WHISPER
=============================================
Fichier source : sample_fr.mp3
Texte original : La reconnaissance vocale permet de convertir la parole en texte. Cette technolog...

--- Transcription (format json) ---
Resultat : La reconnaissance vocale permet de convertir la parole en texte. Cette technologie utilise des réseaux de neurones profonds pour analyser le signal audio et identifier les mots-prononces.

--- Transcription (format verbose_json) ---
Texte : La reconnaissance vocale permet de convertir la parole en texte. Cette technologie utilise des réseaux de neurones profonds pour analyser le signal audio et identifier les mots-prononces.
Langue detectee : french
Duree : 11.8s

Segments (3) :
  [0.0s - 3.6s] La reconnaissance vocale permet de convertir la parole en texte.
  [4.7s - 7.6s] Cette technologie utilise des réseaux de neurones profonds
  [7.6s - 11.5s] pour analyser le signal audio et identifier les mots-prononces.

Mots avec timestamps (28) :
  [0.00s - 0.18s] La
  [0.18s - 0.84s] reconnaissance
  [0.84s - 1.24s] vocale
  [1.24s - 1.58s] permet
  [1.58s - 1.92s] de
  [1.92s - 2.38s] convertir
  [2.38s - 2.58s] la
  [2.58s - 2.88s] parole
  [2.88s - 3.16s] en
  [3.16s - 3.60s] texte
  [4.70s - 4.78s] Cette
  [4.78s - 5.44s] technologie
  [5.44s - 6.00s] utilise
  [6.00s - 6.22s] des
  [6.22s - 6.50s] réseaux
  ... (13 mots supplementaires)

Resultat sauvegarde : transcription_fr.json

Interpretation : Transcription Whisper

Aspect Valeur Signification
Fidelite Très elevee Whisper reproduit le texte original avec precision
Detection de langue Automatique Whisper detecte la langue sans paramètre explicite
Timestamps Niveau mot et segment Utile pour sous-titrage, karaoke, synchronisation

Points cles : 1. Le format verbose_json fournit les metadonnees les plus completes 2. Les timestamps au niveau mot necessitent timestamp_granularities=["word"] 3. La detection de langue est fiable pour les langues courantes

Exemple guidé 1 : Transcription avec paramètres avancés

Contribution étudiante de @pierre-ingrachen (PR #18549), intégrée comme exemple guidé.

La transcription de base ci-dessus laissait Whisper détecter la langue et renvoyait un objet JSON. Cet exemple fait varier, sur le même fichier audio, les deux paramètres qui pilotent la forme de la réponse, et mesure leur effet.

Objectif : comprendre l’impact des paramètres response_format et language sur le résultat de la transcription.

Étapes : 1. Transcrire l’audio français en spécifiant explicitement language="fr" (format json) 2. Transcrire une seconde fois en format text (sans métadonnées) 3. Comparer les deux résultats : le texte est-il identique ? 4. Afficher la différence de temps de réponse entre les deux appels

Points clés du code : - language="fr" s’ajoute dans client.audio.transcriptions.create() pour imposer la langue au lieu de la détecter - le format "text" renvoie une chaîne brute (str), et non un objet dont on lit l’attribut .text - la comparaison se fait après une normalisation minimale : text1.strip().lower() == text2.strip().lower()

# Exemple guide 1 : Transcription avec parametres avances
# Contribution etudiante de @pierre-ingrachen (PR #18549)
print("EXEMPLE GUIDE 1 : PARAMETRES AVANCES")
print("=" * 45)

if "fr" in audio_samples:
    sample = audio_samples["fr"]
    print(f"Fichier source : {sample['path'].name}")
    print(f"Texte original : {sample['text_original'][:80]}...")

    # --- Appel 1 : format json, langue imposee ---
    print(f"\n--- Appel 1 : json + language='fr' | Appel 2 : text ---")
    t0 = time.time()
    with open(sample['path'], 'rb') as audio_file:
        ex1_json = client.audio.transcriptions.create(
            model=stt_model,
            file=audio_file,
            response_format="json",
            language="fr"
        )
    t1 = time.time()
    # --- Appel 2 : format text, chaine brute ---
    with open(sample['path'], 'rb') as audio_file:
        ex1_text = client.audio.transcriptions.create(
            model=stt_model,
            file=audio_file,
            response_format="text"
        )
    print(f"json : {t1-t0:.2f}s | text : {time.time()-t1:.2f}s")
    print(f"Type des reponses : json -> {type(ex1_json).__name__}, text -> {type(ex1_text).__name__}")
    print(f'Les deux résultats sont identiques : {ex1_json.text.strip().lower() == ex1_text.strip().lower()}')
    print(f"JSON : {ex1_json.text}")
    print(f"Text : {ex1_text}")
EXEMPLE GUIDE 1 : PARAMETRES AVANCES
=============================================
Fichier source : sample_fr.mp3
Texte original : La reconnaissance vocale permet de convertir la parole en texte. Cette technolog...

--- Appel 1 : json + language='fr' | Appel 2 : text ---
json : 1.05s | text : 1.07s
Type des reponses : json -> Transcription, text -> str
Les deux résultats sont identiques : True
JSON : La reconnaissance vocale permet de convertir la parole en texte. Cette technologie utilise des réseaux de neurones profonds pour analyser le signal audio et identifier les mots-prononces.
Text : La reconnaissance vocale permet de convertir la parole en texte. Cette technologie utilise des réseaux de neurones profonds pour analyser le signal audio et identifier les mots-prononces.

Lecture du résultat — exemple guidé 1 (paramètres avancés)

  • Les deux résultats sont identiques : True : imposer language="fr" n’a rien changé sur cet échantillon, parce que la détection automatique trouvait déjà french (transcription de base ci-dessus). Le paramètre language sert quand la détection peut se tromper : audio très court, accent marqué, langues mélangées. L’exercice 1 le met à l’épreuve sur l’échantillon multi.
  • Type des reponses : json -> Transcription, text -> str : le format text ne renvoie pas d’objet, ex1_text est directement la chaîne. C’est pourquoi le code écrit ex1_text.strip() sans .text. Changer de format de réponse oblige à changer la façon de lire la réponse.
  • Les deux temps de réponse : json : 1.05s | text : 1.07s à cette exécution, soit 0.02 s d’écart ; dans le rendu d’origine, c’était l’appel json le plus lent. Avec un seul appel par format, l’écart relève du réseau, pas du format : pour comparer des latences, il faut répéter les appels et comparer des médianes.
  • Le texte lui-même n’est pas celui de la source : Whisper écrit réseaux et mots-prononces, alors que le texte synthétisé en section 1 porte reseaux et mots prononces. Les deux transcriptions sont identiques entre elles ; savoir si elles sont fidèles est une autre question, que l’exercice 1 mesure.

Exercice 1 : Mesurer la fidélité d’une transcription (taux d’erreur par mot)

L’exemple guidé 1 a comparé deux transcriptions entre elles et les a trouvées identiques. Il ne dit pas si elles sont fidèles au texte prononcé. La mesure standard est le taux d’erreur par mot (WER, Word Error Rate) : le nombre minimal de substitutions, de suppressions et d’insertions de mots qui transforment la référence en transcription, divisé par le nombre de mots de la référence.

Contexte : un WER dépend autant de la normalisation des textes que du modèle. Le texte source de l’échantillon français (section 1) est écrit sans accents (reseaux, prononces) ; Whisper, lui, écrit les accents et la ponctuation à sa façon. Sans normalisation, ces écarts d’écriture comptent comme des erreurs de reconnaissance.

Étapes : 1. Écrire normaliser(texte) : minuscules, accents retirés, ponctuation et tirets remplacés par des espaces, puis découpage en mots 2. Écrire wer(mots_reference, mots_hypothese) par programmation dynamique (distance d’édition au niveau des mots) 3. Calculer le WER de la transcription de l’exemple guidé 1 (ex1_json.text) contre audio_samples["fr"]["text_original"], d’abord brut (texte.lower().split()), puis normalisé : combien d’« erreurs » disparaissent ? 4. Transcrire l’échantillon multi (alternance français/anglais, que la section 4 étudiera) deux fois : avec détection automatique, puis avec language="fr" imposé. Comparer les deux WER normalisés : que fait Whisper de la phrase anglaise quand on lui impose le français ?

Indices : - # Indice : unicodedata.normalize("NFD", texte) sépare chaque lettre de son accent ; on retire ensuite les caractères de catégorie "Mn" (unicodedata.category(c)) - # Indice : re.sub(r"[^\w\s]", " ", texte) remplace ponctuation et tirets par des espaces - # Indice : table d de taille (len(ref)+1) x (len(hyp)+1), avec d[i][0] = i, d[0][j] = j et d[i][j] = min(d[i-1][j] + 1, d[i][j-1] + 1, d[i-1][j-1] + (ref[i-1] != hyp[j-1]))

# Exercice 1 : Taux d'erreur par mot (WER) et effet de la normalisation
# TODO etudiant : mesurer la fidelite des transcriptions au texte source
import re
import unicodedata

# Etape 1 : Normalisation (minuscules, sans accents, sans ponctuation ni tirets)
def normaliser(texte):
    """Retourne la liste des mots normalises de `texte`."""
    # Indice : unicodedata.normalize("NFD", ...) puis retirer les caracteres de categorie "Mn"
    return None  # TODO etudiant

# Etape 2 : Distance d'edition au niveau des mots, rapportee a la longueur de la reference
def wer(mots_reference, mots_hypothese):
    """(substitutions + suppressions + insertions) / len(mots_reference)."""
    # Indice : table de programmation dynamique (len(ref)+1) x (len(hyp)+1)
    return None  # TODO etudiant

# Etape 3 : WER brut puis WER normalise de la transcription de l'exemple guide 1
wer_brut = None  # TODO etudiant
wer_normalise = None  # TODO etudiant

# Etape 4 : Echantillon "multi" -- detection automatique contre language="fr" impose
wer_multi_auto = None  # TODO etudiant
wer_multi_fr = None  # TODO etudiant

print("Exercice a completer")
Exercice a completer

Synthese de la Section 2

Cette section a demontre les capacites fondamentales de transcription de Whisper.

Résultats obtenus : - Transcription fidele (texte original reproduit avec precision) - Detection automatique de langue (french identifiee sans paramètre) - Timestamps a deux niveaux (segments de 3-4s, mots individuels) - Metadonnees completes (duree 11.8s, 28 mots, 3 segments)

Formats de sortie explores : - json : Texte simple, usage general - verbose_json : Metadonnees completes (timestamps, langue, duree)

Transition : Après avoir maitrise la transcription de base, nous allons explorer les formats specialises pour le sous-titrage (SRT, VTT) qui sont essentiels pour les applications video.

Section 3 : Formats de sortie specialises

Whisper supporte des formats standards pour le sous-titrage :

Format Description Usage
srt SubRip Subtitle Lecteurs video, YouTube
vtt WebVTT Navigateurs web, HTML5 video
text Texte brut Traitement de texte, NLP
# Formats de sous-titrage
print("FORMATS DE SOUS-TITRAGE")
print("=" * 45)

if "fr" in audio_samples:
    sample = audio_samples["fr"]
    subtitle_formats = ["text", "srt", "vtt"]

    for fmt in subtitle_formats:
        print(f"\n--- Format : {fmt.upper()} ---")
        with open(sample['path'], 'rb') as audio_file:
            result = client.audio.transcriptions.create(
                model=stt_model,
                file=audio_file,
                response_format=fmt
            )

        # Le resultat est une chaine pour text/srt/vtt
        output_text = result if isinstance(result, str) else str(result)
        print(output_text[:300])

        # Sauvegarde
        if save_results:
            ext = fmt
            result_file = OUTPUT_DIR / f"transcription_fr.{ext}"
            with open(result_file, 'w', encoding='utf-8') as f:
                f.write(output_text)
            print(f"Sauvegarde : {result_file.name}")

    print(f"\nTous les formats generes avec succes")
else:
    print("Aucun echantillon audio disponible")
FORMATS DE SOUS-TITRAGE
=============================================

--- Format : TEXT ---
La reconnaissance vocale permet de convertir la parole en texte. Cette technologie utilise des réseaux de neurones profonds pour analyser le signal audio et identifier les mots-prononces.

Sauvegarde : transcription_fr.text

--- Format : SRT ---
1
00:00:00,000 --> 00:00:03,600
La reconnaissance vocale permet de convertir la parole en texte.

2
00:00:04,480 --> 00:00:07,680
Cette technologie utilise des réseaux de neurones profonds

3
00:00:07,680 --> 00:00:11,600
pour analyser le signal audio et identifier les mots-prononces.



Sauvegarde : transcription_fr.srt

--- Format : VTT ---
WEBVTT

00:00:00.000 --> 00:00:03.600
La reconnaissance vocale permet de convertir la parole en texte.

00:00:04.480 --> 00:00:07.680
Cette technologie utilise des réseaux de neurones profonds

00:00:07.680 --> 00:00:11.600
pour analyser le signal audio et identifier les mots-prononces.


Sauvegarde : transcription_fr.vtt

Tous les formats generes avec succes

Interpretation : Formats de sous-titrage

Format Structure Usage typique
SRT Numéro + timestamps HH:MM:SS,ms Lecteurs video classiques, YouTube
VTT HEADER WEBVTT + timestamps HH:MM:SS.mmm Web moderne, HTML5 video, streaming
Text Texte brut sans metadonnees Traitement NLP, indexation

Points cles : 1. Le format SRT est le plus universellement compatible 2. VTT offre plus de fonctionnalites (styles, positionnement) 3. Les timestamps sont generes automatiquement par Whisper 4. La duree de chaque sous-titre est calculee par segmentation intelligente

Note technique : Whisper segmente le texte en phrases logiques (pauses naturelles), pas uniquement par duree. Cela produit des sous-titres plus lisibles que decouper arbitrairement tous les N caractères.

Exemple guidé 2 : Génération manuelle de sous-titres SRT à partir de timestamps

Contribution étudiante de @pierre-ingrachen (PR #18549), intégrée comme exemple guidé.

L’API Whisper peut retourner des timestamps au niveau des mots. Cet exemple reconstruit manuellement un fichier SRT en regroupant les mots en sous-titres de six mots au plus.

Contexte : Les plateformes vidéo (YouTube, Vimeo) nécessitent des sous-titres précis. Whisper génère les timestamps mot par mot, mais il faut les regrouper en lignes lisibles.

Étapes : 1. Transcrire un audio avec verbose_json et timestamp_granularities=["word"] 2. Extraire la liste des mots avec leurs timestamps 3. Regrouper les mots par blocs (ici six mots au plus : MAX_WORDS_PER_LINE = 6) 4. Formater chaque bloc au format SRT (numéro, timestamps, texte) 5. Sauvegarder le résultat dans un fichier .srt

Points clés du code : - ex2_result.words contient la liste des mots avec .start, .end, .word - le format SRT écrit les timestamps HH:MM:SS,mmm --> HH:MM:SS,mmm - seconds_to_srt_time passe tout en millisecondes entières puis enchaîne des divmod : c’est plus sûr que d’afficher un timedelta, dont la conversion en texte omet les millisecondes quand elles sont nulles et ne complète pas les heures sur deux chiffres

# Exemple guide 2 : Generation manuelle de sous-titres SRT a partir de timestamps
# Contribution etudiante de @pierre-ingrachen (PR #18549)

from datetime import timedelta

# Etape 1 : Transcrire avec timestamps au niveau mot
# (echantillon francais genere en section 1)
with open(audio_samples["fr"]["path"], 'rb') as audio_file:
    ex2_result = client.audio.transcriptions.create(
        model=stt_model,
        file=audio_file,
        response_format="verbose_json",
        timestamp_granularities=["word"]
    )
ex2_words = ex2_result.words
print(f"{len(ex2_words)} mots recus")

# Etape 2 : Fonction de conversion secondes -> format SRT
def seconds_to_srt_time(seconds):
    """Convertit des secondes (float) en format HH:MM:SS,mmm."""
    ms = round(seconds * 1000)   # tout en millisecondes entieres
    s, ms = divmod(ms, 1000)
    m, s = divmod(s, 60)
    h, m = divmod(m, 60)
    return f"{h:02d}:{m:02d}:{s:02d},{ms:03d}"

# Etape 3 : Regrouper les mots par blocs de max 6 mots
MAX_WORDS_PER_LINE = 6
srt_blocks = []  # liste de (debut, fin, texte) pour chaque bloc
for i in range(0, len(ex2_words), MAX_WORDS_PER_LINE):
    paquet = ex2_words[i:i + MAX_WORDS_PER_LINE]
    debut = paquet[0].start
    fin = paquet[-1].end
    texte = " ".join(w.word.strip() for w in paquet)
    srt_blocks.append((debut, fin, texte))
print(f"{len(srt_blocks)} blocs de sous-titres")

# Etape 4 : Formater en SRT
srt_content = ""
for numero, (debut, fin, texte) in enumerate(srt_blocks, start=1):
    srt_content += f"{numero}\n"
    srt_content += f"{seconds_to_srt_time(debut)} --> {seconds_to_srt_time(fin)}\n"
    srt_content += f"{texte}\n\n"

# Etape 5 : Sauvegarder
result_file = OUTPUT_DIR / "ex2_custom.srt"
with open(result_file, 'w', encoding='utf-8') as f:
    f.write(srt_content)
print(f"Sauvegarde : {result_file.name}")
print(srt_content[:300])
28 mots recus
5 blocs de sous-titres
Sauvegarde : ex2_custom.srt
1
00:00:00,000 --> 00:00:02,380
La reconnaissance vocale permet de convertir

2
00:00:02,380 --> 00:00:05,440
la parole en texte Cette technologie

3
00:00:05,440 --> 00:00:07,640
utilise des réseaux de neurones profonds

4
00:00:07,640 --> 00:00:09,960
pour analyser le signal audio et

5
00:00:09,9

Lecture du résultat — exemple guidé 2 (sous-titres SRT)

  • 28 mots recus → 5 blocs : 28 = 4 × 6 + 4, soit quatre blocs de six mots et un dernier de quatre. L’affichage s’arrête au milieu du bloc 5 parce que le code n’imprime que srt_content[:300] ; le fichier ex2_custom.srt est complet.
  • Les blocs sont jointifs : le bloc 1 finit à 00:00:02,380, fin de « convertir », et le bloc 2 commence au même instant, début de « la ». Dans une parole continue, Whisper donne à chaque mot un début égal à la fin du mot précédent.
  • Le bloc 2 enjambe la fin de la première phrase : la parole en texte Cette technologie reste affiché de 2,38 s à 5,44 s. Or la liste des mots de la transcription de base montre un silence de plus d’une seconde entre « texte » (fin à 3,60 s) et « Cette » (début à 4,70 s), et c’est exactement là que Whisper clôt son premier segment ([0.0s - 3.6s]) et ouvre le deuxième ([4.7s - 7.6s]). Le découpage fixe de six mots ignore cette pause.
  • La ponctuation a disparu : texte Cette n’a plus de point. Les timestamps mot par mot ne portent pas la ponctuation, que seul le texte complet conserve. Le découpage ne peut donc s’appuyer que sur le temps. C’est l’objet de l’exercice 2 : couper sur les pauses et borner la durée.

Exercice 2 : Sous-titres lisibles — couper sur les pauses, borner la durée

L’exemple guidé 2 regroupe les mots six par six, sans regarder le temps qui les sépare. Un bloc peut donc enjamber la fin d’une phrase, et la ponctuation, absente des timestamps mot par mot, ne peut plus signaler la coupure. Un sous-titre professionnel se coupe au contraire sur les pauses de la parole, avec une durée et une longueur bornées.

Contexte : trois contraintes courantes de lisibilité — couper dès qu’un silence atteint PAUSE_MIN secondes, ne pas afficher un sous-titre plus de DUREE_MAX secondes, ne pas dépasser CARACTERES_MAX caractères. La vitesse de lecture sert ensuite de contrôle : au-delà d’environ 17 caractères par seconde, un sous-titre défile trop vite pour être lu.

Étapes : 1. Écrire decouper(mots, pause_min, duree_max, caracteres_max) qui parcourt ex2_words (exemple guidé 2) et ouvre un nouveau bloc dès qu’une des trois contraintes l’exige 2. Compter, pour les blocs de l’exemple guidé 2 et pour les vôtres, les violations de chaque contrainte (pause interne, durée, longueur) 3. Calculer la vitesse de lecture (caractères par seconde) de chaque bloc et signaler ceux qui dépassent VITESSE_MAX 4. Sauvegarder vos blocs au format SRT dans ex2_pauses.srt, en réutilisant seconds_to_srt_time

Indices : - # Indice : la pause entre deux mots consécutifs vaut mot.start - mot_precedent.end - # Indice : les blocs de l’exemple guidé 2 sont les tranches ex2_words[i:i + MAX_WORDS_PER_LINE] ; les recalculer donne accès aux mots de chaque bloc - # Indice : un mot seul qui dépasse une contrainte forme son propre bloc ; la boucle ne doit jamais produire de bloc vide

# Exercice 2 : Sous-titres lisibles -- decoupage sur les pauses, duree bornee
# TODO etudiant : regrouper les mots en respectant pauses, duree et longueur

PAUSE_MIN = 0.25       # silence (s) qui impose une coupure
DUREE_MAX = 3.0        # duree maximale d'un sous-titre (s)
CARACTERES_MAX = 42    # longueur maximale d'un sous-titre (caracteres)
VITESSE_MAX = 17       # vitesse de lecture maximale (caracteres par seconde)

# Etape 1 : Decoupage guide par les trois contraintes
def decouper(mots, pause_min, duree_max, caracteres_max):
    """Retourne une liste de (debut, fin, texte) respectant les trois contraintes."""
    # Indice : ouvrir un nouveau bloc si la pause atteint pause_min,
    # ou si ajouter le mot ferait depasser duree_max ou caracteres_max
    return []  # TODO etudiant

blocs_pauses = decouper(ex2_words, PAUSE_MIN, DUREE_MAX, CARACTERES_MAX)

# Etape 2 : Violations de chaque contrainte -- exemple guide 2 contre vos blocs
violations = None  # TODO etudiant

# Etape 3 : Vitesse de lecture (caracteres par seconde) de chaque bloc
vitesses = None  # TODO etudiant

# Etape 4 : Sauvegarde SRT (reutiliser seconds_to_srt_time) dans OUTPUT_DIR / "ex2_pauses.srt"

print("Exercice a completer")
Exercice a completer

Synthese de la Section 3

Cette section a explore les formats de sortie specialises pour le sous-titrage.

Résultats obtenus : - 3 formats generes automatiquement (text, SRT, VTT) - SRT : 3 sous-titres avec timestamps HH:MM:SS,ms - VTT : Format Web avec header WEBVTT - Fichiers sauvegardes dans outputs/audio/stt/

Applications : - SRT : Compatible avec YouTube, VLC, lecteurs video classiques - VTT : Integation web, HTML5 video, plateformes de streaming - Text : Traitement NLP, indexation, analyse de contenu

Transition : Maintenant que nous maitrisons les formats de sortie, nous allons explorer les capacites de traduction et de detection multilingue de Whisper, qui permettent de traiter des audio dans n’importe quelle langue.

Section 4 : Traduction et detection multilingue

L’API Whisper offre un endpoint de traduction (translations) qui traduit l’audio en anglais, quelle que soit la langue source.

Endpoint Entree Sortie
transcriptions Audio (n’importe quelle langue) Texte dans la langue source
translations Audio (n’importe quelle langue) Texte en anglais
# Traduction et detection multilingue
print("TRADUCTION ET DETECTION MULTILINGUE")
print("=" * 45)

if test_translation and "fr" in audio_samples:
    sample_fr = audio_samples["fr"]

    # --- Transcription (langue source) ---
    print("\n--- Transcription (francais -> francais) ---")
    with open(sample_fr['path'], 'rb') as audio_file:
        transcript = client.audio.transcriptions.create(
            model=stt_model,
            file=audio_file,
            response_format="json"
        )
    print(f"Resultat : {transcript.text}")

    # --- Traduction (langue source -> anglais) ---
    print("\n--- Traduction (francais -> anglais) ---")
    with open(sample_fr['path'], 'rb') as audio_file:
        translation = client.audio.translations.create(
            model=stt_model,
            file=audio_file,
            response_format="json"
        )
    print(f"Resultat : {translation.text}")

    # --- Test multilingue ---
    if "multi" in audio_samples:
        print("\n--- Detection multilingue ---")
        sample_multi = audio_samples["multi"]
        print(f"Texte original : {sample_multi['text_original']}")

        with open(sample_multi['path'], 'rb') as audio_file:
            transcript_multi = client.audio.transcriptions.create(
                model=stt_model,
                file=audio_file,
                response_format="verbose_json"
            )
        print(f"Transcription : {transcript_multi.text}")
        print(f"Langue detectee : {transcript_multi.language}")

    print(f"\nTraduction et detection terminees")
else:
    print("Traduction desactivee ou echantillons manquants")
TRADUCTION ET DETECTION MULTILINGUE
=============================================

--- Transcription (francais -> francais) ---
Resultat : La reconnaissance vocale permet de convertir la parole en texte. Cette technologie utilise des réseaux de neurones profonds pour analyser le signal audio et identifier les mots-prononces.

--- Traduction (francais -> anglais) ---
Resultat : Vocal recognition converts speech into text. This technology uses deep neural networks to analyze audio signals and identify pronounced words.

--- Detection multilingue ---
Texte original : Bonjour, je parle francais. Now I switch to English. Et je reviens au francais pour terminer.
Transcription : Bonjour, je parle français. Now I switch to English. Et je reviens au français pour terminer.
Langue detectee : french

Traduction et detection terminees

Interpretation : Traduction et multilingue

Aspect Valeur Signification
Traduction Francais vers anglais Whisper traduit directement sans étape intermediaire
Multilingue Detection langue dominante Whisper identifie la langue principale du fichier
Code-switching Gere partiellement Le melange de langues peut reduire la precision

Note technique : L’endpoint translations ne supporte que la traduction vers l’anglais. Pour d’autres langues cibles, combiner Whisper STT + un LLM de traduction.

Exemple guidé 3 : Pipeline de traduction et comparaison multilingue

Contribution étudiante de @pierre-ingrachen (PR #18569), intégrée comme exemple guidé.

La traduction ci-dessus partait de l’échantillon français déjà généré. Cet exemple construit un mini-pipeline complet sur un audio qu’il produit lui-même : synthèse vocale, transcription avec détection de langue, traduction vers l’anglais, et mesure du temps de chaque appel.

Objectif : enchaîner synthèse, transcription et traduction sur le même fichier, et comparer ce que renvoie chaque endpoint.

Étapes : 1. Générer un audio français par synthèse vocale (tts-1, voix nova) et le sauvegarder dans OUTPUT_DIR 2. Transcrire avec whisper-1 en format verbose_json, qui renvoie aussi la langue détectée 3. Traduire le même fichier vers l’anglais avec l’endpoint translations 4. Comparer la durée des deux appels

Points clés du code : - le fichier audio est rouvert pour chaque appel (with open(..., 'rb')) : un flux déjà lu par le premier appel serait vide pour le second - l’endpoint translations n’a pas de paramètre de langue cible : il traduit toujours vers l’anglais - time.time() avant et après chaque appel mesure le temps réseau compris, pas seulement le calcul du modèle

# Exemple guide 3 : Pipeline de traduction et comparaison multilingue
# Contribution etudiante de @pierre-ingrachen (PR #18569)

# Etape 1 : Generer un audio de test en francais (TTS)
ex3_text = "La reconnaissance vocale permet de convertir la parole en texte. " \
"Cette technologie utilise des réseaux de neurones profonds pour analyser le signal audio et identifier les mots prononcés."
ex3_tts = client.audio.speech.create(
    model="tts-1",
    voice="nova",
    input=ex3_text,
    response_format="mp3"
)
ex3_audio_path = OUTPUT_DIR / "ex3_sample.mp3"
with open(ex3_audio_path, 'wb') as f:
    f.write(ex3_tts.content)
print(f"Audio genere : {ex3_audio_path.name}")

# Etape 2 : Transcrire avec verbose_json
t0 = time.time()
with open(ex3_audio_path, 'rb') as audio_file:
    transcript_result = client.audio.transcriptions.create(
        model=stt_model,
        file=audio_file,
        response_format="verbose_json"
    )
transcript_time = time.time() - t0
transcript_lang = transcript_result.language

# Etape 3 : Traduire vers l'anglais (l'endpoint traduit toujours vers l'anglais, pas de parametre language)
t0 = time.time()
with open(ex3_audio_path, 'rb') as audio_file:
    translation_result = client.audio.translations.create(
        model=stt_model,
        file=audio_file,
        response_format="json"
    )
translation_time = time.time() - t0

# Etape 4 : Afficher la comparaison
print(f"\nTexte de base : {ex3_text}")
print(f"Langue detectee : {transcript_lang}")
print(f"Transcription : {transcript_result.text}")
print(f"Traduction : {translation_result.text}")
print(f"\nTemps transcription : {transcript_time:.2f}s | traduction : {translation_time:.2f}s")
print(f"Rapport traduction / transcription : {translation_time / transcript_time:.2f}x")
Audio genere : ex3_sample.mp3

Texte de base : La reconnaissance vocale permet de convertir la parole en texte. Cette technologie utilise des réseaux de neurones profonds pour analyser le signal audio et identifier les mots prononcés.
Langue detectee : french
Transcription : La reconnaissance vocale permet de convertir la parole en texte. Cette technologie utilise des réseaux de neurones profonds pour analyser le signal audio et identifier les mots prononcés.
Traduction : Vocal recognition converts speech into text. This technology uses deep neural networks to analyze audio signals and identify pronounced words.

Temps transcription : 0.90s | traduction : 1.52s
Rapport traduction / transcription : 1.68x

Lecture du résultat — exemple guidé 3 (pipeline de traduction)

  • Langue detectee : french : en verbose_json, Whisper renvoie le nom de la langue, en anglais, et non son code. Le paramètre language attend au contraire un code ISO ("fr", exemple guidé 1) : les deux valeurs ne se comparent pas directement.
  • La transcription est exacte : elle reproduit mot pour mot le texte de base, accent de « prononcés » compris. Sur l’échantillon de la section 1, lu par la même voix, la transcription plus haut rendait « mots-prononces » : le texte de cet échantillon écrit « prononces », sans accent.
  • La traduction est littérale : « Vocal recognition » calque « reconnaissance vocale », là où l’anglais dit speech recognition, et « identify pronounced words » traduit mot à mot. L’endpoint translations passe directement de l’audio à l’anglais, sans consigne possible sur le vocabulaire : c’est la limite qu’explore l’exercice 3.
  • Les temps ne départagent rien : 0.90 s pour la transcription et 1.52 s pour la traduction à cette exécution, contre 1.78 s et 1.92 s à l’exécution de l’étudiant. Le rapport passe de 1.08 à 1.68 : un seul appel par opération, réseau compris, ne suffit pas à conclure. Il faudrait répéter chaque appel et comparer des médianes.

Exercice 3 : Traduction directe ou en deux étapes — que perd-on en route ?

L’exemple guidé 3 traduit l’audio directement vers l’anglais avec l’endpoint translations. Une autre voie existe : transcrire d’abord en français, puis faire traduire le texte par un modèle de langage. Elle coûte un appel de plus, mais elle permet de choisir la langue cible et de donner des consignes au traducteur.

Contexte : un message vocal professionnel contient souvent des éléments qui ne doivent pas changer à la traduction : un nom propre, un montant, une date, un terme technique. Ce sont eux qu’on vérifie.

Étapes : 1. Générer par synthèse vocale le message ex3b_text, qui contient un nom, une date, un montant et un terme technique, et le sauvegarder dans OUTPUT_DIR 2. Traduction directe : endpoint translations (audio → anglais), en mesurant la durée 3. Traduction en deux étapes : transcription avec language="fr", puis traduction du texte par un modèle de chat, en mesurant la durée totale des deux appels 4. Pour chaque traduction, lister les éléments de ELEMENTS_A_PRESERVER qui ont disparu, et comparer les durées

Indices : - # Indice : pour la seconde étape, client.chat.completions.create(model=..., messages=[...]) avec une consigne du type « Traduis en anglais, sans modifier les noms propres ni les nombres » - # Indice : avec un modèle de raisonnement (par exemple gpt-5-mini), prévoyez un max_completion_tokens large (2000 ou plus) : les tokens de raisonnement sont pris sur ce budget, et un budget trop bas laisse la réponse vide - # Indice : un montant peut revenir sous la forme 4,350 ou 4 350 ; retirez espaces et virgules des deux côtés avant de comparer - # Indice : la voie en deux étapes permet une langue cible que l’endpoint translations ne sait pas produire ; essayez l’espagnol

# Exercice 3 : Traduction directe ou en deux etapes -- que perd-on en route ?
# TODO etudiant : comparer l'endpoint translations et le pipeline transcription + LLM

ex3b_text = ("Le rendez-vous avec Madame Lefèvre est reporté au 12 mars. "
             "Le devis de 4350 euros couvre la migration du cluster Kubernetes.")
ELEMENTS_A_PRESERVER = ["Lefèvre", "12", "4350", "Kubernetes"]

# Etape 1 : Generer l'audio du message (client.audio.speech.create) dans OUTPUT_DIR / "ex3b_message.mp3"
ex3b_audio_path = None  # TODO etudiant

# Etape 2 : Traduction directe (audio -> anglais) avec l'endpoint translations
traduction_directe = None  # TODO etudiant
temps_directe = None  # TODO etudiant

# Etape 3 : Transcription (language="fr") puis traduction du texte par un modele de chat
traduction_deux_etapes = None  # TODO etudiant
temps_deux_etapes = None  # TODO etudiant : duree totale des deux appels

# Etape 4 : Elements perdus dans chaque traduction, et comparaison des durees
elements_perdus = None  # TODO etudiant : {"directe": [...], "deux_etapes": [...]}

print("Exercice a completer")
Exercice a completer

Synthese de la Section 4

Cette section a explore les capacites de traduction et de detection multilingue de Whisper.

Résultats obtenus : - Traduction automatique francais → anglais de haute qualite - Detection de langue dominante (french identifiee pour l’echantillon multilingue) - Code-switching gere partiellement (alternance fr/en transcrite fidèlement) - Endpoint translations pour traduction directe vers l’anglais

Distinction critique : - transcriptions : preserve la langue source (francais → francais) - translations : traduit vers l’anglais (francais → anglais)

Limitations : - L’endpoint translations ne supporte que l’anglais comme langue cible - Pour d’autres langues cibles, combiner Whisper STT + un LLM de traduction

Transition : Après avoir explore les fonctionnalites de traduction, nous allons comparer les deux modèles STT disponibles (whisper-1 vs gpt-4o-transcribe) pour comprendre leurs différences de performance et de compatibilite.

Section 5 : Comparaison Whisper-1 vs GPT-4o-Transcribe

OpenAI propose deux modèles de transcription :

Modèle Architecture Forces Limitations
whisper-1 Whisper V2 Rapide, stable, multilingue Precision moderee sur accents forts
gpt-4o-transcribe GPT-4o Meilleure comprehension contextuelle Refuse verbose_json (pas de timestamps)
# Comparaison des modeles STT
print("COMPARAISON MODELES STT")
print("=" * 45)

stt_models_to_test = ["whisper-1"]
if compare_models:
    stt_models_to_test.append("gpt-4o-transcribe")

comparison_results = {}

if "fr" in audio_samples:
    sample = audio_samples["fr"]

    for model in stt_models_to_test:
        print(f"\n--- Modele : {model} ---")
        start_time = time.time()

        try:
            with open(sample['path'], 'rb') as audio_file:
                transcript = client.audio.transcriptions.create(
                    model=model,
                    file=audio_file,
                    response_format="json"
                )

            elapsed = time.time() - start_time

            comparison_results[model] = {
                "text": transcript.text,
                "time": elapsed,
                "words": len(transcript.text.split())
            }

            print(f"  Texte : {transcript.text}")
            print(f"  Mots : {len(transcript.text.split())}")
            print(f"  Temps API : {elapsed:.2f}s")

        except Exception as e:
            print(f"  Erreur : {str(e)[:100]}")
            comparison_results[model] = {"error": str(e)}

    # Tableau comparatif
    if len(comparison_results) > 1:
        print(f"\nTableau comparatif :")
        print(f"{'Modele':<25} {'Temps API':<12} {'Mots':<10}")
        print("-" * 47)
        for model, data in comparison_results.items():
            if "error" not in data:
                print(f"{model:<25} {data['time']:<12.2f} {data['words']:<10}")
            else:
                print(f"{model:<25} {'ERREUR':<12} {'-':<10}")
else:
    print("Aucun echantillon audio disponible")
COMPARAISON MODELES STT
=============================================

--- Modele : whisper-1 ---
  Texte : La reconnaissance vocale permet de convertir la parole en texte. Cette technologie utilise des réseaux de neurones profonds pour analyser le signal audio et identifier les mots-prononces.
  Mots : 27
  Temps API : 1.16s

--- Modele : gpt-4o-transcribe ---
  Texte : La reconnaissance vocale permet de convertir la parole en texte. Cette technologie utilise des réseaux de neurones profonds pour analyser le signal audio et identifier les mots prononcés.
  Mots : 28
  Temps API : 0.90s

Tableau comparatif :
Modele                    Temps API    Mots      
-----------------------------------------------
whisper-1                 1.16         27        
gpt-4o-transcribe         0.90         28        

Interpretation : Comparaison des modèles

Correction de @pierre-ingrachen (PR #18569) : la cellule demandait le format verbose_json, que gpt-4o-transcribe refuse (erreur 400). Elle utilise désormais json, accepté par les deux modèles, et compte les mots au lieu d’afficher la langue et la durée, que seul verbose_json fournit.

Aspect whisper-1 gpt-4o-transcribe
Fin de la seconde phrase mots-prononces mots prononcés
Nombre de mots 27 28
Temps d’appel (cette exécution) 1.16 s 0.90 s

Points clés : 1. Les deux transcriptions sont identiques, sauf sur les deux derniers mots. 2. Le texte source contient lui-même une coquille : la cellule de génération des échantillons écrit « les mots prononces », sans accent, et la synthèse vocale a pu le lire autrement que « prononcés ». whisper-1 rend « mots-prononces » ; gpt-4o-transcribe écrit « mots prononcés », la forme que la phrase appelle. C’est la « compréhension contextuelle » annoncée : le modèle corrige vers le sens. C’est un avantage pour la lecture, mais un risque quand on attend une transcription fidèle à ce qui a été dit. 3. L’écart de 27 à 28 mots vient du tiret, pas d’un mot manquant : compter les mots mesure le découpage, pas la justesse. Le taux d’erreur par mot contre le texte de référence (exercice 1) est une mesure plus sûre. 4. Une seule mesure ne départage pas les deux modèles : les temps d’appel incluent le réseau et varient d’une exécution à l’autre (de 0.9 à 2.5 s sur les deux exécutions du 30/09). 5. gpt-4o-transcribe refuse verbose_json : pour les timestamps et les sous-titres, whisper-1 reste nécessaire.

Note technique : l’exemple guidé 3 fait lire la même phrase, avec l’accent, par la même voix, et whisper-1 y écrit correctement « mots prononcés ». L’écart observé ici tient donc plus probablement au texte source qu’au modèle. Les deux modèles ont le même tarif ($0.006/minute) ; le choix se fait sur le besoin : formats et timestamps (whisper-1) ou compréhension contextuelle (gpt-4o-transcribe).

La comparaison des modèles etant complete, le mode interactif permet d’experimenter librement la transcription sur vos propres fichiers audio en ajustant les paramètres en temps reel.

# Mode interactif
if notebook_mode == "interactive" and not skip_widgets:
    print("MODE INTERACTIF")
    print("=" * 50)
    print("\nEntrez un texte a synthetiser puis transcrire (round-trip) :")
    print("(Laissez vide pour passer a la suite)")

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

        if user_text.strip():
            # Generation TTS
            print(f"\n1. Generation TTS...")
            tts_response = client.audio.speech.create(
                model="tts-1",
                voice="nova",
                input=user_text,
                response_format="mp3"
            )
            print(f"   Audio genere ({len(tts_response.content)/1024:.1f} KB)")
            display_audio_bundle(tts_response.content)

            # Sauvegarde temporaire pour transcription
            temp_path = OUTPUT_DIR / "interactive_sample.mp3"
            with open(temp_path, 'wb') as f:
                f.write(tts_response.content)

            # Transcription
            print(f"\n2. Transcription STT...")
            with open(temp_path, 'rb') as audio_file:
                transcript = client.audio.transcriptions.create(
                    model=stt_model,
                    file=audio_file,
                    response_format="json"
                )

            print(f"   Original    : {user_text}")
            print(f"   Transcrit   : {transcript.text}")

            # Comparaison
            match = user_text.lower().strip() == transcript.text.lower().strip()
            print(f"   Correspondance exacte : {'Oui' if match else 'Non'}")
        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 puis transcrire (round-trip) :
(Laissez vide pour passer a la suite)
Mode interactif non disponible (execution automatisee)

Interpretation : Mode interactif

Le mode interactif permet d’explorer le pipeline TTS→STT en temps reel. Cette approche “round-trip” est pedagogique pour comprendre :

Étape Opération Verification
1 Synthese TTS Generation audio depuis texte
2 Transcription STT Reconversion audio vers texte
3 Comparaison Mesure de la fidelite de la chaîne

Cas d’usage : - Test de prononciation : verifier comment un mot est transcrit - Evaluation de qualite : mesurer la degradation TTS→STT - Experimentation : tester différents accents ou langues

Note technique : En mode batch (Papermill, MCP), l’interface interactive est desactivee automatiquement via le paramètre skip_widgets=True. Cela evite les blocages lors de l’exécution automatisee.

Synthese de la Section 5

Cette section a compare les deux modèles STT disponibles dans l’API OpenAI.

Résultats obtenus : - whisper-1 : Modèle polyvalent, stable, compatible avec tous les formats - gpt-4o-transcribe : Meilleure comprehension contextuelle mais restrictions de format - Temps d’appel : de 0.9 à 2.5 s selon le modèle et l’exécution, réseau compris ; une seule mesure ne départage pas les deux modèles - Cout identique : $0.006/minute pour les deux modèles

Critères de choix :

Scénario Modèle recommande
Sous-titrage, timestamps whisper-1
Audio avec bruit de fond gpt-4o-transcribe
Formats sorties multiples whisper-1
Comprehension contextuelle gpt-4o-transcribe

Transition : Après avoir explore toutes les fonctionnalites de l’API Whisper, nous allons maintenant examiner les bonnes pratiques, les couts et les prochaines étapes pour approfondir vos connaissances en reconnaissance vocale.

Bonnes pratiques et analyse des couts

Optimisation de la transcription

Stratégie Description Impact
Specifier la langue language="fr" Precision accrue, latence reduite
Audio propre Reduire le bruit de fond Meilleure qualite de transcription
Segmenter les longs fichiers Decouper en segments < 25 MB Evite les timeouts
Utiliser verbose_json Exploiter les timestamps Utile pour le sous-titrage

Grille tarifaire (Janvier 2025)

Modèle Cout Equivalent
whisper-1 $0.006 / minute ~$0.36 / heure
gpt-4o-transcribe $0.006 / minute ~$0.36 / heure
# 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 STT : {stt_model}")
print(f"Format reponse : {response_format}")
print(f"Echantillons generes : {len(audio_samples)}")

if comparison_results:
    print(f"Modeles compares : {list(comparison_results.keys())}")

if save_results:
    saved = list(OUTPUT_DIR.glob('*'))
    print(f"Fichiers sauvegardes : {len(saved)} dans {OUTPUT_DIR.relative_to(GENAI_ROOT)}")

print(f"\nPROCHAINES ETAPES")
print(f"1. Decouvrir les operations audio de base (01-3-Basic-Audio-Operations)")
print(f"2. Tester Whisper en local avec GPU (01-4-Whisper-Local)")
print(f"3. Essayer le TTS local avec Kokoro (01-5-Kokoro-TTS-Local)")
print(f"4. Explorer la comparaison multi-modeles (03-1)")

print(f"\nNotebook Whisper STT termine - {datetime.now().strftime('%H:%M:%S')}")
STATISTIQUES DE SESSION
=============================================
Date : 2026-09-30 11:29:02
Modele STT : whisper-1
Format reponse : verbose_json
Echantillons generes : 3
Modeles compares : ['whisper-1', 'gpt-4o-transcribe']
Fichiers sauvegardes : 6 dans outputs\audio\stt

PROCHAINES ETAPES
1. Decouvrir les operations audio de base (01-3-Basic-Audio-Operations)
2. Tester Whisper en local avec GPU (01-4-Whisper-Local)
3. Essayer le TTS local avec Kokoro (01-5-Kokoro-TTS-Local)
4. Explorer la comparaison multi-modeles (03-1)

Notebook Whisper STT termine - 11:29:02

CHALLENGE BONUS - Sous-titrage Automatique

Points : 0.5 pts

Objectif

Créer des sous-titres synchronisés pour un court extrait audio.

Ce que vous avez appris

Ce notebook montre: - Section 2 : Transcription avec timestamps (verbose_json) - Section 3 : Formats de sous-titrage (SRT, VTT) - Section 4 : Traduction audio vers anglais

Critères de succes

Contraintes techniques

  • Utiliser les timestamps de transcript.words
  • Regrouper les mots en segments de 5-8 mots maximum
  • Format SRT standard avec numérotation

Soumission : PR avec titre “Challenge #9 - [Votre Nom]”, fichier SRT et extrait audio


Conclusion : Whisper STT - Synthese

Ce notebook a explore l’API OpenAI Whisper pour la reconnaissance vocale, de la transcription de base aux formats specialises de sous-titrage.

Competences acquises : - Transcription d’audio avec Whisper API (formats json, verbose_json, text, srt, vtt) - Utilisation des timestamps (segments et mots) pour le sous-titrage - Detection automatique de langue et traduction vers l’anglais - Comparaison des modèles whisper-1 et gpt-4o-transcribe - Bonnes pratiques pour l’optimisation des couts et de la qualite

Points cles a retenir : 1. Whisper offre une fidelite de transcription très elevee sur les audio propres 2. Le format verbose_json avec timestamp_granularities=["word"] est ideal pour le sous-titrage précis 3. La detection de langue automatique est fiable pour les langues courantes 4. L’endpoint translations traduit directement vers l’anglais (pas d’autres langues cibles) 5. whisper-1 est plus polyvalent, gpt-4o-transcribe offre une meilleure comprehension contextuelle

Prochaines étapes : - Explorer les opérations audio de base (01-3-Basic-Audio-Opérations) - Tester Whisper en local avec GPU (01-4-Whisper-Local) - Decouvrir le TTS local avec Kokoro (01-5-Kokoro-TTS-Local) - Comparer les modèles STT/LLM multi-fournisseurs (03-1)

Ressources : - Documentation OpenAI Audio API : https://platform.openai.com/docs/guides/speech-to-text - Modèle Whisper original (OpenAI) : https://github.com/openai/whisper - Grille tarifaire a jour : https://openai.com/pricing

Retour au sommet