jeysshon commited on
Commit
8e6557d
·
verified ·
1 Parent(s): 89d3e8f

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +268 -466
app.py CHANGED
@@ -1,470 +1,269 @@
1
  import os
2
- import gc
3
- import json
4
  import sys
5
- import subprocess
6
- import librosa
7
- import numpy as np
8
- import soundfile as sf
9
- import warnings
10
- import gradio as gr
11
  import logging
12
- import time
13
  import traceback
14
  import tempfile
 
15
  from pathlib import Path
16
 
 
 
 
17
  # Configuración
18
- warnings.filterwarnings("ignore")
19
  logging.basicConfig(level=logging.INFO)
20
  logger = logging.getLogger(__name__)
21
 
22
- title = "<center><strong><font size='7'>🎵 Multi-Instrument Separator Pro</font></strong></center>"
23
  description = """
24
- ### 🚀 Separador profesional de múltiples instrumentos usando IA
25
- **Separa tu música en hasta 8+ instrumentos diferentes con calidad profesional**
26
- - 🎤 **Voces** (principales y coros)
27
- - 🥁 **Batería** (completa)
28
- - 🎸 **Bajo** (frecuencias graves)
29
- - 🎹 **Piano** (teclas)
30
- - 🎻 **Cuerdas** (violín, viola, etc.)
31
- - 🎺 **Vientos** (trompeta, saxo, etc.)
32
- - 🎸 **Guitarra** (acústica y eléctrica)
33
- - 🎛️ **Sintetizadores** y otros
34
-
35
- **¡Sin dependencias complejas! Funciona siempre.**
36
  """
37
 
38
- # URLs de modelos especializados por instrumento (NO SE USAN - SOLO REFERENCIA)
39
- # Ya no necesitamos descargar modelos ONNX complejos
40
- INSTRUMENT_DESCRIPTIONS = {
41
- "vocals": "Voces principales y coros usando filtros especializados",
42
- "drums": "Elementos percusivos: kick, snare, hi-hats, toms",
43
- "bass": "Frecuencias graves y líneas de bajo (20-250Hz)",
44
- "piano": "Teclas y acordes con componentes híbridos",
45
- "guitar": "Guitarra acústica y eléctrica con filtros armónicos",
46
- "strings": "Instrumentos de cuerda: violín, viola, cello",
47
- "winds": "Instrumentos de viento: trompeta, saxo, flauta",
48
- "other": "Sintetizadores y otros instrumentos no clasificados"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
49
  }
50
 
51
- # Directorios
52
- BASE_DIR = "."
53
- output_dir = os.path.join(BASE_DIR, "separated")
54
-
55
- def create_directories():
56
- """Crear directorios necesarios"""
57
- os.makedirs(output_dir, exist_ok=True)
58
-
59
- class SimpleAudioSeparator:
60
- """Separador de audio simplificado usando librosa y procesamiento digital"""
61
 
62
  def __init__(self):
63
- self.sr = 44100
 
 
64
 
65
- def separate_vocals(self, audio):
66
- """Separar voces usando técnicas de procesamiento digital"""
67
- try:
68
- # Usar separación armónica-percusiva
69
- harmonic, percussive = librosa.effects.hpss(audio, margin=3.0)
70
-
71
- # Las voces están principalmente en componentes armónicos
72
- # Aplicar filtro de frecuencias vocales (80Hz - 8kHz)
73
- vocal_filtered = self._apply_vocal_filter(harmonic)
74
- instrumental = audio - vocal_filtered
75
-
76
- return vocal_filtered, instrumental
77
- except Exception as e:
78
- logger.error(f"Error en separación de voces: {e}")
79
- return audio * 0.1, audio * 0.9 # Fallback
80
-
81
- def separate_drums(self, audio):
82
- """Separar batería usando componentes percusivos"""
83
- try:
84
- # Separación H/P con parámetros optimizados para drums
85
- harmonic, percussive = librosa.effects.hpss(audio, margin=(1.0, 5.0))
86
-
87
- # Filtrar frecuencias de drums (60Hz - 15kHz)
88
- drums = self._apply_drums_filter(percussive)
89
- no_drums = audio - drums
90
-
91
- return drums, no_drums
92
- except Exception as e:
93
- logger.error(f"Error en separación de batería: {e}")
94
- return audio * 0.2, audio * 0.8
95
-
96
- def separate_bass(self, audio):
97
- """Separar bajo usando filtros de frecuencia"""
98
- try:
99
- # Filtro pasa-bajos agresivo para frecuencias graves
100
- bass = self._apply_bass_filter(audio)
101
- no_bass = audio - bass
102
-
103
- return bass, no_bass
104
- except Exception as e:
105
- logger.error(f"Error en separación de bajo: {e}")
106
- return audio * 0.15, audio * 0.85
107
-
108
- def separate_piano(self, audio):
109
- """Separar piano usando características espectrales"""
110
- try:
111
- # Piano tiene características tanto armónicas como percusivas
112
- harmonic, percussive = librosa.effects.hpss(audio, margin=(2.0, 3.0))
113
-
114
- # Combinar componentes con pesos apropiados para piano
115
- piano = harmonic * 0.7 + percussive * 0.3
116
-
117
- # Filtrar rango de frecuencias del piano
118
- piano = self._apply_piano_filter(piano)
119
- no_piano = audio - piano
120
-
121
- return piano, no_piano
122
- except Exception as e:
123
- logger.error(f"Error en separación de piano: {e}")
124
- return audio * 0.2, audio * 0.8
125
-
126
- def separate_guitar(self, audio):
127
- """Separar guitarra usando componentes armónicos"""
128
  try:
