Alazka2503 commited on
Commit
f32c120
·
verified ·
1 Parent(s): 8c3efb3

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +304 -34
app.py CHANGED
@@ -39,13 +39,26 @@ def get_available_models():
39
  return []
40
  return [f for f in os.listdir(MODEL_DIR) if f.endswith(".pth") or f.endswith(".onnx")]
41
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42
  def load_engine(model_name):
43
  """
44
  Carga un motor TTS si no está en la caché.
45
  Busca dinámicamente el archivo de configuración correspondiente.
46
  """
47
  if model_name not in tts_engines_cache:
48
- print(f"Cargando modelo: {model_name}...")
49
 
50
  # Construir rutas dinámicamente
51
  model_path = os.path.join(MODEL_DIR, model_name)
@@ -59,81 +72,338 @@ def load_engine(model_name):
59
  raise FileNotFoundError(f"No se encontró el archivo de configuración correspondiente: {config_path}")
60
 
61
  # Crear y cachear la nueva instancia del motor con su config específica
62
- print(f"Usando configuración: {config_path}")
63
  tts_engines_cache[model_name] = TTS(
64
  config_path=config_path,
65
  model_path=model_path
66
  )
67
- print(f"Modelo {model_name} cargado y cacheado.")
68
 
69
  return tts_engines_cache[model_name]
70
 
71
- # 2. FUNCIÓN DE INFERENCIA DINÁMICA (sin cambios)
72
- def inference(model_name, prompt):
73
  """
74
  Carga el modelo seleccionado (si es necesario) y genera el audio.
75
  """
76
  if not model_name:
77
- return None, "Error: Por favor, selecciona un modelo."
 
 
 
78
 
79
  try:
 
80
  tts_engine = load_engine(model_name)
 
 
81
  output_path = os.path.join("temp_audio", f"audio_{int(time.time())}.wav")
 
 
82
  tts_engine.text_to_speech(prompt, output_path, noise_scale=0.75, noise_scale_w=0.8, length_scale=1)
83
- return output_path, f"### Audio Generado con {model_name}"
 
 
 
 
 
 
 
 
 
 
84
  except Exception as e:
85
- print(f"Ocurrió un error durante la inferencia: {e}")
86
- return None, f"Error: {e}"
 
 
 
 
 
 
 
87
 
 
 
 
 
 
 
 
 
 
88
 
89
  # --- INTERFAZ DE USUARIO CON GRADIO ---
90
 
91
  fvoice_theme = FVoiceTheme.FVoiceTheme()
92
  css = """
93
- #logo-header { display: flex; align-items: center; justify-content: space-between; padding: 10px 20px; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
94
  a { text-decoration: none; }
 
 
 
 
 
 
 
 
 
95
  """
 
96
  gr.set_static_paths(paths=[Path.cwd().absolute()/"src/assets"])
97
 
98
  available_models = get_available_models()
99
 
100
- with gr.Blocks(title="F-VOICE", theme=fvoice_theme, css=css) as demo:
 
 
101
  gr.HTML("""
102
  <div id="logo-header">
103
  <a href="https://github.com/SIAFI-UNAM/F-VOICE" target="_blank">
104
- <div style="display: flex; align-items: center; gap: 10px;">
105
  <img src='/gradio_api/file=assets/logo.webp' width='100' height='100' />
106
- <h1 id='F_VOICE_header' style='margin: 0; font-size:50px'>F-VOICE</h1>
 
 
 
 
 
107
  </div>
108
  </a>
 
 
 
 
 
109
  </div>
110
  """)
111
- gr.Markdown("""
112
- <div style='font-size:18px; line-height:1.6; color:#FFE3D8; padding: 10px 0;'>
113
- <strong>F-VOICE</strong> es un sistema TTS (Text-to-Speech) que utiliza modelos neuronales avanzados
114
- para sintetizar audio a partir de texto, replicando características vocales aprendidas.<br><br>
115
- Al ingresar un texto nuevo, este será <em>"leído"</em> con la voz que se replicó, dándole las características que aprendió.
 
 
 
 
 
 
 
 
 
116
  </div>
117
  """)
