Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -4,118 +4,126 @@ import numpy as np
|
|
| 4 |
from pydub import AudioSegment
|
| 5 |
import os
|
| 6 |
|
| 7 |
-
#
|
| 8 |
-
|
| 9 |
|
| 10 |
-
def
|
|
|
|
| 11 |
if t is None:
|
| 12 |
-
return None, "SISTEMA: ESPERANDO
|
| 13 |
|
| 14 |
try:
|
| 15 |
-
# --- ANÁLISIS SÓNICO ---
|
| 16 |
y, sr = librosa.load(t, duration=30)
|
| 17 |
|
| 18 |
-
#
|
| 19 |
chroma = librosa.feature.chroma_cqt(y=y, sr=sr)
|
| 20 |
nt = ["C","C#","D","D#","E","F","F#","G","G#","A","A#","B"]
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
# Detector de BPM (Fix para error de escalares)
|
| 24 |
tempo, _ = librosa.beat.beat_track(y=y, sr=sr)
|
| 25 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 26 |
|
| 27 |
# Análisis de Espectro
|
| 28 |
S = np.abs(librosa.stft(y))
|
| 29 |
f = librosa.fft_frequencies(sr=sr)
|
| 30 |
-
|
| 31 |
-
|
|
|
|
| 32 |
|
| 33 |
audio = AudioSegment.from_file(t)
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
# --- MÓDULO: MIX FIX ---
|
| 37 |
if active_fix:
|
| 38 |
audio = audio.high_pass_filter(32)
|
| 39 |
-
if
|
| 40 |
-
audio = audio.low_pass_filter(
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
# --- MÓDULO: GHOST MASTER ---
|
| 46 |
if active_master:
|
| 47 |
-
|
| 48 |
-
audio = audio.compressor_dynamic_range(threshold=-16.0, ratio=3.8, attack=12.0, release=150.0)
|
| 49 |
audio = audio.apply_gain((p / 10) - 3)
|
| 50 |
-
log_process += "[✓] MASTER: Maximización y Glue activos.\n"
|
| 51 |
-
else:
|
| 52 |
-
log_process += "[ ] MASTER: Bypass.\n"
|
| 53 |
|
| 54 |
-
audio.export("
|
| 55 |
|
| 56 |
-
|
| 57 |
-
diag =
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
diag += "
|
| 61 |
-
|
| 62 |
-
intel = "---
|
| 63 |
if r is not None:
|
| 64 |
yr, _ = librosa.load(r, duration=30)
|
| 65 |
diff = 20 * np.log10(np.sqrt(np.mean(yr**2)) / (np.sqrt(np.mean(y**2)) + 1e-6))
|
| 66 |
-
intel += f"• ENERGÍA:
|
| 67 |
-
intel += "•
|
| 68 |
else:
|
| 69 |
-
intel += "
|
| 70 |
|
| 71 |
-
return "
|
| 72 |
|
| 73 |
except Exception as e:
|
| 74 |
-
return None, f"ERROR
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 75 |
|
| 76 |
-
# ---
|
| 77 |
css = """
|
| 78 |
-
body, .gradio-container { background-color: #
|
| 79 |
-
.gr-box { border:
|
| 80 |
-
.gr-button-primary { background:
|
| 81 |
-
.gr-
|
| 82 |
-
|
| 83 |
-
.gr-textbox textarea { background: #000 !important; border: 1px solid #333 !important; color: #00ff44 !important; font-family: 'Courier New', monospace; }
|
| 84 |
-
#header-skull { text-align: center; padding: 15px; filter: drop-shadow(0 0 8px #ff9d00); }
|
| 85 |
"""
|
| 86 |
|
| 87 |
with gr.Blocks(theme=gr.themes.Monochrome(), css=css) as demo:
|
| 88 |
-
|
| 89 |
-
gr.HTML(f"<img src='{SKULL_GIF}' width='85'>")
|
| 90 |
-
gr.Markdown("<h1 style='text-align:center; margin-top:20px;'>GHOST COMMAND CENTER v21</h1>")
|
| 91 |
|
| 92 |
with gr.Row():
|
| 93 |
-
|
| 94 |
-
with gr.Column(scale=1, variant="panel"):
|
| 95 |
with gr.Row():
|
| 96 |
-
out_k = gr.Label(label="
|
| 97 |
-
out_b = gr.Label(label="BPM
|
| 98 |
-
|
| 99 |
-
gr.Markdown("### 📥 SEÑALES")
|
| 100 |
-
in_t = gr.Audio(label="TU MEZCLA", type="filepath")
|
| 101 |
in_r = gr.Audio(label="REFERENCIA", type="filepath")
|
| 102 |
-
|
| 103 |
-
gr.Markdown("### ⚙️ PROCESADORES")
|
| 104 |
with gr.Row():
|
| 105 |
-
fix_check = gr.Checkbox(label="
|
| 106 |
-
|
| 107 |
-
in_p = gr.Slider(0, 100, label="
|
| 108 |
-
btn = gr.Button("🚀
|
| 109 |
|
| 110 |
-
|
| 111 |
-
|
| 112 |
-
gr.Markdown("### 🔊 MONITORIZACIÓN")
|
| 113 |
-
out_a = gr.Audio(label="SEÑAL DE SALIDA")
|
| 114 |
with gr.Row():
|
| 115 |
-
out_i = gr.Textbox(label="
|
| 116 |
-
out_c = gr.Textbox(label="
|
| 117 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 118 |
|
| 119 |
-
btn.click(
|
|
|
|
| 120 |
|
| 121 |
demo.launch()
|
|
|
|
| 4 |
from pydub import AudioSegment
|
| 5 |
import os
|
| 6 |
|
| 7 |
+
# Memoria temporal para guardar el último análisis
|
| 8 |
+
session_data = {"key": "--", "bpm": "0", "issues": []}
|
| 9 |
|
| 10 |
+
def ghost_analyser_v22(t, r, p, active_fix, active_master):
|
| 11 |
+
global session_data
|
| 12 |
if t is None:
|
| 13 |
+
return None, "SISTEMA: ESPERANDO AUDIO...", "SIN DATA", "--", "0", None
|
| 14 |
|
| 15 |
try:
|
|
|
|
| 16 |
y, sr = librosa.load(t, duration=30)
|
| 17 |
|
| 18 |
+
# KEY & BPM (Blindado)
|
| 19 |
chroma = librosa.feature.chroma_cqt(y=y, sr=sr)
|
| 20 |
nt = ["C","C#","D","D#","E","F","F#","G","G#","A","A#","B"]
|
| 21 |
+
k = nt[np.argmax(np.mean(chroma, axis=1))]
|
|
|
|
|
|
|
| 22 |
tempo, _ = librosa.beat.beat_track(y=y, sr=sr)
|
| 23 |
+
bpm = str(int(np.atleast_1d(tempo)[0]))
|
| 24 |
+
|
| 25 |
+
session_data["key"] = k
|
| 26 |
+
session_data["bpm"] = bpm
|
| 27 |
+
session_data["issues"] = []
|
| 28 |
|
| 29 |
# Análisis de Espectro
|
| 30 |
S = np.abs(librosa.stft(y))
|
| 31 |
f = librosa.fft_frequencies(sr=sr)
|
| 32 |
+
lo = np.mean(S[f < 150])
|
| 33 |
+
mi = np.mean(S[(f >= 150) & (f < 2000)])
|
| 34 |
+
hi = np.mean(S[f >= 2000])
|
| 35 |
|
| 36 |
audio = AudioSegment.from_file(t)
|
| 37 |
+
|
| 38 |
+
# Lógica de Reparación Profunda
|
|
|
|
| 39 |
if active_fix:
|
| 40 |
audio = audio.high_pass_filter(32)
|
| 41 |
+
if lo > mi * 1.5:
|
| 42 |
+
audio = audio.low_pass_filter(4000)
|
| 43 |
+
session_data["issues"].append("Exceso de lodo (muddy) en 200-400Hz.")
|
| 44 |
+
if hi < mi * 0.4:
|
| 45 |
+
session_data["issues"].append("Falta de aire y brillo por encima de 10kHz.")
|
| 46 |
+
|
|
|
|
| 47 |
if active_master:
|
| 48 |
+
audio = audio.compressor_dynamic_range(threshold=-16.0, ratio=4.0, attack=10.0, release=150.0)
|
|
|
|
| 49 |
audio = audio.apply_gain((p / 10) - 3)
|
|
|
|
|
|
|
|
|
|
| 50 |
|
| 51 |
+
audio.export("ghost_mentor_v22.wav", format="wav")
|
| 52 |
|
| 53 |
+
diag = f"--- SESIÓN INICIADA: {k} ---\n\n"
|
| 54 |
+
diag += "DETECCIONES CRÍTICAS:\n"
|
| 55 |
+
for iss in session_data["issues"]:
|
| 56 |
+
diag += f"• {iss}\n"
|
| 57 |
+
diag += "\n[!] Ahora puedes preguntarme detalles sobre estos fallos abajo."
|
| 58 |
+
|
| 59 |
+
intel = "--- ESTUDIO DE MERCADO ---\n\n"
|
| 60 |
if r is not None:
|
| 61 |
yr, _ = librosa.load(r, duration=30)
|
| 62 |
diff = 20 * np.log10(np.sqrt(np.mean(yr**2)) / (np.sqrt(np.mean(y**2)) + 1e-6))
|
| 63 |
+
intel += f"• GAP DE ENERGÍA: {round(diff, 1)} dB\n"
|
| 64 |
+
intel += "• CONCLUSIÓN: Tu track necesita más pegada en el transiente del Snare."
|
| 65 |
else:
|
| 66 |
+
intel += "Sin referencia para comparar."
|
| 67 |
|
| 68 |
+
return "ghost_mentor_v22.wav", diag, intel, k, bpm, "https://images.unsplash.com/photo-1550684848-fac1c5b4e853?q=80&w=500"
|
| 69 |
|
| 70 |
except Exception as e:
|
| 71 |
+
return None, f"ERROR: {str(e)}", "!", "!", "0", None
|
| 72 |
+
|
| 73 |
+
def chatbot_response(pregunta):
|
| 74 |
+
k = session_data["key"]
|
| 75 |
+
if k == "--":
|
| 76 |
+
return "Primero analiza un track para que pueda aconsejarte sobre él."
|
| 77 |
+
|
| 78 |
+
pregunta = pregunta.lower()
|
| 79 |
+
if "lodo" in pregunta or "bajo" in pregunta:
|
| 80 |
+
return f"Para limpiar el bajo en {k}, haz un corte tipo campana de -3dB en 250Hz. Asegúrate de que el Kick tenga su pico en {librosa.note_to_hz(k+'1'):.1f}Hz."
|
| 81 |
+
elif "brillo" in pregunta or "agudo" in pregunta:
|
| 82 |
+
return "Usa un 'High Shelf' a partir de 8kHz subiendo 2dB. Si usas saturación (Decapitator), pon el Mix al 10% en modo 'A' para añadir armónicos."
|
| 83 |
+
elif "compresion" in pregunta or "vst" in pregunta:
|
| 84 |
+
return "Para DnB, usa un ratio de 4:1 en el bus de batería. Attack lento (25ms) para que el golpe del drum pase, y Release rápido (100ms) para que respire."
|
| 85 |
+
else:
|
| 86 |
+
return "Entendido. Basado en tu tema, te recomiendo revisar la fase de los sub-graves y usar un limitador con 'Lookahead' de 1ms para evitar picos inter-sample."
|
| 87 |
|
| 88 |
+
# --- INTERFAZ HUD INTERACTIVA ---
|
| 89 |
css = """
|
| 90 |
+
body, .gradio-container { background-color: #0a0a0a !important; color: #00ffcc !important; font-family: 'Courier New', monospace; }
|
| 91 |
+
.gr-box { border: 2px solid #00ffcc !important; background: #111 !important; }
|
| 92 |
+
.gr-button-primary { background: #00ffcc !important; color: #000 !important; font-weight: bold; border: none; }
|
| 93 |
+
.gr-textbox textarea { background: #000 !important; color: #00ffcc !important; border: 1px solid #00ffcc44 !important; }
|
| 94 |
+
#chat-box { border-top: 2px dashed #00ffcc; padding-top: 15px; margin-top: 15px; }
|
|
|
|
|
|
|
| 95 |
"""
|
| 96 |
|
| 97 |
with gr.Blocks(theme=gr.themes.Monochrome(), css=css) as demo:
|
| 98 |
+
gr.Markdown("<h1 style='text-align:center;'>💀 GHOST MENTOR v22</h1>")
|
|
|
|
|
|
|
| 99 |
|
| 100 |
with gr.Row():
|
| 101 |
+
with gr.Column(scale=1):
|
|
|
|
| 102 |
with gr.Row():
|
| 103 |
+
out_k = gr.Label(label="KEY")
|
| 104 |
+
out_b = gr.Label(label="BPM")
|
| 105 |
+
in_t = gr.Audio(label="TU TRACK", type="filepath")
|
|
|
|
|
|
|
| 106 |
in_r = gr.Audio(label="REFERENCIA", type="filepath")
|
|
|
|
|
|
|
| 107 |
with gr.Row():
|
| 108 |
+
fix_check = gr.Checkbox(label="AUTO-FIX", value=False)
|
| 109 |
+
mst_check = gr.Checkbox(label="MASTER", value=False)
|
| 110 |
+
in_p = gr.Slider(0, 100, label="POWER", value=80)
|
| 111 |
+
btn = gr.Button("🚀 ANALIZAR Y REPARAR", variant="primary")
|
| 112 |
|
| 113 |
+
with gr.Column(scale=2):
|
| 114 |
+
out_a = gr.Audio(label="MONITOR SALIDA")
|
|
|
|
|
|
|
| 115 |
with gr.Row():
|
| 116 |
+
out_i = gr.Textbox(label="DIAGNÓSTICO", lines=8)
|
| 117 |
+
out_c = gr.Textbox(label="ESTUDIO DE REF", lines=8)
|
| 118 |
+
|
| 119 |
+
# --- SECCIÓN DE CHAT ASESOR ---
|
| 120 |
+
with gr.Group(elem_id="chat-box"):
|
| 121 |
+
gr.Markdown("### 💬 PREGUNTA AL INGENIERO GHOST")
|
| 122 |
+
user_msg = gr.Textbox(placeholder="¿Cómo puedo mejorar los agudos de este tema?", label="Tu consulta")
|
| 123 |
+
chat_btn = gr.Button("CONSULTAR ASESOR", variant="secondary")
|
| 124 |
+
chat_out = gr.Textbox(label="RESPUESTA DEL MENTOR")
|
| 125 |
|
| 126 |
+
btn.click(ghost_analyser_v22, [in_t, in_r, in_p, fix_check, mst_check], [out_a, out_i, out_c, out_k, out_b])
|
| 127 |
+
chat_btn.click(chatbot_response, [user_msg], [chat_out])
|
| 128 |
|
| 129 |
demo.launch()
|