129
- harmonic, _ = librosa.effects.hpss(audio, margin=(3.0, 1.0))
130
-
131
- # Filtrar rango de frecuencias de guitarra
132
- guitar = self._apply_guitar_filter(harmonic)
133
- no_guitar = audio - guitar
134
-
135
- return guitar, no_guitar
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
136
  except Exception as e:
137
- logger.error(f"Error en separación de guitarra: {e}")
138
- return audio * 0.2, audio * 0.8
 
139
 
140
- def separate_strings(self, audio):
141
- """Separar instrumentos de cuerda"""
142
  try:
143
- # Cuerdas son muy armónicas
144
- harmonic, _ = librosa.effects.hpss(audio, margin=(5.0, 1.0))
145
-
146
- # Filtrar frecuencias de cuerdas (200Hz - 12kHz)
147
- strings = self._apply_strings_filter(harmonic)
148
- no_strings = audio - strings
 
 
 
 
 
 
 
 
 
 
149
 
150
- return strings, no_strings
151
- except Exception as e:
152
- logger.error(f"Error en separación de cuerdas: {e}")
153
- return audio * 0.15, audio * 0.85
154
-
155
- def separate_winds(self, audio):
156
- """Separar instrumentos de viento"""
157
- try:
158
- harmonic, _ = librosa.effects.hpss(audio, margin=(8.0, 1.0))
159
 
160
- # Filtrar frecuencias de vientos (400Hz - 10kHz)
161
- winds = self._apply_winds_filter(harmonic)
162
- no_winds = audio - winds
163
 
164
- return winds, no_winds
165
  except Exception as e:
166
- logger.error(f"Error en separación de vientos: {e}")
167
- return audio * 0.1, audio * 0.9
168
-
169
- def _apply_vocal_filter(self, audio):
170
- """Filtro optimizado para voces (200Hz - 4kHz)"""
171
- stft = librosa.stft(audio, n_fft=2048)
172
- magnitude, phase = np.abs(stft), np.angle(stft)
173
-
174
- # Frecuencias vocales principales
175
- freq_bins = magnitude.shape[0]
176
- vocal_start = int(200 * freq_bins / (self.sr / 2))
177
- vocal_end = int(4000 * freq_bins / (self.sr / 2))
178
-
179
- # Crear máscara
180
- mask = np.zeros_like(magnitude)
181
- mask[vocal_start:vocal_end] = 1.0
182
-
183
- # Aplicar m��scara suave
184
- filtered_magnitude = magnitude * mask
185
- filtered_stft = filtered_magnitude * np.exp(1j * phase)
186
-
187
- return librosa.istft(filtered_stft)
188
-
189
- def _apply_drums_filter(self, audio):
190
- """Filtro para batería (60Hz - 15kHz)"""
191
- stft = librosa.stft(audio, n_fft=2048)
192
- magnitude, phase = np.abs(stft), np.angle(stft)
193
-
194
- freq_bins = magnitude.shape[0]
195
- drums_start = int(60 * freq_bins / (self.sr / 2))
196
- drums_end = int(15000 * freq_bins / (self.sr / 2))
197
-
198
- mask = np.zeros_like(magnitude)
199
- mask[drums_start:drums_end] = 1.0
200
-
201
- # Enfatizar frecuencias típicas de drums
202
- kick_range = int(100 * freq_bins / (self.sr / 2))
203
- snare_start = int(200 * freq_bins / (self.sr / 2))
204
- snare_end = int(400 * freq_bins / (self.sr / 2))
205
-
206
- mask[:kick_range] *= 1.3 # Enfatizar kick
207
- mask[snare_start:snare_end] *= 1.2 # Enfatizar snare
208
-
209
- filtered_magnitude = magnitude * mask
210
- filtered_stft = filtered_magnitude * np.exp(1j * phase)
211
-
212
- return librosa.istft(filtered_stft)
213
-
214
- def _apply_bass_filter(self, audio):
215
- """Filtro pasa-bajos para bajo (20Hz - 250Hz)"""
216
- stft = librosa.stft(audio, n_fft=4096) # FFT más grande para mejor resolución en graves
217
- magnitude, phase = np.abs(stft), np.angle(stft)
218
-
219
- freq_bins = magnitude.shape[0]
220
- bass_cutoff = int(250 * freq_bins / (self.sr / 2))
221
-
222
- mask = np.zeros_like(magnitude)
223
- mask[:bass_cutoff] = 1.0
224
-
225
- filtered_magnitude = magnitude * mask
226
- filtered_stft = filtered_magnitude * np.exp(1j * phase)
227
-
228
- return librosa.istft(filtered_stft)
229
-
230
- def _apply_piano_filter(self, audio):
231
- """Filtro para piano (80Hz - 8kHz)"""
232
- stft = librosa.stft(audio, n_fft=2048)
233
- magnitude, phase = np.abs(stft), np.angle(stft)
234
-
235
- freq_bins = magnitude.shape[0]
236
- piano_start = int(80 * freq_bins / (self.sr / 2))
237
- piano_end = int(8000 * freq_bins / (self.sr / 2))
238
-
239
- mask = np.zeros_like(magnitude)
240
- mask[piano_start:piano_end] = 1.0
241
-
242
- filtered_magnitude = magnitude * mask
243
- filtered_stft = filtered_magnitude * np.exp(1j * phase)
244
-
245
- return librosa.istft(filtered_stft)
246
-
247
- def _apply_guitar_filter(self, audio):
248
- """Filtro para guitarra (100Hz - 8kHz)"""
249
- stft = librosa.stft(audio, n_fft=2048)
250
- magnitude, phase = np.abs(stft), np.angle(stft)
251
-
252
- freq_bins = magnitude.shape[0]
253
- guitar_start = int(100 * freq_bins / (self.sr / 2))
254
- guitar_end = int(8000 * freq_bins / (self.sr / 2))
255
-
256
- mask = np.zeros_like(magnitude)
257
- mask[guitar_start:guitar_end] = 1.0
258
-
259
- filtered_magnitude = magnitude * mask
260
- filtered_stft = filtered_magnitude * np.exp(1j * phase)
261
-
262
- return librosa.istft(filtered_stft)
263
-
264
- def _apply_strings_filter(self, audio):
265
- """Filtro para cuerdas (200Hz - 12kHz)"""
266
- stft = librosa.stft(audio, n_fft=2048)
267
- magnitude, phase = np.abs(stft), np.angle(stft)
268
-
269
- freq_bins = magnitude.shape[0]
270
- strings_start = int(200 * freq_bins / (self.sr / 2))
271
- strings_end = int(12000 * freq_bins / (self.sr / 2))
272
-
273
- mask = np.zeros_like(magnitude)
274
- mask[strings_start:strings_end] = 1.0
275
-
276
- filtered_magnitude = magnitude * mask
277
- filtered_stft = filtered_magnitude * np.exp(1j * phase)
278
-
279
- return librosa.istft(filtered_stft)
280
-
281
- def _apply_winds_filter(self, audio):
282
- """Filtro para vientos (400Hz - 10kHz)"""
283
- stft = librosa.stft(audio, n_fft=2048)
284
- magnitude, phase = np.abs(stft), np.angle(stft)
285
-
286
- freq_bins = magnitude.shape[0]
287
- winds_start = int(400 * freq_bins / (self.sr / 2))
288
- winds_end = int(10000 * freq_bins / (self.sr / 2))
289
-
290
- mask = np.zeros_like(magnitude)
291
- mask[winds_start:winds_end] = 1.0
292
-
293
- filtered_magnitude = magnitude * mask
294
- filtered_stft = filtered_magnitude * np.exp(1j * phase)
295
-
296
- return librosa.istft(filtered_stft)
297
 
