Aller au contenu principal

Contrôle de la prosodie et du débit

Contrôle de la prosodie et du débit

La prosodie — l’intonation, le rythme, l’accentuation et les pauses — est ce qui distingue une voix synthétique robotique d’une voix naturelle. Bien que l’API TTS d’OpenAI ne propose pas de contrôles SSML explicites, plusieurs techniques permettent d’influencer finement le rendu vocal pour produire un résultat professionnel.

Contrôle du débit par le paramètre speed

L’API TTS propose un paramètre speed qui ajuste la vitesse de parole :

from openai import OpenAI

client = OpenAI()

# Vitesse normale (1.0)
response_normal = client.audio.speech.create(
    model="tts-1",
    voice="nova",
    input="Votre rendez-vous est confirmé pour demain à quatorze heures.",
    speed=1.0
)

# Plus lent (0.75) — pour les instructions complexes
response_slow = client.audio.speech.create(
    model="tts-1",
    voice="nova",
    input="Votre rendez-vous est confirmé pour demain à quatorze heures.",
    speed=0.75
)

# Plus rapide (1.25) — pour les notifications courtes
response_fast = client.audio.speech.create(
    model="tts-1",
    voice="nova",
    input="Votre rendez-vous est confirmé pour demain à quatorze heures.",
    speed=1.25
)
Vitesse Plage Usage
Lent 0.25 — 0.75 Instructions complexes, accessibilité, personnes âgées
Normal 0.9 — 1.1 Conversations, formations, narration
Rapide 1.25 — 2.0 Notifications, confirmations, récapitulatifs

Techniques de formatage du texte

Le principal levier pour contrôler la prosodie est le formatage du texte d’entrée. Le modèle TTS interprète la ponctuation et la structure pour adapter son débit et son intonation.

Pauses

# Pause courte : virgule
text_comma = "Première étape, vérifiez votre configuration."

# Pause moyenne : point-virgule ou tiret
text_semi = "Première étape — vérifiez votre configuration."

# Pause longue : point ou points de suspension
text_dots = "Première étape. Vérifiez votre configuration."
text_ellipsis = "Première étape... vérifiez votre configuration."

# Pause très longue : paragraphe séparé
text_para = "Première étape.\n\nVérifiez votre configuration."

Emphase et intonation

# Question (intonation montante)
text_question = "Souhaitez-vous confirmer cette réservation ?"

# Exclamation (emphase naturelle)
text_exclaim = "Félicitations ! Votre inscription est confirmée !"

# Emphase par capitalisation
text_caps = "C'est TRÈS important de ne pas sauter cette étape."

# Liste avec rythme (le modèle marque naturellement chaque élément)
text_list = "Vous aurez besoin de : un, votre identifiant. Deux, votre mot de passe. Trois, votre code de confirmation."

Adapter le texte au contexte vocal

Un texte écrit pour être lu et un texte écrit pour être prononcé sont très différents. Pour un agent vocal, reformatez systématiquement :

def format_for_speech(text: str) -> str:
    """Adapte un texte écrit pour une lecture vocale naturelle."""
    import re

    # Épeler les sigles courants
    abbreviations = {
        "API": "A P I",
        "URL": "U R L",
        "PDF": "P D F",
        "SMS": "S M S",
    }
    for abbr, spelled in abbreviations.items():
        text = text.replace(abbr, spelled)

    # Écrire les nombres en toutes lettres pour les petits nombres
    number_words = {
        "1": "un", "2": "deux", "3": "trois", "4": "quatre",
        "5": "cinq", "6": "six", "7": "sept", "8": "huit",
        "9": "neuf", "10": "dix"
    }
    for digit, word in number_words.items():
        text = re.sub(rf'\b{digit}\b', word, text)

    # Écrire les heures de manière vocale
    text = re.sub(r'(\d{1,2})h(\d{2})', r'\1 heures \2', text)
    text = re.sub(r'(\d{1,2})h\b', r'\1 heures', text)

    return text

original = "Votre RDV est à 14h30. Envoyez 1 SMS pour confirmer."
formatted = format_for_speech(original)
# → "Votre RDV est à 14 heures 30. Envoyez un S M S pour confirmer."

Vitesse adaptative selon le contenu

Un agent vocal intelligent adapte son débit au type de contenu :

def adaptive_speed(content_type: str, user_preference: float = 1.0) -> float:
    """Calcule la vitesse optimale selon le type de contenu."""
    base_speeds = {
        "greeting": 1.0,
        "instructions": 0.85,
        "confirmation": 1.1,
        "error_message": 0.9,
        "phone_number": 0.7,
        "address": 0.75,
        "summary": 1.15,
        "farewell": 1.0,
    }

    base = base_speeds.get(content_type, 1.0)
    # Ajuster avec la préférence utilisateur (0.5 à 1.5)
    adjusted = base * user_preference
    return max(0.25, min(4.0, adjusted))  # Borner aux limites API

# Utilisation
speed = adaptive_speed("phone_number", user_preference=0.9)
# → 0.63 (lent pour dicter un numéro)

Post-traitement audio

Pour un contrôle encore plus fin, traitez l’audio après génération :

from pydub import AudioSegment

def post_process_speech(audio_bytes: bytes, adjustments: dict) -> bytes:
    """Post-traitement audio pour ajustements fins."""
    import io
    audio = AudioSegment.from_mp3(io.BytesIO(audio_bytes))

    # Ajuster le volume
    if "volume_db" in adjustments:
        audio = audio + adjustments["volume_db"]

    # Ajouter un fondu d'entrée/sortie
    if adjustments.get("fade_in_ms"):
        audio = audio.fade_in(adjustments["fade_in_ms"])
    if adjustments.get("fade_out_ms"):
        audio = audio.fade_out(adjustments["fade_out_ms"])

    # Ajouter du silence au début (pré-roll)
    if adjustments.get("pre_silence_ms"):
        silence = AudioSegment.silent(duration=adjustments["pre_silence_ms"])
        audio = silence + audio

    buffer = io.BytesIO()
    audio.export(buffer, format="mp3")
    return buffer.getvalue()

Points clés à retenir

  • Le paramètre speed (0.25 à 4.0) contrôle la vitesse globale de parole
  • La ponctuation est le levier principal pour les pauses : virgule (courte), point (longue), ellipses (dramatique)
  • Adaptez le texte au contexte vocal : épelez les sigles, écrivez les nombres en lettres, reformulez les heures
  • La vitesse doit varier selon le contenu : lente pour les numéros et adresses, rapide pour les confirmations
  • Le post-traitement audio (volume, fondus, silences) affine le rendu final