Spaces:
Sleeping
Sleeping
Update tts_engine.py
Browse files- tts_engine.py +48 -85
tts_engine.py
CHANGED
|
@@ -1,99 +1,62 @@
|
|
| 1 |
-
# tts_engine.py
|
| 2 |
-
|
| 3 |
-
import pyttsx3
|
| 4 |
import os
|
| 5 |
import sys
|
|
|
|
|
|
|
|
|
|
| 6 |
|
| 7 |
-
|
|
|
|
| 8 |
|
| 9 |
def get_tts_engine():
|
| 10 |
-
"""
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
if _engine is not None:
|
| 14 |
-
return _engine
|
| 15 |
-
|
| 16 |
-
try:
|
| 17 |
-
print("-> [TTS Engine] Tentative d'initialisation de pyttsx3...", file=sys.stderr, flush=True)
|
| 18 |
-
|
| 19 |
-
# 1. MODIFICATION : Initialisation la plus simple possible (parfois suffit)
|
| 20 |
-
# On force toujours 'espeak', mais on retire la 'rate' et 'voice' initiales pour l'instant
|
| 21 |
-
# Le but est de laisser pyttsx3.init() réussir, puis appliquer les propriétés.
|
| 22 |
-
_engine = pyttsx3.init('espeak')
|
| 23 |
-
|
| 24 |
-
# 2. SÉLECTION EXPLICITE DE LA VOIX:
|
| 25 |
-
voices = _engine.getProperty('voices')
|
| 26 |
-
|
| 27 |
-
if not voices:
|
| 28 |
-
raise Exception("Aucune voix eSpeak trouvée. Vérifiez votre installation système eSpeak.")
|
| 29 |
-
|
| 30 |
-
# On applique la rate *après* l'initialisation réussie
|
| 31 |
-
_engine.setProperty('rate', 150)
|
| 32 |
-
|
| 33 |
-
# CORRECTION : On définit explicitement la première voix disponible
|
| 34 |
-
_engine.setProperty('voice', voices[0].id)
|
| 35 |
-
|
| 36 |
-
print("-> [TTS Engine] ✅ Initialisation réussie. Voix initiale définie sur:", voices[0].id, file=sys.stderr, flush=True)
|
| 37 |
-
|
| 38 |
-
except Exception as e:
|
| 39 |
-
# L'erreur est maintenant plus propre ou l'échec de init('espeak') si les données eSpeak manquent.
|
| 40 |
-
print(f"!!! FATAL ERROR: pyttsx3 initialization failed. Erreur: {e}", file=sys.stderr, flush=True)
|
| 41 |
-
_engine = None
|
| 42 |
-
|
| 43 |
-
return _engine
|
| 44 |
|
| 45 |
def reset_tts_engine():
|
| 46 |
-
"""
|
| 47 |
-
|
| 48 |
-
if _engine is not None:
|
| 49 |
-
print("-> [TTS Engine] Arrêt de l'ancien moteur.", file=sys.stderr, flush=True)
|
| 50 |
-
_engine.stop()
|
| 51 |
-
_engine = None
|
| 52 |
-
|
| 53 |
-
def get_available_languages():
|
| 54 |
-
"""Récupère la liste des langues/voix disponibles."""
|
| 55 |
-
engine = get_tts_engine()
|
| 56 |
-
if not engine:
|
| 57 |
-
return {}
|
| 58 |
-
|
| 59 |
-
voices = engine.getProperty('voices')
|
| 60 |
-
languages = {}
|
| 61 |
|
| 62 |
-
|
| 63 |
-
for voice in voices:
|
| 64 |
-
# L'ID de la voix eSpeak contient souvent le code de langue (ex: 'french', 'en-us')
|
| 65 |
-
voice_id = voice.id.split('+')[0].replace(' ', '').lower()
|
| 66 |
-
|
| 67 |
-
if 'en' in voice_id or 'english' in voice.name.lower():
|
| 68 |
-
languages['English'] = voice.id
|
| 69 |
-
elif 'fr' in voice_id or 'french' in voice.name.lower():
|
| 70 |
-
languages['Français'] = voice.id
|
| 71 |
-
elif 'es' in voice_id or 'spanish' in voice.name.lower():
|
| 72 |
-
languages['Español'] = voice.id
|
| 73 |
-
|
| 74 |
-
# Ajout d'une entrée par défaut pour chaque voix, si non déjà mappée
|
| 75 |
-
if voice.name not in languages and voice.name != 'default':
|
| 76 |
-
languages[voice.name] = voice.id
|
| 77 |
|
| 78 |
-
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
|
| 82 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 83 |
|
| 84 |
def text_to_audio_file(text, voice_id, output_path="output.wav"):
|
| 85 |
-
"""Convertit le texte en fichier audio
|
| 86 |
-
|
| 87 |
-
|
| 88 |
-
|
| 89 |
-
|
| 90 |
-
|
| 91 |
-
|
|
|
|
|
|
|
|
|
|
| 92 |
|
| 93 |
-
|
| 94 |
-
|
| 95 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 96 |
|
| 97 |
-
# NOTE: eSpeak génère par défaut un fichier .wav.
|
| 98 |
-
return os.path.abspath(output_path)
|
| 99 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
import os
|
| 2 |
import sys
|
| 3 |
+
import subprocess # NOUVEL IMPORT
|
| 4 |
+
|
| 5 |
+
# Remplacer pyttsx3 par une fonction qui utilise directement la commande espeak
|
| 6 |
|
| 7 |
+
# --- Fonctions de compatibilité (non utilisées avec subprocess) ---
|
| 8 |
+
# Elles sont maintenues pour que gunicorn.conf.py ne génère pas d'erreur.
|
| 9 |
|
| 10 |
def get_tts_engine():
|
| 11 |
+
"""Puisque nous utilisons subprocess, il n'y a pas de moteur à initialiser."""
|
| 12 |
+
return True
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 13 |
|
| 14 |
def reset_tts_engine():
|
| 15 |
+
"""Ne fait rien, car il n'y a pas de moteur à réinitialiser."""
|
| 16 |
+
pass
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 17 |
|
| 18 |
+
# --- Logique de Langues/Voix ---
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 19 |
|
| 20 |
+
def get_available_languages():
|
| 21 |
+
"""Retourne une liste des langues courantes eSpeak (sans pyttsx3, nous hardcodons)."""
|
| 22 |
+
# Ce sont les codes de voix utilisés directement par eSpeak
|
| 23 |
+
return {
|
| 24 |
+
'Anglais (US)': 'en-us',
|
| 25 |
+
'Français': 'fr',
|
| 26 |
+
'Espagnol': 'es',
|
| 27 |
+
'Allemand': 'de',
|
| 28 |
+
'Italien': 'it',
|
| 29 |
+
'Default (English)': 'en'
|
| 30 |
+
}
|
| 31 |
+
|
| 32 |
+
# --- Logique de Génération Audio ---
|
| 33 |
|
| 34 |
def text_to_audio_file(text, voice_id, output_path="output.wav"):
|
| 35 |
+
"""Convertit le texte en fichier audio .wav en utilisant la commande 'espeak'."""
|
| 36 |
+
|
| 37 |
+
# Construction de la commande eSpeak:
|
| 38 |
+
# espeak -v [VOIX] -w [OUTPUT_PATH] "[TEXTE]"
|
| 39 |
+
command = [
|
| 40 |
+
"espeak",
|
| 41 |
+
"-v", voice_id, # Sélectionne la voix
|
| 42 |
+
"-w", output_path, # Spécifie le fichier de sortie (.wav par défaut)
|
| 43 |
+
text # Le texte à parler
|
| 44 |
+
]
|
| 45 |
|
| 46 |
+
try:
|
| 47 |
+
# Exécution de la commande
|
| 48 |
+
# Nous utilisons `check=True` pour lever une exception si la commande échoue
|
| 49 |
+
result = subprocess.run(command, check=True, capture_output=True, text=True)
|
| 50 |
+
|
| 51 |
+
# Le fichier audio a été généré avec succès
|
| 52 |
+
return os.path.abspath(output_path)
|
| 53 |
+
|
| 54 |
+
except subprocess.CalledProcessError as e:
|
| 55 |
+
# Erreur si espeak échoue (mauvaise voix, etc.)
|
| 56 |
+
raise Exception(f"La commande eSpeak a échoué. Erreur: {e.stderr}")
|
| 57 |
+
except FileNotFoundError:
|
| 58 |
+
# Erreur si la commande 'espeak' n'est pas trouvée (problème Dockerfile)
|
| 59 |
+
raise Exception("Le programme 'espeak' est introuvable. Vérifiez votre installation dans le Dockerfile.")
|
| 60 |
|
|
|
|
|
|
|
| 61 |
|
| 62 |
+
# Note: Le moteur n'est plus initialisé/stocké, donc les appels post_fork réussiront
|