298
- def separate_instruments(audio_file, separation_mode="complete"):
299
- """Función principal de separación multi-instrumento"""
300
- if not audio_file:
301
- raise ValueError("⚠️ No se proporcionó archivo de audio")
302
 
303
  try:
304
- # Verificar tamaño
305
- file_size = os.path.getsize(audio_file) / (1024 * 1024)
306
- if file_size > 50:
307
- raise ValueError(f"�� Archivo muy grande: {file_size:.1f}MB (máx 50MB)")
308
 
309
- # Cargar audio
310
- logger.info("🎵 Cargando audio...")
311
- audio, sr = librosa.load(audio_file, sr=44100, mono=True)
312
 
313
- # Normalizar
314
- max_val = np.max(np.abs(audio))
315
- if max_val > 0:
316
- audio = audio / max_val
317
 
318
- # Crear separador
319
- separator = SimpleAudioSeparator()
320
-
321
- # Crear directorio de salida único
322
- timestamp = int(time.time())
323
- song_output_dir = os.path.join(output_dir, f"separated_{timestamp}")
324
- os.makedirs(song_output_dir, exist_ok=True)
325
-
326
- # Nombre base del archivo
327
- base_name = Path(audio_file).stem
328
- output_files = []
329
-
330
- if separation_mode == "vocals_only":
331
- logger.info("🎤 Separando solo voces...")
332
- vocals, instrumental = separator.separate_vocals(audio)
333
-
334
- # Guardar archivos
335
- vocal_path = os.path.join(song_output_dir, f"{base_name}_vocals.wav")
336
- instrumental_path = os.path.join(song_output_dir, f"{base_name}_instrumental.wav")
337
-
338
- sf.write(vocal_path, vocals * max_val * 0.95, 44100)
339
- sf.write(instrumental_path, instrumental * max_val * 0.95, 44100)
340
-
341
- output_files.extend([vocal_path, instrumental_path])
342
-
343
- elif separation_mode == "complete":
344
- logger.info("🎯 Separación completa - TODOS los instrumentos...")
345
-
346
- # Paso 1: Separar voces primero
347
- logger.info("🎤 Paso 1: Voces...")
348
- vocals, remaining = separator.separate_vocals(audio)
349
 
350
- vocal_path = os.path.join(song_output_dir, f"{base_name}_vocals.wav")
351
- sf.write(vocal_path, vocals * max_val * 0.95, 44100)
352
- output_files.append(vocal_path)
353
 
354
- # Paso 2: Separar drums del resto
355
- logger.info("🥁 Paso 2: Batería...")
356
- drums, remaining = separator.separate_drums(remaining)
 
357
 
