Aller au contenu principal
Leçon 6 sur 16 · Voxtral Audio — Transcription et Speech-to-Text

Timestamps et Segmentation Temporelle

Leçon 6 sur 16 Mis à jour le 3 novembre 2026 Développeur
Sur cette page
  1. Structurer la transcription dans le temps
  2. Granularité segment : le bloc de parole
  3. Et la granularité mot ?
  4. Générer des sous-titres SRT
  5. Naviguer dans un enregistrement long
  6. Points clés à retenir

Cours mis à jour le 3 novembre 2026 pour Voxtral 3, d’après le guide Migrate from Voxtral 2 de Mistral : les modèles Voxtral 2 (voxtral-mini-2602, voxtral-mini-transcribe-realtime-2602, voxtral-mini-tts-2603), dépréciés le 31 octobre 2026 et retirés le 30 avril 2027, sont remplacés dans les exemples par voxtral-mini-transcribe-3, voxtral-mini-transcribe-realtime-3 et voxtral-mini-tts-3-0. À connaître : horodatage par segment uniquement (word renvoie une erreur 400), diarisation possible en temps réel, réglages d’une session temps réel figés dès le premier morceau d’audio, entrées context_bias libres (phrases avec espaces). Sources : Migrate from Voxtral 2 · Speech to Text.

Structurer la transcription dans le temps

Une transcription brute est utile, mais une transcription horodatée ouvre des possibilités bien plus riches : sous-titrage, navigation temporelle, synchronisation avec la vidéo, extraction d’un passage précis. Sans timestamps, retrouver dans un enregistrement de deux heures le moment où le budget a été évoqué relève de l’écoute au jugé ; avec eux, c’est une recherche textuelle suivie d’un clic. Le paramètre timestamp_granularities de Voxtral vous donne ce pouvoir.

Granularité segment : le bloc de parole

Un segment est un bloc de parole continu — typiquement une phrase ou un groupe de phrases prononcées sans pause significative. C’est la granularité que vous choisirez par défaut, parce qu’elle correspond à l’unité de lecture naturelle.

import os
from mistralai.client import Mistral

client = Mistral(api_key=os.environ["MISTRAL_API_KEY"])

with open("conference.mp3", "rb") as f:
    response = client.audio.transcriptions.complete(
        model="voxtral-mini-transcribe-3",
        file={"content": f, "file_name": "conference.mp3"},
        timestamp_granularities=["segment"]
    )

# Parcourir les segments
for seg in response.segments:
    debut = f"{seg.start:.1f}s"
    fin = f"{seg.end:.1f}s"
    print(f"[{debut} → {fin}] {seg.text}")

La sortie ressemble à ceci :

[0.0s → 4.2s] Bonjour à tous, merci d'être présents aujourd'hui.
[4.5s → 11.8s] Nous allons aborder les résultats du trimestre et les perspectives pour la suite.
[12.1s → 18.3s] Commençons par le chiffre d'affaires qui a progressé de douze pour cent.

Ce découpage sert la navigation dans un enregistrement long, l’affichage de blocs de sous-titres et l’indexation par passage : chaque segment devient une entrée consultable, avec son point d’entrée dans l’audio.

Et la granularité mot ?

Voxtral 2 savait aussi horodater chaque mot (timestamp_granularities=["word"]). Voxtral 3 ne le propose plus : la granularité word renvoie une erreur 400, et le guide de migration de Mistral demande de passer aux segments. Les usages qui exigeaient le calage mot à mot — sous-titres façon karaoké, mise en surbrillance du mot en cours, analyse fine du débit — doivent donc s’appuyer sur les segments, quitte à répartir le temps d’un segment entre ses mots par une estimation côté client.

def estimer_mots(segment):
    """Répartit la durée d'un segment entre ses mots, au prorata de leur longueur (estimation)."""
    mots = segment.text.split()
    if not mots or segment.start is None or segment.end is None:
        return []
    total = sum(len(m) for m in mots)
    debut, duree = segment.start, segment.end - segment.start
    resultat = []
    for mot in mots:
        fin = debut + duree * len(mot) / total
        resultat.append((round(debut, 2), round(fin, 2), mot))
        debut = fin
    return resultat

