gelpi01 commited on
Commit
a8a043c
·
1 Parent(s): 5406992

Añadir pipeline de audio y interfaz Gradio

Browse files
Files changed (2) hide show
  1. app.py +34 -0
  2. audio_pipeline.py +124 -0
app.py ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ from audio_pipeline import (
3
+ descargar_audio,
4
+ separar_audio_demucs,
5
+ limpiar_stems,
6
+ combinar_stems_sin_vocales,
7
+ reducir_ruido
8
+ )
9
+
10
+ def generar_base(url: str):
11
+ # 1) Descarga el audio
12
+ descargar_audio(url, output_filename="audio")
13
+ # 2) Separa stems
14
+ separar_audio_demucs("audio.wav", output_dir="separated")
15
+ stems_dir = "separated/audio"
16
+ # 3) Limpia stems
17
+ limpiar_stems(stems_dir)
18
+ # 4) Genera base instrumental
19
+ combinar_stems_sin_vocales(stems_dir)
20
+ # 5) Reduce ruido
21
+ resultado = f"{stems_dir}/base_instrumental_clean.wav"
22
+ reducir_ruido(f"{stems_dir}/base_instrumental.wav", resultado)
23
+ return resultado
24
+
25
+ demo = gr.Interface(
26
+ fn=generar_base,
27
+ inputs=gr.Text(label="URL de YouTube"),
28
+ outputs=gr.Audio(label="Base instrumental limpia"),
29
+ title="Generador de Base Instrumental",
30
+ description="Pega la URL de un vídeo de YouTube y espera la creación de la instrumental."
31
+ )
32
+
33
+ if __name__ == "__main__":
34
+ demo.launch()
audio_pipeline.py ADDED
@@ -0,0 +1,124 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import yt_dlp
2
+ import subprocess
3
+ import sys
4
+ import os
5
+ import librosa
6
+ import numpy as np
7
+ import soundfile as sf
8
+ import noisereduce as nr
9
+ import scipy.signal as signal
10
+
11
+ # ###########################################
12
+ # Funciones de Filtros y Efectos
13
+ # ###########################################
14
+
15
+ def highpass_filter(audio, sr, cutoff=80, order=4):
16
+ """Aplica un filtro Butterworth de paso alto al audio."""
17
+ sos = signal.butter(order, cutoff, btype='highpass', fs=sr, output='sos')
18
+ filtered = signal.sosfilt(sos, audio)
19
+ return filtered
20
+
21
+ # ###########################################
22
+ # Etapa 1: Descarga y Separación con Demucs
23
+ # ###########################################
24
+
25
+ def descargar_audio(url, output_filename='audio'):
26
+ """Descarga el audio de YouTube y lo convierte a WAV."""
27
+ opciones = {
28
+ 'format': 'bestaudio/best',
29
+ 'outtmpl': f'{output_filename}.%(ext)s',
30
+ 'postprocessors': [{
31
+ 'key': 'FFmpegExtractAudio',
32
+ 'preferredcodec': 'wav',
33
+ }],
34
+ }
35
+ with yt_dlp.YoutubeDL(opciones) as ydl:
36
+ ydl.download([url])
37
+
38
+
39
+ def separar_audio_demucs(input_file, output_dir="separated"):
40
+ """Separa stems usando Demucs."""
41
+ comando = [
42
+ sys.executable, '-m', 'demucs',
43
+ input_file,
44
+ '--out', output_dir
45
+ ]
46
+ try:
47
+ subprocess.run(comando, check=True)
48
+ print("Separación con Demucs completada.")
49
+ except subprocess.CalledProcessError as e:
50
+ print(f"Error durante la separación con Demucs: {e}")
51
+ sys.exit(1)
52
+
53
+ # ###########################################
54
+ # Función para limpiar cada stem (reducción de ruido, etc.)
55
+ # ###########################################
56
+
57
+ def limpiar_stems(stems_dir):
58
+ """Aplica reducción de ruido a cada stem en stems_dir."""
59
+ for archivo in os.listdir(stems_dir):
60
+ if archivo.endswith('.wav'):
61
+ file_path = os.path.join(stems_dir, archivo)
62
+ y, sr = librosa.load(file_path, sr=None)
63
+ reduced = nr.reduce_noise(y=y, sr=sr)
64
+ sf.write(file_path.replace('.wav', '_cleaned.wav'), reduced, sr)
65
+
66
+ # ###########################################
67
+ # Combina stems excluyendo la parte vocal
68
+ # ###########################################
69
+
70
+ def combinar_stems_sin_vocales(stems_dir):
71
+ """Mezcla todos los stems excepto los que contienen vocales."""
72
+ archivos = [f for f in os.listdir(stems_dir) if f.endswith('.wav')]
73
+ archivos_clean = [f for f in archivos if 'cleaned' in f.lower() and 'vocal' not in f.lower()]
74
+ if archivos_clean:
75
+ lista_archivos = archivos_clean
76
+ else:
77
+ lista_archivos = [f for f in archivos if 'vocal' not in f.lower()]
78
+ signals = []
79
+ for archivo in lista_archivos:
80
+ file_path = os.path.join(stems_dir, archivo)
81
+ print(f"Incluyendo: {archivo}")
82
+ y, sr = librosa.load(file_path, sr=None)
83
+ signals.append(y)
84
+ if not signals:
85
+ print("No se encontraron stems para combinar (excluyendo vocales).")
86
+ return
87
+ max_len = max(len(s) for s in signals)
88
+ mezclado = sum(np.pad(s, (0, max_len - len(s))) for s in signals) / len(signals)
89
+ sf.write(os.path.join(stems_dir, 'base_instrumental.wav'), mezclado, sr)
90
+
91
+ # ###########################################
92
+ # Reducción de ruido en archivo de audio
93
+ # ###########################################
94
+
95
+ def reducir_ruido(input_file, output_file, noise_duration=0.5):
96
+ """Aplica reducción de ruido basada en los primeros segundos de audio."""
97
+ y, sr = librosa.load(input_file, sr=None)
98
+ noise = y[:int(noise_duration * sr)]
99
+ reduced = nr.reduce_noise(y=y, sr=sr, y_noise=noise)
100
+ sf.write(output_file, reduced, sr)
101
+
102
+ # ###########################################
103
+ # Función principal
104
+ # ###########################################
105
+
106
+ def main():
107
+ url = input("Introduce la URL de YouTube: ")
108
+ output_name = input("Nombre base para archivos (sin extensión): ")
109
+ descargar_audio(url, output_filename=output_name)
110
+ audio_file = f"{output_name}.wav"
111
+ separar_audio_demucs(audio_file, output_dir="separated")
112
+ stems_dir = os.path.join("separated", output_name)
113
+ limpiar_stems(stems_dir)
114
+
115
+ output_base = os.path.join(stems_dir, 'base_instrumental.wav')
116
+ print("Combinando stems 'cleaned' para generar la base instrumental...")
117
+ combinar_stems_sin_vocales(stems_dir)
118
+
119
+ output_clean = os.path.join(stems_dir, 'base_instrumental_clean.wav')
120
+ print("Aplicando reducción de ruido...")
121
+ reducir_ruido(output_base, output_clean, noise_duration=0.5)
122
+
123
+ # Se omite la etapa de mastering para mantener la calidad original de la mezcla
124
+ print("Proceso completado. Revisa el archivo 'base_instrumental_clean.wav' para escuchar el resultado final.")