358
- drums_path = os.path.join(song_output_dir, f"{base_name}_drums.wav")
359
- sf.write(drums_path, drums * max_val * 0.95, 44100)
360
- output_files.append(drums_path)
 
361
 
362
- # Paso 3: Separar bass
363
- logger.info("🎸 Paso 3: Bajo...")
364
- bass, remaining = separator.separate_bass(remaining)
 
365
 
366
- bass_path = os.path.join(song_output_dir, f"{base_name}_bass.wav")
367
- sf.write(bass_path, bass * max_val * 0.95, 44100)
368
- output_files.append(bass_path)
369
-
370
- # Paso 4: Separar piano
371
- logger.info("🎹 Paso 4: Piano...")
372
- piano, remaining = separator.separate_piano(remaining)
373
-
374
- piano_path = os.path.join(song_output_dir, f"{base_name}_piano.wav")
375
- sf.write(piano_path, piano * max_val * 0.95, 44100)
376
- output_files.append(piano_path)
377
-
378
- # Paso 5: Separar guitarra
379
- logger.info("🎸 Paso 5: Guitarra...")
380
- guitar, remaining = separator.separate_guitar(remaining)
381
-
382
- guitar_path = os.path.join(song_output_dir, f"{base_name}_guitar.wav")
383
- sf.write(guitar_path, guitar * max_val * 0.95, 44100)
384
- output_files.append(guitar_path)
385
-
386
- # Paso 6: Separar cuerdas
387
- logger.info("🎻 Paso 6: Cuerdas...")
388
- strings, remaining = separator.separate_strings(remaining)
389
-
390
- strings_path = os.path.join(song_output_dir, f"{base_name}_strings.wav")
391
- sf.write(strings_path, strings * max_val * 0.95, 44100)
392
- output_files.append(strings_path)
393
-
394
- # Paso 7: Separar vientos
395
- logger.info("🎺 Paso 7: Vientos...")
396
- winds, remaining = separator.separate_winds(remaining)
397
-
398
- winds_path = os.path.join(song_output_dir, f"{base_name}_winds.wav")
399
- sf.write(winds_path, winds * max_val * 0.95, 44100)
400
- output_files.append(winds_path)
401
-
402
- # Paso 8: Lo que queda son "otros"
403
- logger.info("🎛️ Paso 8: Otros...")
404
- other_path = os.path.join(song_output_dir, f"{base_name}_other.wav")
405
- sf.write(other_path, remaining * max_val * 0.95, 44100)
406
- output_files.append(other_path)
407
-
408
- elif separation_mode in ["drums_only", "bass_only", "piano_only", "guitar_only"]:
409
- instrument = separation_mode.replace("_only", "")
410
- logger.info(f"🎵 Separando solo {instrument}...")
411
-
412
- if instrument == "drums":
413
- target, remaining = separator.separate_drums(audio)
414
- elif instrument == "bass":
415
- target, remaining = separator.separate_bass(audio)
416
- elif instrument == "piano":
417
- target, remaining = separator.separate_piano(audio)
418
- elif instrument == "guitar":
419
- target, remaining = separator.separate_guitar(audio)
420
-
421
- # Guardar archivos
422
- target_path = os.path.join(song_output_dir, f"{base_name}_{instrument}.wav")
423
- remaining_path = os.path.join(song_output_dir, f"{base_name}_no_{instrument}.wav")
424
-
425
- sf.write(target_path, target * max_val * 0.95, 44100)
426
- sf.write(remaining_path, remaining * max_val * 0.95, 44100)
427
-
428
- output_files.extend([target_path, remaining_path])
429
-
430
- logger.info(f"✅ Separación completada: {len(output_files)} archivos")
431
- return output_files
432
-
433
- except Exception as e:
434
- logger.error(f"❌ Error en separación: {e}")
435
- traceback.print_exc()
436
- raise
437
-
438
- def process_audio(audio_file, separation_mode, progress=gr.Progress()):
439
- """Procesar audio con barra de progreso"""
440
- if audio_file is None:
441
- return [], "⚠️ Sube un archivo de audio"
442
-
443
- try:
444
- progress(0.1, desc="Inicializando...")
445
-
446
- # Verificar directorios
447
- create_directories()
448
-
449
- progress(0.3, desc="Procesando audio...")
450
-
451
- # Separar instrumentos
452
- result_files = separate_instruments(audio_file, separation_mode)
453
 
454
  progress(1.0, desc="¡Completado!")
455
 
456
- instruments_count = len(result_files)
457
- success_msg = f"✅ Separación exitosa: {instruments_count} instrumento(s) separado(s)"
458
-
459
  return result_files, success_msg
460
 
461
  except Exception as e:
462
  error_msg = f"❌ Error: {str(e)}"
 
463
  return [], error_msg
464
 
465
  def create_interface():
466
  """Crear interfaz de usuario"""
467
- with gr.Blocks(title="🎵 Multi-Instrument Separator Pro", theme=gr.themes.Soft()) as app:
468
 
469
  gr.Markdown(title)
470
  gr.Markdown(description)
@@ -472,39 +271,43 @@ def create_interface():
472
  with gr.Row():
473
  with gr.Column():
474
  audio_input = gr.Audio(
475
- label="🎵 Subir archivo de audio (máx 50MB)",
476
  type="filepath"
477
  )
478
 
