binaryMao commited on
Commit
5cea967
·
verified ·
1 Parent(s): 65d9e15

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +160 -68
app.py CHANGED
@@ -1,20 +1,23 @@
1
  # -*- coding: utf-8 -*-
2
- """RobotsMali_ASR_Demo.ipynb
3
- Automatically generated by Colab.
4
- Original file is located at
5
- https://colab.research.google.com/drive/1fCpSvqwoSbpEBC62cZrQuqQGr4U1BNsh
6
  """
7
  import gradio as gr
8
- from transformers import pipeline
9
  import time
10
  import os
 
 
 
 
 
 
 
11
 
12
  # ----------------------------------------------------------------------
13
- # 1. CONFIGURATION DES MODÈLES
14
  # ----------------------------------------------------------------------
15
- # Liste des identifiants exacts des modèles RobotsMali
16
  ROBOTSMALI_MODELS = [
17
- "RobotsMali/soloba-ctc-0.6b-v0", # Le modèle qui posait problème
18
  "RobotsMali/soloni-114m-tdt-ctc-v1",
19
  "RobotsMali/soloni-114m-tdt-ctc-V0",
20
  "RobotsMali/stt-bm-quartznet5x5-V0",
@@ -22,109 +25,200 @@ ROBOTSMALI_MODELS = [
22
  "RobotsMali/soloba-ctc-0.6b-v1"
23
  ]
24
 
25
- # Cache pour stocker les pipelines ASR déjà chargés.
 
 
 
26
  asr_pipelines = {}
27
 
 
 
 
28
  def load_pipeline(model_name):
29
  """
30
- Charge le pipeline ASR (modèle + processeur) pour un modèle donné et le met en cache.
31
  """
32
  if model_name not in asr_pipelines:
33
- print(f"-> Tentative de chargement du modèle: {model_name}...")
 
 
34
  try:
35
- # Cette ligne charge le modèle directement depuis le Hub Hugging Face
36
- asr_pipelines[model_name] = pipeline(
37
- "automatic-speech-recognition",
38
- model=model_name,
39
- # device=0 # Décommenter si un GPU est disponible et souhaité
40
- )
41
- print(f"-> Modèle {model_name} chargé avec succès.")
 
 
 
 
 
 
 
 
 
 
 
 
 
42
  except Exception as e:
43
- # Meilleure gestion d'erreur : affiche la trace complète dans les logs du Space
44
- print(f"!!! Erreur de chargement pour {model_name}: {e}")
45
- # Si vous avez besoin de la trace complète dans les logs du Space
46
- # import traceback; traceback.print_exc()
47
- raise RuntimeError(f"Impossible de charger le modèle {model_name}. Vérifiez sa configuration.")
48
- return asr_pipelines[model_name]
 
 
 
 
49
 
50
  # ----------------------------------------------------------------------
51
- # 2. FONCTION PRINCIPALE D'INFÉRENCE
52
  # ----------------------------------------------------------------------
53
- def transcribe_audio(model_name: str, audio_path: str) -> str:
54
  """
55
- Effectue la transcription ASR sur un fichier audio en utilisant le modèle sélectionné.
56
  """
57
  if audio_path is None:
58
- return "⚠️ Veuillez d'abord télécharger ou enregistrer un fichier audio."
 
59
  if not ROBOTSMALI_MODELS:
60
- return "Liste de modèles ASR indisponible."
 
61
 
62
  start_time = time.time()
 
 
 
63
  try:
64
- if model_name not in ROBOTSMALI_MODELS:
65
- return f"Modèle sélectionné ({model_name}) non reconnu."
 
 
 
 
66
 
67
- # Le modèle est chargé ou récupéré depuis le cache ici
68
- asr_pipe = load_pipeline(model_name)
69
 
70
- # S'assure que le fichier audio existe avant de l'envoyer au pipeline
71
- if not os.path.exists(audio_path):
72
- return "❌ Erreur : Fichier audio introuvable ou chemin invalide."
73
-
74
- result = asr_pipe(audio_path)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
75
  end_time = time.time()
76
  duration = end_time - start_time
77
 
78
- transcription_text = result.get("text", "Transcription non disponible.")
79
- model_short_name = model_name.split('/')[-1]
80
 
81
- output = f"**Modèle Utilisé :** `{model_short_name}`\n\n"
 
 
 
 
82
  output += f"***\n"
83
- output += f"**Temps d'inférence (hors chargement) :** {duration:.2f} secondes\n"
84
- output += f"**RÉSULTAT DE LA TRANSCRIPTION :**\n"
85
- output += f"**{transcription_text.strip()}**"
86
 
87
- return output
 
 
 
 
 
 
 
 
 
88
 
89
  except RuntimeError as e:
90
- # Ceci capture l'erreur de chargement (si elle n'a pas été capturée au pré-chargement)
91
- return f"❌ Erreur critique : {str(e)}"
92
  except Exception as e:
93
- return f"❌ Erreur lors de la transcription avec {model_name}: {e}"
 
 
 
 
 
 
 
 
 
 
94
 
95
  # ----------------------------------------------------------------------
96
- # 2.5. PRÉ-CHARGEMENT DU MODÈLE PAR DÉFAUT (NOUVEAU)
97
  # ----------------------------------------------------------------------
98
 
99
- INITIAL_DESCRIPTION = "Sélectionnez un modèle ASR de RobotsMali, puis enregistrez ou téléchargez un fichier audio (MP3 recommandé) pour obtenir la transcription."
100
 
101
  if ROBOTSMALI_MODELS:
102
  default_model = ROBOTSMALI_MODELS[0]
103
  try:
104
- # Tente de charger le modèle par défaut au démarrage du script
105
- load_pipeline(default_model)
106
  default_model_short_name = default_model.split('/')[-1]
107
  INITIAL_DESCRIPTION = (
108
- f"✅ Le modèle par défaut `{default_model_short_name}` a été préchargé avec succès. "
109
  f"Téléchargez ou enregistrez votre audio pour transcrire."
110
  )
111
  except RuntimeError as e:
112
- # Si le chargement échoue, informe l'utilisateur que le modèle par défaut est cassé
113
  default_model_short_name = default_model.split('/')[-1]
114
  INITIAL_DESCRIPTION = (
115
- f"❌ ERREUR CRITIQUE AU DÉMARRAGE : Impossible de charger le modèle "
116
- f"`{default_model_short_name}`. "
117
- f"**Veuillez sélectionner un autre modèle dans la liste** (ex: le second). "
118
  f"Détails de l'erreur : {str(e)}"
119
  )
120
- except Exception:
121
- # Capture toute autre erreur non gérée par RuntimeError
122
- INITIAL_DESCRIPTION = "❌ ERREUR CRITIQUE AU DÉMARRAGE : Problème inconnu lors du pré-chargement du modèle."
123
-
124
-
125
- # ----------------------------------------------------------------------
126
- # 3. INTERFACE GRADIO ET LANCEMENT
127
- # ----------------------------------------------------------------------
128
 
129
  model_dropdown = gr.Dropdown(
130
  label="1. Sélectionner un Modèle RobotsMali",
@@ -146,11 +240,9 @@ interface = gr.Interface(
146
  fn=transcribe_audio,
147
  inputs=[model_dropdown, audio_input],
148
  outputs=text_output,
149
- title="🤖 RobotsMali ASR Multi-Modèles (Test Colab)",
150
- # Utilise la description générée par la phase de pré-chargement
151
  description=INITIAL_DESCRIPTION,
152
  allow_flagging="never")
153
 
154
- # Lancement de l'Interface Gradio sur Colab
155
  print("Lancement de l'interface Gradio...")
156
  interface.launch(share=True)
 
1
  # -*- coding: utf-8 -*-
2
+ """RobotsMali_ASR_Demo.ipynb - Script Final pour Démo Fluide et Stable
3
+ Version optimisée pour la RAM, la vitesse et l'affichage 'Lyrics'.
 
 
4
  """
5
  import gradio as gr
 
6
  import time
7
  import os
8
+ import librosa
9
+ import soundfile as sf
10
+ import numpy as np
11
+
12
+ # --- IMPORTS NEMO ---
13
+ import nemo.collections.asr as nemo_asr
14
+ # --------------------
15
 
16
  # ----------------------------------------------------------------------
17
+ # CONSTANTES DE CONFIGURATION
18
  # ----------------------------------------------------------------------
 
19
  ROBOTSMALI_MODELS = [
20
+ "RobotsMali/soloba-ctc-0.6b-v0",
21
  "RobotsMali/soloni-114m-tdt-ctc-v1",
22
  "RobotsMali/soloni-114m-tdt-ctc-V0",
23
  "RobotsMali/stt-bm-quartznet5x5-V0",
 
25
  "RobotsMali/soloba-ctc-0.6b-v1"
26
  ]
27
 
28
+ CHUNK_DURATION_SEC = 25 # Durée par segment (secondes) pour économiser la RAM
29
+ SR_TARGET = 16000 # Taux d'échantillonnage cible pour NeMo ASR (16kHz)
30
+
31
+ # Cache pour stocker les modèles NeMo chargés.
32
  asr_pipelines = {}
33
 
34
+ # ----------------------------------------------------------------------
35
+ # 1. FONCTIONS DE GESTION DES MODÈLES (CHARGEMENT + WARM-UP)
36
+ # ----------------------------------------------------------------------
37
  def load_pipeline(model_name):
38
  """
39
+ Charge le modèle NeMo, le met en cache et effectue un warm-up.
40
  """
41
  if model_name not in asr_pipelines:
42
+ print(f"-> Tentative de chargement du modèle NeMo: {model_name}...")
43
+ temp_warmup_file = "dummy_warmup.wav"
44
+
45
  try:
46
+ # 🚀 CHARGEMENT NEMO
47
+ model_instance = nemo_asr.models.ASRModel.from_pretrained(model_name=model_name)
48
+ model_instance.eval()
49
+
50
+ asr_pipelines[model_name] = model_instance
51
+ print(f"-> Modèle NeMo {model_name} chargé avec succès.")
52
+
53
+ # ----------------------------------------------------
54
+ # WARM-UP (Inférence à blanc)
55
+ # ----------------------------------------------------
56
+ print(f" [Warmup] Exécution d'une inférence à blanc...")
57
+
58
+ dummy_audio = np.random.randn(SR_TARGET).astype(np.float32) # 1s d'audio
59
+ sf.write(temp_warmup_file, dummy_audio, SR_TARGET)
60
+
61
+ model_instance.transcribe([temp_warmup_file], batch_size=1)
62
+
63
+ print(f" [Warmup] Terminé.")
64
+ # ----------------------------------------------------
65
+
66
  except Exception as e:
67
+ if model_name in asr_pipelines:
68
+ del asr_pipelines[model_name]
69
+ print(f"!!! Erreur de chargement NeMo pour {model_name}: {e}")
70
+ raise RuntimeError(f"Impossible de charger le modèle {model_name}. Détail: {e}")
71
+
72
+ finally:
73
+ if os.path.exists(temp_warmup_file):
74
+ os.remove(temp_warmup_file)
75
+
76
+ return asr_pipelines.get(model_name)
77
 
78
  # ----------------------------------------------------------------------
79
+ # 2. FONCTION PRINCIPALE D'INFÉRENCE AVEC STREAMING ET DÉCOUPAGE
80
  # ----------------------------------------------------------------------
81
+ def transcribe_audio(model_name: str, audio_path: str):
82
  """
83
+ Effectue la transcription ASR avec découpage (chunking) et streaming d'état.
84
  """
85
  if audio_path is None:
86
+ yield "⚠️ Veuillez d'abord télécharger ou enregistrer un fichier audio."
87
+ return
88
  if not ROBOTSMALI_MODELS:
89
+ yield "Liste de modèles ASR indisponible."
90
+ return
91
 
92
  start_time = time.time()
93
+ model_short_name = model_name.split('/')[-1]
94
+ temp_chunk_paths = [] # Pour le nettoyage final
95
+
96
  try:
97
+ # ----------------------------------------------------------------
98
+ # ÉTAPE 1 : PRÉPARATION ET CHARGEMENT AUDIO
99
+ # ----------------------------------------------------------------
100
+ yield f"**[1/4] CHARGEMENT AUDIO...** Préparation du fichier original (Mono @ 16kHz). ⚙️"
101
+
102
+ full_audio_data, sr = librosa.load(audio_path, sr=SR_TARGET, mono=True)
103
 
104
+ total_duration = len(full_audio_data) / SR_TARGET
105
+ samples_per_chunk = int(CHUNK_DURATION_SEC * SR_TARGET)
106
 
107
+ # ----------------------------------------------------------------
108
+ # ÉTAPE 2 : CHARGEMENT/VÉRIFICATION DU MODÈLE ET DÉCOUPAGE
109
+ # ----------------------------------------------------------------
110
+ yield f"**[2/4] PRÉ-CALCUL...** Chargement du modèle et découpage ({total_duration:.1f}s en segments de {CHUNK_DURATION_SEC}s). 🧠"
111
+
112
+ asr_model = load_pipeline(model_name)
113
+
114
+ # Logique de DÉCOUPAGE
115
+ audio_segments = []
116
+ for i in range(0, len(full_audio_data), samples_per_chunk):
117
+ audio_segments.append(full_audio_data[i:i + samples_per_chunk])
118
+
119
+ num_chunks = len(audio_segments)
120
+ full_transcription_text = ""
121
+
122
+ # ----------------------------------------------------------------
123
+ # ÉTAPE 3 : TRANSCRIPTION PAR SEGMENT
124
+ # ----------------------------------------------------------------
125
+
126
+ for idx, segment_data in enumerate(audio_segments):
127
+
128
+ # Message d'état clé pour l'utilisateur
129
+ yield f"**[3/4] TRANSCRIPTION EN COURS...** Analyse du segment {idx + 1}/{num_chunks}. ⏳"
130
+
131
+ # Écriture du chunk temporaire
132
+ chunk_path = f"{os.path.splitext(os.path.basename(audio_path))[0]}_chunk_{idx}.wav"
133
+ sf.write(chunk_path, segment_data, SR_TARGET)
134
+ temp_chunk_paths.append(chunk_path)
135
+
136
+ # 🚀 INFÉRENCE NEMO
137
+ transcriptions = asr_model.transcribe([chunk_path], batch_size=1)
138
+
139
+ # --- GESTION DE L'OBJET HYPOTHESIS (CORRIGÉE) ---
140
+ segment_text = ""
141
+ if transcriptions and transcriptions[0]:
142
+ hyp_object = transcriptions[0]
143
+
144
+ # Accède à l'attribut .text de l'objet Hypothesis
145
+ if hasattr(hyp_object, 'text'):
146
+ segment_text = hyp_object.text.strip()
147
+ elif isinstance(hyp_object, str):
148
+ segment_text = hyp_object.strip()
149
+ # Gère le cas où transcribe retourne une liste de listes
150
+ elif isinstance(hyp_object, list) and hasattr(hyp_object[0], 'text'):
151
+ segment_text = hyp_object[0].text.strip()
152
+
153
+ if not segment_text:
154
+ segment_text = "[Transcription vide]"
155
+
156
+ # Ajout d'un double saut de ligne pour le format "Lyrics" (paragraphe par segment)
157
+ full_transcription_text += segment_text + "\n\n"
158
+
159
+ # ----------------------------------------------------
160
+ # ÉTAPE 4 : RÉSULTAT FINAL
161
+ # ----------------------------------------------------
162
  end_time = time.time()
163
  duration = end_time - start_time
164
 
165
+ transcription_text_final = full_transcription_text.strip()
 
166
 
167
+ # 1. EN-TÊTE D'INFORMATION
168
+ output = f"**Modèle Utilisé :** `{model_short_name}` (NeMo)\n"
169
+ output += f"**Durée de l'Audio :** {total_duration:.1f} secondes\n"
170
+ output += f"**Temps de Traitement Total :** {duration:.2f} secondes\n"
171
+ output += f"**DÉCOUPAGE :** {CHUNK_DURATION_SEC} secondes ({num_chunks} segments)\n"
172
  output += f"***\n"
 
 
 
173
 
174
+ # 2. PRÉSENTATION LYRICS PROPRE
175
+ output += "**RÉSULTAT DE LA TRANSCRIPTION (Lyrics) :**\n"
176
+ # Utilisation du bloc de citation Markdown pour la structure
177
+ output += ">>> " + transcription_text_final.replace('\n\n', '\n>>> ')
178
+
179
+ # 3. NOTE FINALE
180
+ output += "\n\n*Note : Audio converti en **Mono @ 16kHz** pour la transcription.*"
181
+
182
+ # Le dernier 'yield' envoie le résultat final
183
+ yield output
184
 
185
  except RuntimeError as e:
186
+ yield f"❌ Erreur critique lors du chargement : {str(e)}"
 
187
  except Exception as e:
188
+ # Affiche le texte partiel en cas d'erreur
189
+ if 'full_transcription_text' in locals() and full_transcription_text:
190
+ yield f"❌ Erreur lors de la transcription, le traitement s'est arrêté. Texte partiel:\n>>> {full_transcription_text.strip().replace('\n\n', '\n>>> ')}"
191
+ yield f"❌ Erreur générale : {e}"
192
+ finally:
193
+ # Nettoyage
194
+ for chunk_path in temp_chunk_paths:
195
+ if os.path.exists(chunk_path):
196
+ os.remove(chunk_path)
197
+ print(f"-> {len(temp_chunk_paths)} fichiers temporaires de segments supprimés.")
198
+
199
 
200
  # ----------------------------------------------------------------------
201
+ # 4. PRÉ-CHARGEMENT ET INTERFACE GRADIO
202
  # ----------------------------------------------------------------------
203
 
204
+ INITIAL_DESCRIPTION = "Sélectionnez un modèle ASR de RobotsMali, puis enregistrez ou téléchargez un fichier audio pour obtenir la transcription."
205
 
206
  if ROBOTSMALI_MODELS:
207
  default_model = ROBOTSMALI_MODELS[0]
208
  try:
209
+ load_pipeline(default_model)
 
210
  default_model_short_name = default_model.split('/')[-1]
211
  INITIAL_DESCRIPTION = (
212
+ f"✅ Le modèle par défaut `{default_model_short_name}` (NeMo) a été **préchargé et réchauffé** avec succès. "
213
  f"Téléchargez ou enregistrez votre audio pour transcrire."
214
  )
215
  except RuntimeError as e:
 
216
  default_model_short_name = default_model.split('/')[-1]
217
  INITIAL_DESCRIPTION = (
218
+ f"❌ ERREUR CRITIQUE AU DÉMARRAGE : Impossible de charger le modèle `{default_model_short_name}`. "
219
+ f"**Veuillez sélectionner un autre modèle dans la liste**. "
 
220
  f"Détails de l'erreur : {str(e)}"
221
  )
 
 
 
 
 
 
 
 
222
 
223
  model_dropdown = gr.Dropdown(
224
  label="1. Sélectionner un Modèle RobotsMali",
 
240
  fn=transcribe_audio,
241
  inputs=[model_dropdown, audio_input],
242
  outputs=text_output,
243
+ title="🤖 RobotsMali ASR Multi-Modèles (Démo NeMo Fluide)",
 
244
  description=INITIAL_DESCRIPTION,
245
  allow_flagging="never")
246
 
 
247
  print("Lancement de l'interface Gradio...")
248
  interface.launch(share=True)