118
-
 
119
  with gr.Row():
120
- with gr.Column():
121
- prompt = gr.TextArea(placeholder="Escribe tu prompt aquí ...", label="Prompt")
122
- with gr.Column():
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
123
  model = gr.Dropdown(
124
  available_models,
125
- label="Modelo",
126
- value=available_models[0] if available_models else None
 
127
  )
128
- btn = gr.Button("Generar")
129
-
130
- markdown_output = gr.Markdown("### Ejemplo de voz")
131
- audio = gr.Audio(value="assets/preview.wav", autoplay=False, label="Voz reproducida", interactive=False)
132
-
133
- btn.click(fn=inference, inputs=[model, prompt], outputs=[audio, markdown_output])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
134
 
 
135
  if __name__ == "__main__":
136
- print("Recordatorio: Asegúrate de que cada modelo en la carpeta './models' tenga un archivo de configuración .json con el mismo nombre en la carpeta './configs'.")
137
- if not available_models:
138
- print("ADVERTENCIA: No se encontraron modelos en la carpeta './models/'. La aplicación se ejecutará pero no podrá generar audio.")
139
- demo.launch()
 
 
 
 
 
 
 
 
 
 
 
 
 
39
  return []
40
  return [f for f in os.listdir(MODEL_DIR) if f.endswith(".pth") or f.endswith(".onnx")]
41
 
42
+ def get_model_info(model_name):
43
+ """Devuelve información básica sobre el modelo seleccionado."""
44
+ if not model_name:
45
+ return "Selecciona un modelo para ver su información"
46
+
47
+ model_path = os.path.join(MODEL_DIR, model_name)
48
+ if os.path.exists(model_path):
49
+ file_size = os.path.getsize(model_path) / (1024 * 1024) # MB
50
+ file_type = "ONNX" if model_name.endswith(".onnx") else "PyTorch"
51
+ status = "✅ Cargado" if model_name in tts_engines_cache else "⏳ Sin cargar"
52
+ return f"**{model_name}** | {file_type} | {file_size:.1f} MB | {status}"
53
+ return "Información no disponible"
54
+
55
  def load_engine(model_name):
56
  """
57
  Carga un motor TTS si no está en la caché.
58
  Busca dinámicamente el archivo de configuración correspondiente.
59
  """
60
  if model_name not in tts_engines_cache:
61
+ print(f"🔄 Cargando modelo: {model_name}...")
62
 
63
  # Construir rutas dinámicamente
64
  model_path = os.path.join(MODEL_DIR, model_name)
 
72
  raise FileNotFoundError(f"No se encontró el archivo de configuración correspondiente: {config_path}")
73
 
74
  # Crear y cachear la nueva instancia del motor con su config específica
75
+ print(f"📁 Usando configuración: {config_path}")
76
  tts_engines_cache[model_name] = TTS(
77
  config_path=config_path,
78
  model_path=model_path
79
  )
80
+ print(f"Modelo {model_name} cargado y cacheado.")
81
 
82
  return tts_engines_cache[model_name]
83
 
84
+ def inference(model_name, prompt, progress=gr.Progress()):
 
85
  """
86
  Carga el modelo seleccionado (si es necesario) y genera el audio.
87
  """
88
  if not model_name:
89
+ return None, "⚠️ **Error:** Por favor, selecciona un modelo.", ""
90
+
91
+ if not prompt.strip():
92
+ return None, "⚠️ **Error:** Por favor, ingresa un texto.", ""
93
 
94
  try:
95
+ progress(0.2, desc="Cargando modelo...")
96
  tts_engine = load_engine(model_name)
97
+
98
+ progress(0.5, desc="Procesando texto...")
99
  output_path = os.path.join("temp_audio", f"audio_{int(time.time())}.wav")
100
+
101
+ progress(0.8, desc="Generando audio...")
102
  tts_engine.text_to_speech(prompt, output_path, noise_scale=0.75, noise_scale_w=0.8, length_scale=1)