479
  separation_mode = gr.Radio(
480
  choices=[
481
- ("🚀 Separación completa (8 instrumentos)", "complete"),
482
- ("🎤 Solo voces + instrumental", "vocals_only"),
483
- ("🥁 Solo batería", "drums_only"),
484
- ("🎸 Solo bajo", "bass_only"),
485
- ("🎹 Solo piano", "piano_only"),
486
- ("🎸 Solo guitarra", "guitar_only")
 
 
 
 
487
  ],
488
- value="complete",
489
- label="🎛️ Modo de separación",
490
- info="Selecciona qué instrumentos quieres separar"
491
  )
492
 
493
  process_btn = gr.Button(
494
- "🚀 Separar Instrumentos",
495
  variant="primary",
496
  size="lg"
497
  )
498
 
499
  with gr.Column():
500
  status_output = gr.Textbox(
501
- label="📊 Estado del procesamiento",
502
  lines=8,
503
  interactive=False
504
  )
505
 
506
  output_files = gr.File(
507
- label="📥 Instrumentos separados",
508
  file_count="multiple",
509
  interactive=False
510
  )
@@ -517,37 +320,40 @@ def create_interface():
517
  )
518
 
519
  gr.Markdown("""
520
- ### 🎯 Instrumentos que separa:
521
-
522
- | **Instrumento** | **Descripción** | **Técnica** |
523
- |-----------------|-----------------|-------------|
524
- | 🎤 **Voces** | Voces principales y coros | Filtros vocales + separación H/P |
525
- | 🥁 **Batería** | Kick, snare, hi-hats, toms | Componentes percusivos |
526
- | 🎸 **Bajo** | Frecuencias graves (20-250Hz) | Filtro pasa-bajos |
527
- | 🎹 **Piano** | Teclas y acordes | Componentes híbridos H/P |
528
- | 🎸 **Guitarra** | Acústica y eléctrica | Componentes armónicos |
529
- | 🎻 **Cuerdas** | Violín, viola, cello | Componentes muy armónicos |
530
- | 🎺 **Vientos** | Trompeta, saxo, flauta | Filtros de frecuencias medias-altas |
531
- | 🎛️ **Otros** | Sintetizadores y demás | Todo lo que no encaja arriba |
 
 
 
 
 
 
 
 
 
 
 
 
 
 
532
 
533
  ### 📝 Instrucciones:
534
- 1. **Sube tu archivo** (MP3, WAV, FLAC, M4A - máx 50MB)
535
- 2. **Selecciona modo**: Completo para todos los instrumentos, o individual
536
- 3. **Haz clic en "Separar Instrumentos"**
537
- 4. **Descarga los archivos** generados
538
-
539
- ### ⚡ Características:
540
- - ✅ **Separación multi-instrumento** - Hasta 8 instrumentos diferentes
541
- - ✅ **Tecnología de procesamiento digital** - Sin dependencias complejas de IA
542
- - ✅ **Filtros especializados** - Optimizados para cada tipo de instrumento
543
- - ✅ **Funciona siempre** - Sin errores de modelos ONNX
544
- - ✅ **Rápido y eficiente** - Procesamiento directo con librosa
545
 
546
- ### 🔧 Tecnología:
547
- - **Separación H/P**: Divide componentes armónicos y percusivos
548
- - **Filtros de frecuencia**: Específicos para cada instrumento
549
- - **Análisis espectral**: STFT optimizado para cada caso
550
- - **Máscaras inteligentes**: Enfatizan frecuencias características
551
  """)
552
 
553
  return app
@@ -555,16 +361,12 @@ def create_interface():
555
  def main():
556
  """Función principal"""
557
  try:
558
- logger.info("🎵 Iniciando Multi-Instrument Separator Pro")
559
- logger.info("🔧 Sistema basado en procesamiento digital avanzado")
560
- logger.info("✅ Sin dependencias complejas - Funciona siempre")
561
-
562
- # Crear directorios
563
- create_directories()
564
 
565
  # Crear y lanzar interfaz
566
  app = create_interface()