C’est une approximation, utile pour un affichage progressif, pas pour une analyse prosodique : si le calage exact de chaque mot est indispensable, un outil d’alignement forcé dédié, appliqué après la transcription, reste la solution.

Générer des sous-titres SRT

Le format SRT n’a rien de mystérieux : un numéro d’ordre, une plage horaire au format HH:MM:SS,mmm, le texte, une ligne vide. La fonction ci-dessous parcourt les segments et découpe ceux qui dépasseraient la largeur d’affichage confortable, fixée ici à soixante caractères.

def generer_srt_depuis_segments(segments, max_chars=60):
    """Génère un fichier SRT à partir des segments Voxtral."""
    lignes_srt = []
    compteur = 1

    for seg in segments:
        texte = seg.text.strip()
        if not texte:
            continue

        # Découper les segments trop longs
        mots = texte.split()
        sous_texte = ""
        for mot in mots:
            if len(sous_texte) + len(mot) + 1 > max_chars and sous_texte:
                lignes_srt.append(str(compteur))
                lignes_srt.append(
                    f"{formater_temps_srt(seg.start)} --> "
                    f"{formater_temps_srt(seg.end)}"
                )
                lignes_srt.append(sous_texte.strip())
                lignes_srt.append("")
                compteur += 1
                sous_texte = mot
            else:
                sous_texte += " " + mot

        if sous_texte.strip():
            lignes_srt.append(str(compteur))
            lignes_srt.append(
                f"{formater_temps_srt(seg.start)} --> "
                f"{formater_temps_srt(seg.end)}"
            )
            lignes_srt.append(sous_texte.strip())
            lignes_srt.append("")
            compteur += 1

    return "\n".join(lignes_srt)

def formater_temps_srt(secondes):
    """Convertit des secondes en format SRT (HH:MM:SS,mmm)."""
    h = int(secondes // 3600)
    m = int((secondes % 3600) // 60)
    s = int(secondes % 60)
    ms = int((secondes % 1) * 1000)
    return f"{h:02d}:{m:02d}:{s:02d},{ms:03d}"

# Utilisation
srt_contenu = generer_srt_depuis_segments(response.segments)
with open("sous_titres.srt", "w", encoding="utf-8") as f:
    f.write(srt_contenu)

print("Fichier SRT généré avec succès")

Autre exploitation directe des segments : construire un sommaire temporel. Le script suivant relève un repère toutes les cinq minutes et en affiche les premiers mots, ce qui donne au lecteur une carte de l’enregistrement avant même de l’écouter.

def creer_index_temporel(segments, intervalle_minutes=5):
    """Crée un index toutes les N minutes."""
    index = []
    prochain_jalon = 0

    for seg in segments:
        if seg.start >= prochain_jalon * 60:
            minutes = int(seg.start // 60)
            secondes = int(seg.start % 60)
            apercu = seg.text[:80] + "..." if len(seg.text) > 80 else seg.text
            index.append(f"[{minutes:02d}:{secondes:02d}] {apercu}")
            prochain_jalon = minutes + intervalle_minutes

    return index

# Utilisation
for entree in creer_index_temporel(response.segments, intervalle_minutes=5):
    print(entree)

Avec Voxtral 2, timestamp_granularities était incompatible avec le paramètre language. La documentation de Voxtral 3 ne mentionne plus cette restriction : vérifiez-le sur un essai avant de combiner les deux dans un traitement en production.

Points clés à retenir

  • Une seule granularité avec Voxtral 3 : "segment" ("word" renvoie une erreur 400)
  • Le calage mot à mot, s’il est nécessaire, s’estime à partir des segments ou passe par un outil d’alignement
  • Les timestamps sont en secondes (nombres décimaux), start et end pour chaque segment
  • Idéal pour le sous-titrage SRT/VTT, la navigation temporelle, l’indexation
  • L’incompatibilité avec language de Voxtral 2 n’est plus documentée : vérifiez-la sur un essai