103
+
104
+ progress(1.0, desc="¡Completado!")
105
+
106
+ success_msg = f"""
107
+ ### ✅ Audio Generado con {model_name}
108
+ **📝 Texto:** {len(prompt)} caracteres procesados
109
+ **🎵 Listo para reproducir**
110
+ """
111
+
112
+ return output_path, success_msg, get_model_info(model_name)
113
+
114
  except Exception as e:
115
+ error_msg = f"""
116
+ ### Error Durante la Generación
117
+ **Modelo:** {model_name}
118
+ **Error:** {str(e)}
119
+
120
+ Verifica que el modelo y su configuración sean correctos.
121
+ """
122
+ print(f"❌ Ocurrió un error durante la inferencia: {e}")
123
+ return None, error_msg, get_model_info(model_name)
124
 
125
+ def get_example_texts():
126
+ """Devuelve textos de ejemplo para probar."""
127
+ return [
128
+ "Hola, soy F-VOICE, un sistema de síntesis de voz neuronal.",
129
+ "La inteligencia artificial está transformando el mundo de la síntesis de voz.",
130
+ "Buenos días, espero que tengas un excelente día.",
131
+ "Este es un ejemplo de síntesis de voz con tecnología avanzada.",
132
+ "¿Cómo estás? Me alegra poder hablar contigo."
133
+ ]
134
 
135
  # --- INTERFAZ DE USUARIO CON GRADIO ---
136
 
137
  fvoice_theme = FVoiceTheme.FVoiceTheme()
138
  css = """
139
+ #logo-header {
140
+ display: flex;
141
+ align-items: center;
142
+ justify-content: space-between;
143
+ padding: 25px;
144
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
145
+ border-radius: 20px;
146
+ margin-bottom: 30px;
147
+ box-shadow: 0 8px 25px rgba(0,0,0,0.3);
148
+ }
149
+
150
+ #logo-header img {
151
+ border-radius: 50%;
152
+ box-shadow: 0 4px 15px rgba(0,0,0,0.4);
153
+ transition: transform 0.3s ease;
154
+ }
155
+
156
+ #logo-header img:hover {
157
+ transform: scale(1.05);
158
+ }
159
+
160
+ #F_VOICE_header {
161
+ background: linear-gradient(45deg, #FFE3D8, #FFF);
162
+ -webkit-background-clip: text;
163
+ -webkit-text-fill-color: transparent;
164
+ text-shadow: 3px 3px 6px rgba(0,0,0,0.4);
165
+ font-weight: bold;
166
+ }
167
+
168
+ .main-container {
169
+ background: rgba(255, 255, 255, 0.03);
170
+ border-radius: 15px;
171
+ padding: 25px;
172
+ margin: 15px 0;
173
+ border: 1px solid rgba(255, 255, 255, 0.1);
174
+ backdrop-filter: blur(10px);
175
+ }
176
+
177
+ .model-info {
178
+ background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
179
+ color: white;
180
+ padding: 15px;
181
+ border-radius: 12px;
182
+ margin: 10px 0;
183
+ text-align: center;
184
+ font-weight: 500;
185
+ }
186
+
187
+ .examples-row {
188
+ display: flex;
189
+ gap: 10px;
190
+ margin: 15px 0;
191
+ flex-wrap: wrap;
192
+ }
193
+
194
+ .example-btn {
195
+ background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
196
+ border: none;
197
+ color: white;
198
+ padding: 8px 15px;
199
+ border-radius: 20px;
200
+ cursor: pointer;
201
+ font-size: 14px;
202
+ transition: all 0.3s ease;
203
+ }
204
+
205
+ .example-btn:hover {
206
+ transform: translateY(-2px);
207
+ box-shadow: 0 5px 15px rgba(240, 147, 251, 0.4);
208
+ }
209
+
210
+ .generate-btn {
211
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
212
+ border: none;
213
+ color: white;
214
+ padding: 15px 30px;
215
+ border-radius: 25px;
216
+ font-size: 18px;
217
+ font-weight: bold;
218
+ cursor: pointer;
219
+ transition: all 0.3s ease;
220
+ box-shadow: 0 5px 20px rgba(102, 126, 234, 0.4);
221
+ }
222
+
223
+ .generate-btn:hover {
224
+ transform: translateY(-3px);
225
+ box-shadow: 0 8px 25px rgba(102, 126, 234, 0.6);
226
+ }
227
+
228
  a { text-decoration: none; }
229
+
230
+ .footer-info {
231
+ text-align: center;
232
+ margin-top: 30px;
233
+ padding: 20px;
234
+ background: rgba(255, 255, 255, 0.02);
235
+ border-radius: 15px;
236
+ border: 1px solid rgba(255, 255, 255, 0.05);
237
+ }
238
  """
