Spaces:
Sleeping
Sleeping
v6: Update app defaults for real music — delta=0.12, energy=-35, min_gap=0.03, NCC compare=0 (auto)
Browse files
app.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
| 1 |
"""
|
| 2 |
-
Gradio UI — Sample Extractor
|
| 3 |
-
NCC clustering
|
| 4 |
"""
|
| 5 |
|
| 6 |
import gradio as gr
|
|
@@ -105,7 +105,6 @@ def run_extraction(audio_in, stem_choice, demucs_model, demucs_shifts, demucs_ov
|
|
| 105 |
zip_path = build_archive(clusters, bpm, stem_sr,
|
| 106 |
midi_path=midi_path, rendered_audio=rendered)
|
| 107 |
|
| 108 |
-
# Metrics
|
| 109 |
rows = []
|
| 110 |
for c in sorted(clusters, key=lambda x: x.count, reverse=True):
|
| 111 |
best = c.best_hit
|
|
@@ -120,7 +119,11 @@ def run_extraction(audio_in, stem_choice, demucs_model, demucs_shifts, demucs_ov
|
|
| 120 |
})
|
| 121 |
|
| 122 |
summary = f"**Detected BPM: {bpm}** · **{len(clusters)} unique samples** from {len(hits)} hits\n\n"
|
| 123 |
-
summary += f"Model: `{demucs_model}` ·
|
|
|
|
|
|
|
|
|
|
|
|
|
| 124 |
summary += "| Sample | Hits | MIDI Note |\n|---|---|---|\n"
|
| 125 |
for c in sorted(clusters, key=lambda x: x.count, reverse=True):
|
| 126 |
summary += f"| {c.label} | {c.count} | {c.midi_note} |\n"
|
|
@@ -135,11 +138,10 @@ def run_extraction(audio_in, stem_choice, demucs_model, demucs_shifts, demucs_ov
|
|
| 135 |
|
| 136 |
# ─── Tab 2: Evaluate ─────────────────────────────────────────────────────────
|
| 137 |
|
| 138 |
-
def run_eval(pattern, bpm, bars, ncc_threshold, progress=gr.Progress()):
|
| 139 |
progress(0.0, desc="Generating synthetic song...")
|
| 140 |
song = generate_test_song(pattern_name=pattern, bars=int(bars),
|
| 141 |
bpm=float(bpm), variation='medium', seed=42)
|
| 142 |
-
|
| 143 |
detected_bpm = detect_bpm(song.drums_only, song.sr)
|
| 144 |
|
| 145 |
progress(0.2, desc="Extracting...")
|
|
@@ -147,7 +149,8 @@ def run_eval(pattern, bpm, bars, ncc_threshold, progress=gr.Progress()):
|
|
| 147 |
if not hits: return None, None, None, None, "", ""
|
| 148 |
|
| 149 |
hits = classify_hits(hits)
|
| 150 |
-
clusters = cluster_hits(hits, ncc_threshold=float(ncc_threshold)
|
|
|
|
| 151 |
select_best(clusters)
|
| 152 |
for c in clusters:
|
| 153 |
if c.count >= 2: c.synthesized = synthesize_from_cluster(c)
|
|
@@ -161,9 +164,6 @@ def run_eval(pattern, bpm, bars, ncc_threshold, progress=gr.Progress()):
|
|
| 161 |
for h in song.hits]
|
| 162 |
report = evaluate_extraction(clusters, gt, gt_hits, song.sr, hits)
|
| 163 |
|
| 164 |
-
mix_out = audio_tuple(song.mix, song.sr)
|
| 165 |
-
rendered_out = audio_tuple(rendered, song.sr)
|
| 166 |
-
|
| 167 |
summary = [
|
| 168 |
{'Metric': 'Detected BPM', 'Value': f"{detected_bpm}", 'Target': f"{song.bpm}"},
|
| 169 |
{'Metric': 'Clusters', 'Value': str(len(clusters)), 'Target': str(len(gt))},
|
|
@@ -173,59 +173,47 @@ def run_eval(pattern, bpm, bars, ncc_threshold, progress=gr.Progress()):
|
|
| 173 |
]
|
| 174 |
if report.unmatched_gt:
|
| 175 |
summary.append({'Metric': '⚠ Unmatched', 'Value': ', '.join(report.unmatched_gt), 'Target': 'None'})
|
| 176 |
-
|
| 177 |
matches = [{'Cluster': m.cluster_label, 'GT': m.gt_name, 'SI-SDR': f"{m.si_sdr:.1f}",
|
| 178 |
'Score': f"{m.sample_score:.1f}"} for m in report.matches]
|
| 179 |
|
| 180 |
progress(1.0)
|
| 181 |
-
return (
|
| 182 |
-
pd.DataFrame(matches) if matches else None, "", "")
|
| 183 |
|
| 184 |
|
| 185 |
-
# ─── Tab 3: Optimize ───────────────────────────────────────
|
| 186 |
|
| 187 |
def run_optimize(n_iters, config_name, author, save_hub, progress=gr.Progress()):
|
| 188 |
logs = []
|
| 189 |
-
progress(0.0
|
| 190 |
-
state = run_optimization(n_iterations=int(n_iters),
|
| 191 |
-
|
| 192 |
-
|
| 193 |
-
save_to_hub=bool(save_hub), log_fn=lambda m: logs.append(m))
|
| 194 |
progress(1.0)
|
| 195 |
-
hist = [{'Iter': r.iteration, 'Score': f"{r.avg_score:.1f}",
|
| 196 |
-
|
| 197 |
if state.history:
|
| 198 |
-
fig, ax = plt.subplots(figsize=(10,
|
| 199 |
ax.plot([r.iteration for r in state.history], [r.avg_score for r in state.history], 'b-o')
|
| 200 |
ax.set_xlabel('Iteration'); ax.set_ylabel('Score'); ax.grid(True, alpha=0.3); plt.tight_layout()
|
| 201 |
-
else:
|
| 202 |
-
fig, ax = plt.subplots(); ax.text(0.5, 0.5, "No data")
|
| 203 |
return '\n'.join(logs), pd.DataFrame(hist), fig, json.dumps(state.best_config, indent=2)
|
| 204 |
|
| 205 |
-
|
| 206 |
-
# ─── Tab 4: Leaderboard ──────────────────────────────────────────────────────
|
| 207 |
-
|
| 208 |
def refresh_leaderboard():
|
| 209 |
try:
|
| 210 |
lb = get_leaderboard()
|
| 211 |
return pd.DataFrame(lb) if lb else pd.DataFrame(), ""
|
| 212 |
-
except Exception as e:
|
| 213 |
-
return pd.DataFrame(), str(e)
|
| 214 |
|
| 215 |
|
| 216 |
# ─── Build App ────────────────────────────────────────────────────────────────
|
| 217 |
|
| 218 |
-
def get_stems_for_model(model_name):
|
| 219 |
-
stems = DEMUCS_STEMS.get(model_name, ["drums", "bass", "other", "vocals"])
|
| 220 |
-
return gr.update(choices=stems + ["all"], value=stems[0])
|
| 221 |
-
|
| 222 |
def build_app():
|
| 223 |
with gr.Blocks(title="🎵 Sample Extractor", theme=gr.themes.Soft(),
|
| 224 |
css=".gradio-container{max-width:1300px!important}") as app:
|
| 225 |
-
gr.Markdown("# 🎵 Sample Extractor
|
| 226 |
-
"Extract distinct sounds from audio using **NCC waveform matching**
|
| 227 |
-
"
|
| 228 |
-
"Full control over Demucs model, onset detection, and clustering parameters.")
|
| 229 |
|
| 230 |
with gr.Tabs():
|
| 231 |
# ── Extract ──
|
|
@@ -234,51 +222,36 @@ def build_app():
|
|
| 234 |
|
| 235 |
with gr.Accordion("🔧 Stem Separation", open=False):
|
| 236 |
with gr.Row():
|
| 237 |
-
demucs_model = gr.Dropdown(DEMUCS_MODELS, value="htdemucs_ft",
|
| 238 |
-
|
| 239 |
-
|
| 240 |
-
|
| 241 |
-
demucs_shifts = gr.Slider(0, 5, value=1, step=1,
|
| 242 |
-
label='Shifts (TTA, 0=fastest)')
|
| 243 |
-
demucs_overlap = gr.Slider(0.0, 0.5, value=0.25, step=0.05,
|
| 244 |
-
label='Overlap')
|
| 245 |
|
| 246 |
with gr.Accordion("🎯 Onset Detection", open=False):
|
| 247 |
with gr.Row():
|
| 248 |
-
onset_mode = gr.Dropdown(['auto','percussive','harmonic','broadband'],
|
| 249 |
-
|
| 250 |
-
|
| 251 |
-
label='Delta (sensitivity)')
|
| 252 |
-
energy_db = gr.Slider(-70, -10, value=-45, step=1,
|
| 253 |
-
label='Energy threshold (dB)')
|
| 254 |
with gr.Row():
|
| 255 |
-
pre_pad = gr.Slider(0.0, 0.05, value=0.005, step=0.001,
|
| 256 |
-
|
| 257 |
-
|
| 258 |
-
|
| 259 |
-
|
| 260 |
-
|
| 261 |
-
|
| 262 |
-
label='Min gap (s)')
|
| 263 |
-
|
| 264 |
-
with gr.Accordion("🔗 Clustering", open=False):
|
| 265 |
with gr.Row():
|
| 266 |
-
|
| 267 |
-
|
| 268 |
-
|
| 269 |
-
label='Compare window (ms)')
|
| 270 |
-
linkage_dd = gr.Dropdown(['average', 'complete', 'single'],
|
| 271 |
-
value='average', label='Linkage')
|
| 272 |
with gr.Row():
|
| 273 |
-
|
| 274 |
-
|
| 275 |
-
|
| 276 |
-
|
| 277 |
-
gr.Markdown("*Set both target min/max > 0 to auto-search for the right threshold. "
|
| 278 |
-
"Leave at 0 to use the NCC threshold directly.*")
|
| 279 |
|
| 280 |
with gr.Accordion("⚙️ Post-processing", open=False):
|
| 281 |
-
do_synth = gr.Checkbox(value=True, label='Synthesize optimal samples
|
| 282 |
|
| 283 |
extract_btn = gr.Button("🔬 Extract Samples", variant="primary", size="lg")
|
| 284 |
|
|
@@ -286,23 +259,18 @@ def build_app():
|
|
| 286 |
with gr.Row():
|
| 287 |
stem_out = gr.Audio(type='numpy', label='Stem', interactive=False)
|
| 288 |
rendered_out = gr.Audio(type='numpy', label='🔊 Reconstruction', interactive=False)
|
| 289 |
-
|
| 290 |
gr.Markdown("### Downloads")
|
| 291 |
with gr.Row():
|
| 292 |
archive_file = gr.File(label="📦 ZIP Archive", interactive=False)
|
| 293 |
midi_file = gr.File(label="🎹 MIDI", interactive=False)
|
| 294 |
-
sample_files = gr.File(label="Individual WAV samples", file_count="multiple",
|
| 295 |
-
interactive=False)
|
| 296 |
metrics_tbl = gr.Dataframe(label="Extracted Samples")
|
| 297 |
status_txt = gr.Textbox(visible=False)
|
| 298 |
|
| 299 |
-
# Update available stems when model changes
|
| 300 |
demucs_model.change(
|
| 301 |
-
fn=lambda m: gr.update(choices=DEMUCS_STEMS.get(m,
|
| 302 |
inputs=[demucs_model], outputs=[stem_dd])
|
| 303 |
-
|
| 304 |
-
extract_btn.click(
|
| 305 |
-
run_extraction,
|
| 306 |
[audio_in, stem_dd, demucs_model, demucs_shifts, demucs_overlap,
|
| 307 |
onset_mode, onset_delta, energy_db, pre_pad, min_dur, max_dur, min_gap,
|
| 308 |
ncc_thresh, ncc_ms, linkage_dd, target_min, target_max, do_synth],
|
|
@@ -316,7 +284,10 @@ def build_app():
|
|
| 316 |
ev_pat = gr.Dropdown(['rock','funk','halftime'], value='rock', label='Pattern')
|
| 317 |
ev_bpm = gr.Slider(80, 200, value=120, step=2, label='BPM')
|
| 318 |
ev_bars = gr.Slider(2, 8, value=4, step=1, label='Bars')
|
| 319 |
-
|
|
|
|
|
|
|
|
|
|
| 320 |
ev_btn = gr.Button("🧪 Evaluate", variant="primary", size="lg")
|
| 321 |
with gr.Row():
|
| 322 |
ev_mix = gr.Audio(type='numpy', label='Original', interactive=False)
|
|
@@ -324,31 +295,31 @@ def build_app():
|
|
| 324 |
ev_summary = gr.Dataframe(label="Summary")
|
| 325 |
ev_matches = gr.Dataframe(label="Matches")
|
| 326 |
ev_s1 = gr.Textbox(visible=False); ev_s2 = gr.Textbox(visible=False)
|
| 327 |
-
ev_btn.click(run_eval, [ev_pat, ev_bpm, ev_bars, ev_ncc],
|
| 328 |
[ev_mix, ev_rendered, ev_summary, ev_matches, ev_s1, ev_s2])
|
| 329 |
|
| 330 |
# ── Optimize ──
|
| 331 |
with gr.Tab("🔄 Optimize"):
|
| 332 |
-
gr.Markdown("### Autonomous Optimization\nTests across 6 diverse songs
|
| 333 |
with gr.Row():
|
| 334 |
-
opt_n = gr.Slider(2,
|
| 335 |
-
opt_name = gr.Textbox(value="optimized",
|
| 336 |
-
opt_author = gr.Textbox(value="",
|
| 337 |
-
opt_save = gr.Checkbox(value=True,
|
| 338 |
opt_btn = gr.Button("🚀 Optimize", variant="primary", size="lg")
|
| 339 |
-
opt_log = gr.Textbox(label="Log",
|
| 340 |
opt_hist = gr.Dataframe(label="History")
|
| 341 |
opt_plot = gr.Plot(label="Progress")
|
| 342 |
-
opt_params = gr.Code(label="Best Config",
|
| 343 |
-
opt_btn.click(run_optimize,
|
| 344 |
-
[opt_log,
|
| 345 |
|
| 346 |
# ── Leaderboard ──
|
| 347 |
with gr.Tab("🏆 Leaderboard"):
|
| 348 |
gr.Markdown("### Config Leaderboard")
|
| 349 |
lb_btn = gr.Button("🔄 Refresh"); lb_tbl = gr.Dataframe()
|
| 350 |
lb_s = gr.Textbox(visible=False)
|
| 351 |
-
lb_btn.click(refresh_leaderboard,
|
| 352 |
|
| 353 |
return app
|
| 354 |
|
|
|
|
| 1 |
"""
|
| 2 |
+
Gradio UI — Sample Extractor v6.
|
| 3 |
+
NCC clustering with target range, auto-scale compare window, better defaults.
|
| 4 |
"""
|
| 5 |
|
| 6 |
import gradio as gr
|
|
|
|
| 105 |
zip_path = build_archive(clusters, bpm, stem_sr,
|
| 106 |
midi_path=midi_path, rendered_audio=rendered)
|
| 107 |
|
|
|
|
| 108 |
rows = []
|
| 109 |
for c in sorted(clusters, key=lambda x: x.count, reverse=True):
|
| 110 |
best = c.best_hit
|
|
|
|
| 119 |
})
|
| 120 |
|
| 121 |
summary = f"**Detected BPM: {bpm}** · **{len(clusters)} unique samples** from {len(hits)} hits\n\n"
|
| 122 |
+
summary += f"Model: `{demucs_model}` · Onset delta: `{onset_delta}` · Energy: `{energy_db}dB`\n\n"
|
| 123 |
+
if int(target_min) > 0 and int(target_max) > 0:
|
| 124 |
+
summary += f"Target clusters: `{int(target_min)}–{int(target_max)}`\n\n"
|
| 125 |
+
else:
|
| 126 |
+
summary += f"NCC threshold: `{ncc_threshold}`\n\n"
|
| 127 |
summary += "| Sample | Hits | MIDI Note |\n|---|---|---|\n"
|
| 128 |
for c in sorted(clusters, key=lambda x: x.count, reverse=True):
|
| 129 |
summary += f"| {c.label} | {c.count} | {c.midi_note} |\n"
|
|
|
|
| 138 |
|
| 139 |
# ─── Tab 2: Evaluate ─────────────────────────────────────────────────────────
|
| 140 |
|
| 141 |
+
def run_eval(pattern, bpm, bars, ncc_threshold, target_min, target_max, progress=gr.Progress()):
|
| 142 |
progress(0.0, desc="Generating synthetic song...")
|
| 143 |
song = generate_test_song(pattern_name=pattern, bars=int(bars),
|
| 144 |
bpm=float(bpm), variation='medium', seed=42)
|
|
|
|
| 145 |
detected_bpm = detect_bpm(song.drums_only, song.sr)
|
| 146 |
|
| 147 |
progress(0.2, desc="Extracting...")
|
|
|
|
| 149 |
if not hits: return None, None, None, None, "", ""
|
| 150 |
|
| 151 |
hits = classify_hits(hits)
|
| 152 |
+
clusters = cluster_hits(hits, ncc_threshold=float(ncc_threshold),
|
| 153 |
+
target_min=int(target_min), target_max=int(target_max))
|
| 154 |
select_best(clusters)
|
| 155 |
for c in clusters:
|
| 156 |
if c.count >= 2: c.synthesized = synthesize_from_cluster(c)
|
|
|
|
| 164 |
for h in song.hits]
|
| 165 |
report = evaluate_extraction(clusters, gt, gt_hits, song.sr, hits)
|
| 166 |
|
|
|
|
|
|
|
|
|
|
| 167 |
summary = [
|
| 168 |
{'Metric': 'Detected BPM', 'Value': f"{detected_bpm}", 'Target': f"{song.bpm}"},
|
| 169 |
{'Metric': 'Clusters', 'Value': str(len(clusters)), 'Target': str(len(gt))},
|
|
|
|
| 173 |
]
|
| 174 |
if report.unmatched_gt:
|
| 175 |
summary.append({'Metric': '⚠ Unmatched', 'Value': ', '.join(report.unmatched_gt), 'Target': 'None'})
|
|
|
|
| 176 |
matches = [{'Cluster': m.cluster_label, 'GT': m.gt_name, 'SI-SDR': f"{m.si_sdr:.1f}",
|
| 177 |
'Score': f"{m.sample_score:.1f}"} for m in report.matches]
|
| 178 |
|
| 179 |
progress(1.0)
|
| 180 |
+
return (audio_tuple(song.mix, song.sr), audio_tuple(rendered, song.sr),
|
| 181 |
+
pd.DataFrame(summary), pd.DataFrame(matches) if matches else None, "", "")
|
| 182 |
|
| 183 |
|
| 184 |
+
# ─── Tab 3 & 4: Optimize + Leaderboard ───────────────────────────────────────
|
| 185 |
|
| 186 |
def run_optimize(n_iters, config_name, author, save_hub, progress=gr.Progress()):
|
| 187 |
logs = []
|
| 188 |
+
progress(0.0)
|
| 189 |
+
state = run_optimization(n_iterations=int(n_iters), config_name=config_name or "optimized",
|
| 190 |
+
author=author or "anon", save_to_hub=bool(save_hub),
|
| 191 |
+
log_fn=lambda m: logs.append(m))
|
|
|
|
| 192 |
progress(1.0)
|
| 193 |
+
hist = [{'Iter': r.iteration, 'Score': f"{r.avg_score:.1f}", 'Time': f"{r.duration_s:.1f}s"}
|
| 194 |
+
for r in state.history]
|
| 195 |
if state.history:
|
| 196 |
+
fig, ax = plt.subplots(figsize=(10,4))
|
| 197 |
ax.plot([r.iteration for r in state.history], [r.avg_score for r in state.history], 'b-o')
|
| 198 |
ax.set_xlabel('Iteration'); ax.set_ylabel('Score'); ax.grid(True, alpha=0.3); plt.tight_layout()
|
| 199 |
+
else: fig, ax = plt.subplots(); ax.text(0.5,0.5,"No data")
|
|
|
|
| 200 |
return '\n'.join(logs), pd.DataFrame(hist), fig, json.dumps(state.best_config, indent=2)
|
| 201 |
|
|
|
|
|
|
|
|
|
|
| 202 |
def refresh_leaderboard():
|
| 203 |
try:
|
| 204 |
lb = get_leaderboard()
|
| 205 |
return pd.DataFrame(lb) if lb else pd.DataFrame(), ""
|
| 206 |
+
except Exception as e: return pd.DataFrame(), str(e)
|
|
|
|
| 207 |
|
| 208 |
|
| 209 |
# ─── Build App ────────────────────────────────────────────────────────────────
|
| 210 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 211 |
def build_app():
|
| 212 |
with gr.Blocks(title="🎵 Sample Extractor", theme=gr.themes.Soft(),
|
| 213 |
css=".gradio-container{max-width:1300px!important}") as app:
|
| 214 |
+
gr.Markdown("# 🎵 Sample Extractor v6\n"
|
| 215 |
+
"Extract distinct sounds from audio using **NCC waveform matching**. "
|
| 216 |
+
"Set a **target cluster range** to control how many unique samples to extract.")
|
|
|
|
| 217 |
|
| 218 |
with gr.Tabs():
|
| 219 |
# ── Extract ──
|
|
|
|
| 222 |
|
| 223 |
with gr.Accordion("🔧 Stem Separation", open=False):
|
| 224 |
with gr.Row():
|
| 225 |
+
demucs_model = gr.Dropdown(DEMUCS_MODELS, value="htdemucs_ft", label="Demucs Model")
|
| 226 |
+
stem_dd = gr.Dropdown(['drums','bass','other','vocals','all'], value='drums', label='Stem')
|
| 227 |
+
demucs_shifts = gr.Slider(0, 5, value=1, step=1, label='Shifts (0=fastest)')
|
| 228 |
+
demucs_overlap = gr.Slider(0.0, 0.5, value=0.25, step=0.05, label='Overlap')
|
|
|
|
|
|
|
|
|
|
|
|
|
| 229 |
|
| 230 |
with gr.Accordion("🎯 Onset Detection", open=False):
|
| 231 |
with gr.Row():
|
| 232 |
+
onset_mode = gr.Dropdown(['auto','percussive','harmonic','broadband'], value='auto', label='Mode')
|
| 233 |
+
onset_delta = gr.Slider(0.01, 0.5, value=0.12, step=0.01, label='Delta (lower=more onsets)')
|
| 234 |
+
energy_db = gr.Slider(-70, -10, value=-35, step=1, label='Energy threshold (dB)')
|
|
|
|
|
|
|
|
|
|
| 235 |
with gr.Row():
|
| 236 |
+
pre_pad = gr.Slider(0.0, 0.05, value=0.005, step=0.001, label='Pre-pad (s)')
|
| 237 |
+
min_dur = gr.Slider(0.005, 0.2, value=0.02, step=0.005, label='Min duration (s)')
|
| 238 |
+
max_dur = gr.Slider(0.1, 5.0, value=1.5, step=0.1, label='Max duration (s)')
|
| 239 |
+
min_gap = gr.Slider(0.005, 0.2, value=0.03, step=0.005, label='Min gap (s)')
|
| 240 |
+
|
| 241 |
+
with gr.Accordion("🔗 Clustering", open=True):
|
| 242 |
+
gr.Markdown("**Target cluster range** — set both > 0 to auto-find the right threshold:")
|
|
|
|
|
|
|
|
|
|
| 243 |
with gr.Row():
|
| 244 |
+
target_min = gr.Number(value=5, label='Target min clusters', precision=0)
|
| 245 |
+
target_max = gr.Number(value=20, label='Target max clusters', precision=0)
|
| 246 |
+
gr.Markdown("Or set both to 0 and use manual threshold:")
|
|
|
|
|
|
|
|
|
|
| 247 |
with gr.Row():
|
| 248 |
+
ncc_thresh = gr.Slider(0.3, 0.99, value=0.80, step=0.01, label='NCC threshold')
|
| 249 |
+
ncc_ms = gr.Slider(0, 1000, value=0, step=50,
|
| 250 |
+
label='Compare window ms (0=auto)')
|
| 251 |
+
linkage_dd = gr.Dropdown(['average','complete','single'], value='average', label='Linkage')
|
|
|
|
|
|
|
| 252 |
|
| 253 |
with gr.Accordion("⚙️ Post-processing", open=False):
|
| 254 |
+
do_synth = gr.Checkbox(value=True, label='Synthesize optimal samples')
|
| 255 |
|
| 256 |
extract_btn = gr.Button("🔬 Extract Samples", variant="primary", size="lg")
|
| 257 |
|
|
|
|
| 259 |
with gr.Row():
|
| 260 |
stem_out = gr.Audio(type='numpy', label='Stem', interactive=False)
|
| 261 |
rendered_out = gr.Audio(type='numpy', label='🔊 Reconstruction', interactive=False)
|
|
|
|
| 262 |
gr.Markdown("### Downloads")
|
| 263 |
with gr.Row():
|
| 264 |
archive_file = gr.File(label="📦 ZIP Archive", interactive=False)
|
| 265 |
midi_file = gr.File(label="🎹 MIDI", interactive=False)
|
| 266 |
+
sample_files = gr.File(label="Individual WAV samples", file_count="multiple", interactive=False)
|
|
|
|
| 267 |
metrics_tbl = gr.Dataframe(label="Extracted Samples")
|
| 268 |
status_txt = gr.Textbox(visible=False)
|
| 269 |
|
|
|
|
| 270 |
demucs_model.change(
|
| 271 |
+
fn=lambda m: gr.update(choices=DEMUCS_STEMS.get(m,["drums","bass","other","vocals"])+["all"]),
|
| 272 |
inputs=[demucs_model], outputs=[stem_dd])
|
| 273 |
+
extract_btn.click(run_extraction,
|
|
|
|
|
|
|
| 274 |
[audio_in, stem_dd, demucs_model, demucs_shifts, demucs_overlap,
|
| 275 |
onset_mode, onset_delta, energy_db, pre_pad, min_dur, max_dur, min_gap,
|
| 276 |
ncc_thresh, ncc_ms, linkage_dd, target_min, target_max, do_synth],
|
|
|
|
| 284 |
ev_pat = gr.Dropdown(['rock','funk','halftime'], value='rock', label='Pattern')
|
| 285 |
ev_bpm = gr.Slider(80, 200, value=120, step=2, label='BPM')
|
| 286 |
ev_bars = gr.Slider(2, 8, value=4, step=1, label='Bars')
|
| 287 |
+
with gr.Row():
|
| 288 |
+
ev_ncc = gr.Slider(0.3, 0.99, value=0.80, step=0.01, label='NCC threshold')
|
| 289 |
+
ev_tmin = gr.Number(value=0, label='Target min', precision=0)
|
| 290 |
+
ev_tmax = gr.Number(value=0, label='Target max', precision=0)
|
| 291 |
ev_btn = gr.Button("🧪 Evaluate", variant="primary", size="lg")
|
| 292 |
with gr.Row():
|
| 293 |
ev_mix = gr.Audio(type='numpy', label='Original', interactive=False)
|
|
|
|
| 295 |
ev_summary = gr.Dataframe(label="Summary")
|
| 296 |
ev_matches = gr.Dataframe(label="Matches")
|
| 297 |
ev_s1 = gr.Textbox(visible=False); ev_s2 = gr.Textbox(visible=False)
|
| 298 |
+
ev_btn.click(run_eval, [ev_pat, ev_bpm, ev_bars, ev_ncc, ev_tmin, ev_tmax],
|
| 299 |
[ev_mix, ev_rendered, ev_summary, ev_matches, ev_s1, ev_s2])
|
| 300 |
|
| 301 |
# ── Optimize ──
|
| 302 |
with gr.Tab("🔄 Optimize"):
|
| 303 |
+
gr.Markdown("### Autonomous Optimization\nTests across 6 diverse songs.")
|
| 304 |
with gr.Row():
|
| 305 |
+
opt_n = gr.Slider(2,30,value=5,step=1,label='Iterations')
|
| 306 |
+
opt_name = gr.Textbox(value="optimized",label='Config name')
|
| 307 |
+
opt_author = gr.Textbox(value="",label='Author')
|
| 308 |
+
opt_save = gr.Checkbox(value=True,label='Save to Hub')
|
| 309 |
opt_btn = gr.Button("🚀 Optimize", variant="primary", size="lg")
|
| 310 |
+
opt_log = gr.Textbox(label="Log",lines=20,max_lines=40)
|
| 311 |
opt_hist = gr.Dataframe(label="History")
|
| 312 |
opt_plot = gr.Plot(label="Progress")
|
| 313 |
+
opt_params = gr.Code(label="Best Config",language="json")
|
| 314 |
+
opt_btn.click(run_optimize,[opt_n,opt_name,opt_author,opt_save],
|
| 315 |
+
[opt_log,opt_hist,opt_plot,opt_params])
|
| 316 |
|
| 317 |
# ── Leaderboard ──
|
| 318 |
with gr.Tab("🏆 Leaderboard"):
|
| 319 |
gr.Markdown("### Config Leaderboard")
|
| 320 |
lb_btn = gr.Button("🔄 Refresh"); lb_tbl = gr.Dataframe()
|
| 321 |
lb_s = gr.Textbox(visible=False)
|
| 322 |
+
lb_btn.click(refresh_leaderboard,[],[lb_tbl,lb_s])
|
| 323 |
|
| 324 |
return app
|
| 325 |
|