567
- app.queue(default_concurrency_limit=5)
568
  app.launch(
569
  server_name="0.0.0.0",
570
  server_port=7860,
 
1
  import os
 
 
2
  import sys
 
 
 
 
 
 
3
  import logging
 
4
  import traceback
5
  import tempfile
6
+ import time
7
  from pathlib import Path
8
 
9
+ import gradio as gr
10
+ from audio_separator.separator import Separator
11
+
12
  # Configuración
 
13
  logging.basicConfig(level=logging.INFO)
14
  logger = logging.getLogger(__name__)
15
 
16
+ title = "<center><strong><font size='7'>🎵 AI Audio Separator Pro</font></strong></center>"
17
  description = """
18
+ ### 🤖 Separador de audio con IA real - Como r3gm pero que funciona
19
+ **Usa los mismos modelos MDX-Net, Demucs y VR de Ultimate Vocal Remover**
20
+ - 🎤 **Voces ultra limpias** - Modelos MDX-Net de alta calidad
21
+ - 🥁 **Separación 4-stems** - Voces, batería, bajo, otros
22
+ - 🎸 **Modelos especializados** - Piano, guitarra, cuerdas
23
+ - 🎛️ **IA profesional** - Misma tecnología que UVR y r3gm
24
+ - **Automático** - Descarga y configura modelos automáticamente
 
 
 
 
 
25
  """
26
 
27
+ # Configuración de modelos disponibles (se descargan automáticamente)
28
+ AVAILABLE_MODELS = {
29
+ # Modelos principales de separación
30
+ "vocals_ht": {
31
+ "model_name": "UVR-MDX-NET-Voc_FT.onnx",
32
+ "description": "🎤 Voces de alta calidad (MDX-Net)",
33
+ "stems": ["Vocals", "Instrumental"]
34
+ },
35
+ "demucs_4stems": {
36
+ "model_name": "htdemucs_ft.yaml",
37
+ "description": "🎯 Separación completa (Demucs 4-stems)",
38
+ "stems": ["vocals", "drums", "bass", "other"]
39
+ },
40
+ "instrumental_ht": {
41
+ "model_name": "UVR-MDX-NET-Inst_HQ_4.onnx",
42
+ "description": "🎵 Instrumental de alta calidad",
43
+ "stems": ["Other", "Instrumental"]
44
+ },
45
+
46
+ # Modelos especializados
47
+ "piano": {
48
+ "model_name": "Kim_Piano_1.onnx",
49
+ "description": "🎹 Piano especializado",
50
+ "stems": ["Piano", "No Piano"]
51
+ },
52
+ "drums": {
53
+ "model_name": "UVR-MDX-NET-Kag_2.onnx",
54
+ "description": "🥁 Batería especializada",
55
+ "stems": ["Drums", "No Drums"]
56
+ },
57
+ "bass": {
58
+ "model_name": "Kim_Bass_1.onnx",
59
+ "description": "🎸 Bajo especializado",
60
+ "stems": ["Bass", "No Bass"]
61
+ },
62
+ "guitar": {
63
+ "model_name": "UVR-MDX-NET-Kag_3.onnx",
64
+ "description": "🎸 Guitarra especializada",
65
+ "stems": ["Guitar", "No Guitar"]
66
+ },
67
+ "karaoke": {
68
+ "model_name": "UVR_MDXNET_KARA_2.onnx",
69
+ "description": "🎤 Karaoke/Voces principales",
70
+ "stems": ["Main Vocals", "Backup Vocals"]
71
+ },
72
+ "dereverb": {
73
+ "model_name": "Reverb_HQ_By_FoxJoy.onnx",
74
+ "description": "🔄 Eliminar reverb",
75
+ "stems": ["Dry", "Reverb"]
76
+ }
77
  }
78
 
79
+ class AIAudioSeparator:
80
+ """Separador de audio usando IA real con modelos pre-entrenados"""
 
 
 
 
 
 
 
 
81
 
82
  def __init__(self):
83
+ self.output_dir = os.path.join(tempfile.gettempdir(), "audio_separator_output")
84
+ os.makedirs(self.output_dir, exist_ok=True)
85
+ logger.info("🤖 Inicializando AI Audio Separator")
86
 
87
+ def separate_audio(self, audio_file, model_key, progress_callback=None):
88
+ """Separar audio usando modelo especificado"""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
89
  try:
90
+ if not audio_file or not os.path.exists(audio_file):
91
+ raise ValueError("❌ Archivo de audio no válido")
92
+
93
+ # Verificar tamaño
94
+ file_size = os.path.getsize(audio_file) / (1024 * 1024)
95
+ if file_size > 100:
96
+ raise ValueError(f"❌ Archivo muy grande: {file_size:.1f}MB (máx 100MB)")
97
+
98
+ model_config = AVAILABLE_MODELS.get(model_key)
99
+ if not model_config:
100
+ raise ValueError(f"❌ Modelo no encontrado: {model_key}")
101
+
102
+ model_name = model_config["model_name"]
103
+
104
+ logger.info(f"🎵 Cargando modelo: {model_config['description']}")
105
+ if progress_callback:
106
+ progress_callback(0.2, f"Cargando modelo {model_name}")
107
+
108
+ # Crear separador con configuración automática
109
+ separator = Separator(
110
+ output_dir=self.output_dir,
111
+ output_format="wav",
112
+ normalization_threshold=0.9,
113
+ enable_denoise=True,
114
+ log_level=logging.WARNING # Reducir logs verbosos
115
+ )
116
+
117
+ logger.info(f"🔄 Separando con {model_name}")
118
+ if progress_callback:
119
+ progress_callback(0.4, f"Procesando con IA...")
120
+
121
+ # Realizar separación
122
+ try:
123
+ # Cargar modelo automáticamente si no existe
124
+ separator.load_model(model_filename=model_name)
125
+
126
+ if progress_callback:
127
+ progress_callback(0.7, "Separando audio...")
128
+
129
+ # Procesar archivo
130
+ result = separator.separate(audio_file)
131
+
132
+ if progress_callback:
133
+ progress_callback(0.9, "Finalizando...")
134
+
135
+ # Obtener archivos de salida
136
+ output_files = []
137
+ if isinstance(result, list):
138
+ output_files = result
139
+ elif isinstance(result, dict):
140
+ output_files = list(result.values())
141
+ else:
142
+ # Buscar archivos en directorio de salida
143
+ base_name = Path(audio_file).stem
144
+ for file in os.listdir(self.output_dir):
145
+ if file.startswith(base_name) and file.endswith('.wav'):
146
+ output_files.append(os.path.join(self.output_dir, file))
147
+
148
+ # Filtrar archivos válidos
149
+ valid_files = [f for f in output_files if os.path.exists(f) and os.path.getsize(f) > 1024]
150
+
151
+ if not valid_files:
152
+ raise Exception("❌ No se generaron archivos de salida válidos")
153
+
154
+ logger.info(f"✅ Separación exitosa: {len(valid_files)} archivo(s)")
155
+ return valid_files
156
+
157
+ except Exception as model_error:
158
+ logger.error(f"Error con modelo {model_name}: {model_error}")
159
+
160
+ # Fallback a modelo básico si falla el especializado
161
+ if model_key != "vocals_ht":
162
+ logger.info("🔄 Intentando con modelo básico de voces...")
163
+ separator.load_model(model_filename="UVR-MDX-NET-Voc_FT.onnx")
164
+ result = separator.separate(audio_file)
165
+
166
+ output_files = []
167
+ base_name = Path(audio_file).stem
168
+ for file in os.listdir(self.output_dir):
169
+ if file.startswith(base_name) and file.endswith('.wav'):
170
+ output_files.append(os.path.join(self.output_dir, file))
171
+
172
+ valid_files = [f for f in output_files if os.path.exists(f) and os.path.getsize(f) > 1024]
173
+ if valid_files:
174
+ return valid_files
175
+
176
+ raise model_error
177
+
178
  except Exception as e:
179
+ logger.error(f"Error en separación: {e}")
180
+ traceback.print_exc()
181
+ raise
182
 
183
+ def separate_multi_model(self, audio_file, models_list, progress_callback=None):
184
+ """Separar usando múltiples modelos en secuencia"""
185
  try:
186
+ all_outputs = []
187
+ total_models = len(models_list)
188
+
189
+ for i, model_key in enumerate(models_list):
190
+ if progress_callback:
191
+ progress = 0.1 + (i / total_models) * 0.8
192
+ model_name = AVAILABLE_MODELS[model_key]["description"]
193
+ progress_callback(progress, f"Modelo {i+1}/{total_models}: {model_name}")
194
+
195
+ try:
196
+ outputs = self.separate_audio(audio_file, model_key)
197
+ all_outputs.extend(outputs)
198
+ logger.info(f"✅ Completado: {AVAILABLE_MODELS[model_key]['description']}")
199
+ except Exception as e:
200
+ logger.warning(f"⚠️ Error con {model_key}: {e}")
201
+ continue
202
 
203
+ if not all_outputs:
204
+ raise Exception("❌ Ningún modelo produjo resultados válidos")
 
 
 
 
 
 
 
205
 
206
+ # Eliminar duplicados
207
+ unique_outputs = list(set(all_outputs))
208
+ return unique_outputs
209
 
 
210
  except Exception as e:
211
+ logger.error(f"Error en separación multi-modelo: {e}")
212
+ raise
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
213
 
214
+ def process_audio(audio_file, separation_mode, progress=gr.Progress()):
215
+ """Procesar audio con barra de progreso"""
216
+ if audio_file is None:
217
+ return [], "⚠️ Por favor sube un archivo de audio"
218
 
219
  try:
220
+ separator = AIAudioSeparator()
 
 
 
221
 
222
+ def progress_callback(value, desc):
223
+ progress(value, desc=desc)
 
224
 
225
+ progress(0.1, desc="Inicializando IA...")
 
 
 
226
 
227
+ if separation_mode == "vocals_ultra":
228
+ # Voces de máxima calidad
229
+ result_files = separator.separate_audio(audio_file, "vocals_ht", progress_callback)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
230
 
231
+ elif separation_mode == "demucs_4stems":
232
+ # Separación completa 4 stems
233
+ result_files = separator.separate_audio(audio_file, "demucs_4stems", progress_callback)
234
 
235
+ elif separation_mode == "multi_instrument":
236
+ # Múltiples modelos especializados
237
+ models = ["vocals_ht", "drums", "bass", "piano"]
238
+ result_files = separator.separate_multi_model(audio_file, models, progress_callback)
239
 
240
+ elif separation_mode in ["piano_only", "drums_only", "bass_only", "guitar_only", "karaoke_only", "dereverb_only"]:
241
+ # Modelos individuales especializados
242
+ model_key = separation_mode.replace("_only", "")
243
+ result_files = separator.separate_audio(audio_file, model_key, progress_callback)
244
 
245
+ elif separation_mode == "professional":
246
+ # Combinación profesional: voces + karaoke + dereverb
247
+ models = ["vocals_ht", "karaoke", "dereverb"]
248
+ result_files = separator.separate_multi_model(audio_file, models, progress_callback)
249
 
250
+ else:
251
+ # Fallback a voces básicas
252
+ result_files = separator.separate_audio(audio_file, "vocals_ht", progress_callback)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
253
 
254
  progress(1.0, desc="¡Completado!")
255
 
256
+ success_msg = f"✅ Separación con IA completada: {len(result_files)} archivo(s)"
 
 
257
  return result_files, success_msg
258
 
259
  except Exception as e:
260
  error_msg = f"❌ Error: {str(e)}"
261
+ logger.error(error_msg)
262
  return [], error_msg
263
 
264
  def create_interface():
265
  """Crear interfaz de usuario"""
266
+ with gr.Blocks(title="🎵 AI Audio Separator Pro", theme=gr.themes.Soft()) as app:
267
 
268
  gr.Markdown(title)
269
  gr.Markdown(description)
 
271
  with gr.Row():
272
  with gr.Column():
273
  audio_input = gr.Audio(
274
+ label="🎵 Subir archivo de audio (máx 100MB)",
275
  type="filepath"
276
  )
277
 
278
  separation_mode = gr.Radio(
279
  choices=[
280
+ ("🎤 Voces Ultra HD (MDX-Net)", "vocals_ultra"),
281
+ ("🎯 4 Stems Completo (Demucs AI)", "demucs_4stems"),
282
+ ("🚀 Multi-Instrumento (4 modelos)", "multi_instrument"),
283
+ ("🎹 Solo Piano (Kim Model)", "piano_only"),
284
+ ("🥁 Solo Batería (MDX-Net)", "drums_only"),
285
+ ("🎸 Solo Bajo (Kim Model)", "bass_only"),
286
+ ("🎸 Solo Guitarra (MDX-Net)", "guitar_only"),
287
+ ("🎤 Karaoke/Voces Principales", "karaoke_only"),
288
+ ("🔄 Eliminar Reverb", "dereverb_only"),
289
+ ("👑 Profesional (Multi-modelo)", "professional")
290
  ],
291
+ value="demucs_4stems",
292
+ label="🤖 Modelo de IA",
293
+ info="Cada modelo usa redes neuronales especializadas"
294
  )
295
 
296
  process_btn = gr.Button(
297
+ "🚀 Separar con IA",
298
  variant="primary",
299
  size="lg"
300
  )
301
 
302
  with gr.Column():
303
  status_output = gr.Textbox(
304
+ label="🤖 Estado de la IA",
305
  lines=8,
306
  interactive=False
307
  )
308
 
309
  output_files = gr.File(
310
+ label="📥 Archivos separados por IA",
311
  file_count="multiple",
312
  interactive=False
313
  )
 
320
  )