239
+
240
  gr.set_static_paths(paths=[Path.cwd().absolute()/"src/assets"])
241
 
242
  available_models = get_available_models()
243
 
244
+ with gr.Blocks(title="F-VOICE - Síntesis de Voz Neuronal", theme=fvoice_theme, css=css) as demo:
245
+
246
+ # Header con logo y título
247
  gr.HTML("""
248
  <div id="logo-header">
249
  <a href="https://github.com/SIAFI-UNAM/F-VOICE" target="_blank">
250
+ <div style="display: flex; align-items: center; gap: 20px;">
251
  <img src='/gradio_api/file=assets/logo.webp' width='100' height='100' />
252
+ <div>
253
+ <h1 id='F_VOICE_header' style='margin: 0; font-size: 55px;'>F-VOICE</h1>
254
+ <p style='margin: 5px 0 0 0; color: #FFE3D8; font-size: 20px; font-weight: 300;'>
255
+ Sistema de Síntesis de Voz Neuronal ✨
256
+ </p>
257
+ </div>
258
  </div>
259
  </a>
260
+ <div style="text-align: right; color: #FFE3D8; font-size: 16px;">
261
+ <p style="margin: 5px 0;">🎤 <strong>""" + str(len(available_models)) + """</strong> modelos disponibles</p>
262
+ <p style="margin: 5px 0;">🤖 Powered by <strong>AI</strong></p>
263
+ <p style="margin: 5px 0;">⚡ Síntesis en <strong>tiempo real</strong></p>
264
+ </div>
265
  </div>
266
  """)
267
+
268
+ # Descripción principal
269
+ gr.HTML("""
270
+ <div class="main-container">
271
+ <div style="text-align: center; padding: 20px;">
272
+ <h2 style="color: #FFE3D8; margin-bottom: 15px;">
273
+ 🚀 Convierte texto en voz natural con IA
274
+ </h2>
275
+ <p style="font-size: 18px; line-height: 1.6; color: #E0E0E0; max-width: 800px; margin: 0 auto;">
276
+ <strong>F-VOICE</strong> utiliza modelos neuronales de última generación para generar
277
+ síntesis de voz realista y expresiva. Simplemente selecciona un modelo,
278
+ escribe tu texto y obtén audio de alta calidad al instante.
279
+ </p>
280
+ </div>
281
  </div>
282
  """)
283
+
284
+ # Interfaz principal simplificada
285
  with gr.Row():
286
+ with gr.Column(scale=3):
287
+ prompt = gr.TextArea(
288
+ placeholder="✍️ Escribe aquí el texto que quieres convertir a voz...\n\nEjemplo: Hola mundo, este es F-VOICE generando mi voz.",
289
+ label="📝 Tu Texto",
290
+ lines=5,
291
+ max_lines=10
292
+ )
293
+
294
+ # Ejemplos rápidos con HTML personalizado
295
+ gr.HTML("""
296
+ <div style="margin: 20px 0;">
297
+ <p style="margin-bottom: 10px; font-weight: 500; color: #FFE3D8;">💡 Prueba estos ejemplos:</p>
298
+ </div>
299
+ """)
300
+
301
+ with gr.Row():
302
+ example_btns = []
303
+ examples = get_example_texts()
304
+ for i, example in enumerate(examples[:3]):
305
+ btn = gr.Button(f"Ejemplo {i+1}", size="sm", variant="secondary")
306
+ btn.click(lambda x=example: x, outputs=prompt)
307
+
308
+ with gr.Column(scale=1):
309
  model = gr.Dropdown(
310
  available_models,
311
+ label="🎤 Modelo de Voz",
312
+ value=available_models[0] if available_models else None,
313
+ info="Selecciona la voz que prefieras"
314
  )
