PlayerBPlaytime commited on
Commit
cbcfe40
·
verified ·
1 Parent(s): c1408ce

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +218 -0
app.py ADDED
@@ -0,0 +1,218 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import numpy as np
3
+ import soundfile as sf
4
+ import os
5
+ import tempfile
6
+ import zipfile
7
+ from pathlib import Path
8
+
9
+ def get_channel_name(index, total_channels):
10
+ """Asigna nombres a los canales según la configuración Atmos/Surround"""
11
+
12
+ # Configuraciones comunes de Dolby Atmos
13
+ channel_maps = {
14
+ 2: ["Left", "Right"],
15
+ 6: ["Left", "Right", "Center", "LFE", "Left Surround", "Right Surround"],
16
+ 8: ["Left", "Right", "Center", "LFE", "Left Surround", "Right Surround",
17
+ "Left Back", "Right Back"],
18
+ 10: ["Left", "Right", "Center", "LFE", "Left Surround", "Right Surround",
19
+ "Left Back", "Right Back", "Left Height", "Right Height"],
20
+ 12: ["Left", "Right", "Center", "LFE", "Left Surround", "Right Surround",
21
+ "Left Back", "Right Back", "Left Height Front", "Right Height Front",
22
+ "Left Height Rear", "Right Height Rear"],
23
+ 14: ["Left", "Right", "Center", "LFE", "Left Surround", "Right Surround",
24
+ "Left Back", "Right Back", "Left Height Front", "Right Height Front",
25
+ "Left Height Rear", "Right Height Rear", "Top Front", "Top Rear"],
26
+ 16: ["Left", "Right", "Center", "LFE", "Left Surround", "Right Surround",
27
+ "Left Back", "Right Back", "Left Wide", "Right Wide",
28
+ "Left Height Front", "Right Height Front", "Left Height Rear",
29
+ "Right Height Rear", "Top Front", "Top Rear"],
30
+ }
31
+
32
+ if total_channels in channel_maps:
33
+ return channel_maps[total_channels][index]
34
+ else:
35
+ return f"Channel_{index + 1}"
36
+
37
+ def extract_stems(audio_file, output_format):
38
+ """Extrae todos los stems/canales de un archivo de audio multicanal"""
39
+
40
+ if audio_file is None:
41
+ return None, "❌ Por favor, sube un archivo de audio"
42
+
43
+ try:
44
+ # Leer el archivo de audio
45
+ audio_data, sample_rate = sf.read(audio_file)
46
+
47
+ # Obtener información del archivo
48
+ if len(audio_data.shape) == 1:
49
+ # Audio mono
50
+ num_channels = 1
51
+ audio_data = audio_data.reshape(-1, 1)
52
+ else:
53
+ num_channels = audio_data.shape[1]
54
+
55
+ # Información del archivo
56
+ duration = len(audio_data) / sample_rate
57
+ file_name = Path(audio_file).stem
58
+
59
+ info_text = f"""
60
+ ## 📊 Información del archivo
61
+
62
+ - **Nombre:** {Path(audio_file).name}
63
+ - **Canales detectados:** {num_channels}
64
+ - **Sample Rate:** {sample_rate} Hz
65
+ - **Duración:** {duration:.2f} segundos
66
+ - **Formato de salida:** {output_format.upper()}
67
+
68
+ ## 🎚️ Canales extraídos:
69
+
70
+ """
71
+
72
+ # Crear directorio temporal para los stems
73
+ temp_dir = tempfile.mkdtemp()
74
+ stem_files = []
75
+
76
+ # Extraer cada canal
77
+ for i in range(num_channels):
78
+ channel_name = get_channel_name(i, num_channels)
79
+ channel_data = audio_data[:, i]
80
+
81
+ # Normalizar el canal
82
+ max_val = np.max(np.abs(channel_data))
83
+ if max_val > 0:
84
+ channel_data = channel_data / max_val * 0.95
85
+
86
+ # Guardar el stem
87
+ stem_filename = f"{file_name}_{channel_name.replace(' ', '_')}.{output_format}"
88
+ stem_path = os.path.join(temp_dir, stem_filename)
89
+
90
+ sf.write(stem_path, channel_data, sample_rate)
91
+ stem_files.append(stem_path)
92
+
93
+ info_text += f"- ✅ **{channel_name}** → `{stem_filename}`\n"
94
+
95
+ # Crear archivo ZIP con todos los stems
96
+ zip_filename = f"{file_name}_stems.zip"
97
+ zip_path = os.path.join(temp_dir, zip_filename)
98
+
99
+ with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zipf:
100
+ for stem_file in stem_files:
101
+ zipf.write(stem_file, os.path.basename(stem_file))
102
+
103
+ info_text += f"\n## 📦 Descarga\n\nTodos los stems empaquetados en: `{zip_filename}`"
104
+
105
+ return zip_path, info_text
106
+
107
+ except Exception as e:
108
+ return None, f"❌ Error procesando el archivo: {str(e)}"
109
+
110
+ def create_demo_file():
111
+ """Crea un archivo de demostración multicanal"""
112
+
113
+ temp_dir = tempfile.mkdtemp()
114
+ demo_path = os.path.join(temp_dir, "demo_5.1_surround.wav")
115
+
116
+ sample_rate = 48000
117
+ duration = 3 # segundos
118
+ t = np.linspace(0, duration, int(sample_rate * duration))
119
+
120
+ # Crear 6 canales con diferentes frecuencias (simulando 5.1)
121
+ channels = [
122
+ np.sin(2 * np.pi * 440 * t) * 0.5, # Left - La
123
+ np.sin(2 * np.pi * 554 * t) * 0.5, # Right - Do#
124
+ np.sin(2 * np.pi * 330 * t) * 0.5, # Center - Mi
125
+ np.sin(2 * np.pi * 60 * t) * 0.8, # LFE - Bajo
126
+ np.sin(2 * np.pi * 392 * t) * 0.4, # Left Surround - Sol
127
+ np.sin(2 * np.pi * 494 * t) * 0.4, # Right Surround - Si
128
+ ]
129
+
130
+ # Combinar canales
131
+ audio_data = np.column_stack(channels)
132
+
133
+ sf.write(demo_path, audio_data, sample_rate)
134
+
135
+ return demo_path
136
+
137
+ # Interfaz Gradio
138
+ with gr.Blocks(
139
+ title="🎵 Dolby Atmos Stem Extractor",
140
+ theme=gr.themes.Soft(primary_hue="purple", secondary_hue="blue")
141
+ ) as demo:
142
+
143
+ gr.Markdown("""
144
+ # 🎵 Dolby Atmos Stem Extractor
145
+
146
+ Extrae todos los canales/stems de archivos de audio **Dolby Atmos** o **Surround**.
147
+
148
+ ### 🎯 Formatos soportados:
149
+ - **Entrada:** WAV, FLAC, AIFF, OGG (multicanal)
150
+ - **Configuraciones:** Stereo, 5.1, 7.1, 7.1.4, 9.1.6, y más
151
+
152
+ ### 📝 Instrucciones:
153
+ 1. Sube tu archivo de audio multicanal
154
+ 2. Selecciona el formato de salida
155
+ 3. ¡Descarga tus stems!
156
+ """)
157
+
158
+ with gr.Row():
159
+ with gr.Column(scale=1):
160
+ audio_input = gr.File(
161
+ label="📁 Sube tu archivo de audio",
162
+ file_types=[".wav", ".flac", ".aiff", ".ogg", ".mp3", ".m4a"],
163
+ type="filepath"
164
+ )
165
+
166
+ output_format = gr.Radio(
167
+ choices=["wav", "flac", "ogg"],
168
+ value="wav",
169
+ label="🎚️ Formato de salida"
170
+ )
171
+
172
+ extract_btn = gr.Button(
173
+ "🚀 Extraer Stems",
174
+ variant="primary",
175
+ size="lg"
176
+ )
177
+
178
+ demo_btn = gr.Button(
179
+ "🎹 Generar archivo demo 5.1",
180
+ variant="secondary"
181
+ )
182
+
183
+ with gr.Column(scale=1):
184
+ output_file = gr.File(
185
+ label="📦 Descargar Stems (ZIP)"
186
+ )
187
+
188
+ info_output = gr.Markdown(
189
+ label="📊 Información",
190
+ value="*Sube un archivo para comenzar...*"
191
+ )
192
+
193
+ # Ejemplos
194
+ gr.Markdown("""
195
+ ---
196
+ ### 💡 Tips:
197
+ - Los archivos **Dolby Atmos** típicamente tienen 12-16 canales
198
+ - El formato **5.1 Surround** tiene 6 canales
199
+ - El formato **7.1 Surround** tiene 8 canales
200
+ - Los canales de **altura** (Height) son característicos de Atmos
201
+ """)
202
+
203
+ # Eventos
204
+ extract_btn.click(
205
+ fn=extract_stems,
206
+ inputs=[audio_input, output_format],
207
+ outputs=[output_file, info_output]
208
+ )
209
+
210
+ demo_btn.click(
211
+ fn=create_demo_file,
212
+ inputs=[],
213
+ outputs=[audio_input]
214
+ )
215
+
216
+ # Lanzar
217
+ if __name__ == "__main__":
218
+ demo.launch()