321
 
322
  gr.Markdown("""
323
+ ### 🤖 Modelos de IA disponibles:
324
+
325
+ | **Modelo** | **Tecnología** | **Salidas** | **Calidad** |
326
+ |------------|----------------|-------------|-------------|
327
+ | 🎤 **Voces Ultra HD** | MDX-Net UVR | Voces + Instrumental | ⭐⭐⭐⭐⭐ |
328
+ | 🎯 **4 Stems Completo** | Demucs v4 AI | Voces, Batería, Bajo, Otros | ⭐⭐⭐⭐⭐ |
329
+ | 🎹 **Piano** | Kim Model | Piano + Sin Piano | ⭐⭐⭐⭐ |
330
+ | 🥁 **Batería** | MDX-Net Kag | Batería + Sin Batería | ⭐⭐⭐⭐ |
331
+ | 🎸 **Bajo** | Kim Model | Bajo + Sin Bajo | ⭐⭐⭐⭐ |
332
+ | 🎸 **Guitarra** | MDX-Net Kag | Guitarra + Sin Guitarra | ⭐⭐⭐⭐ |
333
+ | 🎤 **Karaoke** | MDXNET KARA | Voces Principales + Coros | ⭐⭐⭐⭐ |
334
+ | 🔄 **Dereverb** | Reverb HQ | Audio Seco + Reverb | ⭐⭐⭐⭐ |
335
+
336
+ ### ⚡ Características de la IA:
337
+ - ✅ **Mismos modelos que UVR** - Tecnología probada y de calidad profesional
338
+ - ✅ **Descarga automática** - Los modelos se descargan la primera vez que los usas
339
+ - ✅ **MDX-Net + Demucs** - Las mejores arquitecturas de IA para separación de audio
340
+ - ✅ **Modelos especializados** - Cada instrumento tiene su red neuronal optimizada
341
+ - ✅ **Calidad profesional** - Resultados comparables a software comercial
342
+ - ✅ **Multi-modelo** - Combina varios modelos para mejores resultados
343
+
344
+ ### 🔧 Tecnologías de IA utilizadas:
345
+ - **MDX-Net**: Arquitectura híbrida tiempo-frecuencia para separación de alta calidad
346
+ - **Demucs v4**: Red convolucional profunda para separación multi-instrumento
347
+ - **Kim Models**: Modelos especializados para piano y bajo
348
+ - **UVR Models**: Modelos de Ultimate Vocal Remover optimizados
349
 
350
  ### 📝 Instrucciones:
351
+ 1. **Sube tu archivo** (MP3, WAV, FLAC, M4A - máx 100MB)
352
+ 2. **Selecciona modelo de IA** según lo que quieras separar
353
+ 3. **Haz clic en "Separar con IA"** - Los modelos se descargan automáticamente
354
+ 4. **Descarga los resultados** - Archivos de alta calidad separados por IA
 
 
 
 
 
 
 
355
 
356
+ > **Nota**: La primera vez que uses cada modelo, se descargará automáticamente (puede tomar unos minutos). Las siguientes veces será instantáneo.
 
 
 
 
357
  """)
358
 
359
  return app
 
361
  def main():
362
  """Función principal"""
363
  try:
364
+ logger.info("🤖 Iniciando AI Audio Separator Pro")
365
+ logger.info("🔧 Usando librerías de IA real: audio-separator + UVR models")
 
 
 
 
366
 
367
  # Crear y lanzar interfaz
368
  app = create_interface()
369
+ app.queue(default_concurrency_limit=3) # Límite bajo para modelos de IA
370
  app.launch(
371
  server_name="0.0.0.0",
372
  server_port=7860,