315
+
316
+ # Información del modelo con estilo
317
+ model_info = gr.HTML(
318
+ f'<div class="model-info">{get_model_info(available_models[0] if available_models else "")}</div>'
319
+ )
320
+
321
+ # Botón de generación grande y llamativo
322
+ with gr.Row():
323
+ with gr.Column():
324
+ btn = gr.Button("🎯 Generar Audio", variant="primary", size="lg", elem_classes=["generate-btn"])
325
+
326
+ # Resultado
327
+ with gr.Row():
328
+ with gr.Column():
329
+ markdown_output = gr.Markdown("""
330
+ ### 🎵 Tu audio aparecerá aquí
331
+
332
+ Selecciona un modelo, escribe tu texto y presiona **"Generar Audio"** para comenzar.
333
+
334
+ 💡 **Consejo:** Los textos más largos y con buena puntuación dan mejores resultados.
335
+ """)
336
+
337
+ audio = gr.Audio(
338
+ value="assets/preview.wav" if os.path.exists("assets/preview.wav") else None,
339
+ autoplay=False,
340
+ label="🔊 Audio Generado",
341
+ interactive=False,
342
+ show_download_button=True
343
+ )
344
+
345
+ # Footer informativo
346
+ gr.HTML("""
347
+ <div class="footer-info">
348
+ <div style="display: flex; justify-content: center; gap: 30px; align-items: center; flex-wrap: wrap;">
349
+ <div style="text-align: center;">
350
+ <p style="margin: 5px 0; font-size: 14px; color: #B0B0B0;">
351
+ 🎨 <strong>Fácil de usar</strong><br>
352
+ Interfaz intuitiva
353
+ </p>
354
+ </div>
355
+ <div style="text-align: center;">
356
+ <p style="margin: 5px 0; font-size: 14px; color: #B0B0B0;">
357
+ ⚡ <strong>Rápido</strong><br>
358
+ Generación instantánea
359
+ </p>
360
+ </div>
361
+ <div style="text-align: center;">
362
+ <p style="margin: 5px 0; font-size: 14px; color: #B0B0B0;">
363
+ 🎯 <strong>Preciso</strong><br>
364
+ Calidad profesional
365
+ </p>
366
+ </div>
367
+ <div style="text-align: center;">
368
+ <p style="margin: 5px 0; font-size: 14px; color: #B0B0B0;">
369
+ 🔧 <strong>Versátil</strong><br>
370
+ Múltiples modelos
371
+ </p>
372
+ </div>
373
+ </div>
374
+ <p style="margin-top: 15px; font-size: 12px; color: #888; text-align: center;">
375
+ Desarrollado con ❤️ usando tecnología de vanguardia en IA
376
+ </p>
377
+ </div>
378
+ """)
379
+
380
+ # Configurar eventos
381
+ def update_model_info(model_name):
382
+ return f'<div class="model-info">{get_model_info(model_name)}</div>'
383
+
384
+ model.change(fn=update_model_info, inputs=[model], outputs=[model_info])
385
+
386
+ btn.click(
387
+ fn=inference,
388
+ inputs=[model, prompt],
389
+ outputs=[audio, markdown_output, model_info]
390
+ )
391
 
392
+ # Mensaje de inicio
393
  if __name__ == "__main__":
394
+ print("=" * 60)
395
+ print("🎤 F-VOICE - Sistema de Síntesis de Voz Neuronal")
396
+ print("=" * 60)
397
+ print(f"📁 Modelos encontrados: {len(available_models)}")
398
+
399
+ if available_models:
400
+ print("✅ Modelos disponibles:")
401
+ for i, model in enumerate(available_models, 1):
402
+ print(f" {i}. {model}")
403
+ else:
404
+ print("⚠️ ADVERTENCIA: No se encontraron modelos en './models/'")
405
+
406
+ print("\n🚀 Iniciando aplicación...")
407
+ print("=" * 60)
408
+
409
+ demo.launch()