Spaces:
Sleeping
Sleeping
hotfix: patch _sf() kwarg bug at import time"
Browse files
app.py
CHANGED
|
@@ -3,14 +3,25 @@ Gradio UI β Sample Extractor v9.
|
|
| 3 |
SuperFlux onsets, transient NCC, mel pre-filter, MIDI quantization, param locking.
|
| 4 |
"""
|
| 5 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 6 |
import gradio as gr
|
| 7 |
-
import numpy as np, pandas as pd, json,
|
| 8 |
import soundfile as sf, librosa
|
| 9 |
import matplotlib; matplotlib.use('Agg')
|
| 10 |
import matplotlib.pyplot as plt
|
| 11 |
|
| 12 |
-
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
| 13 |
-
|
| 14 |
from sample_extractor import (
|
| 15 |
extract_stem, detect_onsets, classify_hits,
|
| 16 |
cluster_hits, select_best, synthesize_from_cluster,
|
|
@@ -78,14 +89,14 @@ def run_extraction(audio_in, stem_choice, demucs_model, demucs_shifts, demucs_ov
|
|
| 78 |
sa,ssr=extract_stem(tmp,stem=stem_choice,device="cpu",
|
| 79 |
model_name=demucs_model,shifts=int(demucs_shifts),overlap=float(demucs_overlap))
|
| 80 |
progress(0.15,desc="BPM..."); bpm=detect_bpm(sa,ssr)
|
| 81 |
-
progress(0.25,desc="Onsets
|
| 82 |
hits=detect_onsets(sa,ssr,mode=onset_mode,onset_delta=float(onset_delta),
|
| 83 |
energy_threshold_db=float(energy_db),pre_pad=float(pre_pad),
|
| 84 |
min_dur=float(min_dur),max_dur=float(max_dur),min_gap=float(min_gap))
|
| 85 |
if not hits:
|
| 86 |
return (audio_tuple(sa,ssr),f"**BPM: {bpm}** β No hits.",None,None,None,None,"",pd.DataFrame())
|
| 87 |
progress(0.35,desc="Classify..."); hits=classify_hits(hits)
|
| 88 |
-
progress(0.45,desc="Cluster
|
| 89 |
cl=cluster_hits(hits,audio=sa,sr=ssr,ncc_threshold=float(ncc_threshold),
|
| 90 |
attack_ms=float(attack_ms),target_min=int(target_min),target_max=int(target_max),linkage=str(linkage))
|
| 91 |
progress(0.65,desc="Select..."); select_best(cl)
|
|
@@ -112,7 +123,7 @@ def run_extraction(audio_in, stem_choice, demucs_model, demucs_shifts, demucs_ov
|
|
| 112 |
sm=f"**BPM: {bpm}** Β· **{len(cl)} samples** from {len(hits)} hits\n\n"
|
| 113 |
sm+=f"`{demucs_model}` Β· Ξ΄=`{onset_delta}` Β· E=`{energy_db}dB` Β· attack=`{attack_ms}ms`"
|
| 114 |
if int(target_min)>0 and int(target_max)>0: sm+=f" Β· clusters `{int(target_min)}β{int(target_max)}`"
|
| 115 |
-
if quantize_midi: sm+=f" Β· MIDI
|
| 116 |
sm+="\n\n| Sample | Hits | MIDI |\n|---|---|---|\n"
|
| 117 |
for c in sorted(cl,key=lambda x:x.count,reverse=True): sm+=f"| {c.label} | {c.count} | {c.midi_note} |\n"
|
| 118 |
progress(1.0)
|
|
@@ -162,57 +173,47 @@ def build_app():
|
|
| 162 |
with gr.Blocks(title="π΅ Sample Extractor",theme=gr.themes.Soft(),
|
| 163 |
css=".gradio-container{max-width:1300px!important}") as app:
|
| 164 |
gr.Markdown("# π΅ Sample Extractor v9\n"
|
| 165 |
-
"**SuperFlux**
|
| 166 |
"**Mel pre-filter** Β· **MIDI quantization** Β· **Auto-Tune** with π locks")
|
| 167 |
-
|
| 168 |
with gr.Tabs():
|
| 169 |
with gr.Tab("π΅ Extract"):
|
| 170 |
audio_in=gr.Audio(sources=['upload'],type='numpy',label='Upload Audio')
|
| 171 |
-
|
| 172 |
with gr.Accordion("π§ Stem Separation",open=False):
|
| 173 |
with gr.Row():
|
| 174 |
dm=gr.Dropdown(DEMUCS_MODELS,value="htdemucs_ft",label="Model")
|
| 175 |
st=gr.Dropdown(['drums','bass','other','vocals','all'],value='drums',label='Stem')
|
| 176 |
dsh=gr.Slider(0,5,value=1,step=1,label='Shifts')
|
| 177 |
dov=gr.Slider(0.0,0.5,value=0.25,step=0.05,label='Overlap')
|
| 178 |
-
|
| 179 |
-
with gr.Accordion("π― Onset Detection (SuperFlux)",open=False):
|
| 180 |
with gr.Row(): om=gr.Dropdown(['auto','percussive','harmonic','broadband'],value='auto',label='Mode')
|
| 181 |
with gr.Row():
|
| 182 |
-
od=gr.Slider(0.01,0.5,value=0.12,step=0.01,label='Delta')
|
| 183 |
-
lock_od=gr.Checkbox(value=False,label='π',scale=0)
|
| 184 |
with gr.Row():
|
| 185 |
-
ed=gr.Slider(-70,-10,value=-35,step=1,label='Energy (dB)')
|
| 186 |
-
lock_ed=gr.Checkbox(value=False,label='π',scale=0)
|
| 187 |
with gr.Row():
|
| 188 |
-
mg=gr.Slider(0.005,0.2,value=0.03,step=0.005,label='Min gap (
|
| 189 |
-
lock_mg=gr.Checkbox(value=False,label='π',scale=0)
|
| 190 |
with gr.Row():
|
| 191 |
-
pp=gr.Slider(0.0,0.05,value=0.003,step=0.001,label='Pre-pad
|
| 192 |
-
mnd=gr.Slider(0.005,0.2,value=0.02,step=0.005,label='Min dur
|
| 193 |
-
mxd=gr.Slider(0.1,5.0,value=1.5,step=0.1,label='Max dur
|
| 194 |
-
|
| 195 |
-
with gr.Accordion("π Clustering (Transient NCC + Mel pre-filter)",open=True):
|
| 196 |
with gr.Row():
|
| 197 |
tmin=gr.Number(value=5,label='Target min',precision=0)
|
| 198 |
tmax=gr.Number(value=20,label='Target max',precision=0)
|
| 199 |
lock_tgt=gr.Checkbox(value=True,label='π Lock range',scale=0)
|
| 200 |
-
gr.Markdown("*π = auto-tune
|
| 201 |
with gr.Row():
|
| 202 |
nt=gr.Slider(0.3,0.99,value=0.80,step=0.01,label='NCC threshold')
|
| 203 |
-
atk=gr.Slider(10,100,value=25,step=5,label='Attack
|
| 204 |
lnk=gr.Dropdown(['average','complete','single'],value='average',label='Linkage')
|
| 205 |
-
|
| 206 |
-
with gr.Accordion("πΉ MIDI & Post-processing",open=False):
|
| 207 |
with gr.Row():
|
| 208 |
-
syn=gr.Checkbox(value=True,label='Synthesize
|
| 209 |
qmidi=gr.Checkbox(value=True,label='Quantize MIDI')
|
| 210 |
subdiv=gr.Dropdown([('8th',8),('16th',16),('32nd',32)],value=16,label='Grid')
|
| 211 |
-
|
| 212 |
with gr.Row():
|
| 213 |
tune_btn=gr.Button("ποΈ Auto-Tune",variant="secondary",size="lg")
|
| 214 |
extract_btn=gr.Button("π¬ Extract",variant="primary",size="lg")
|
| 215 |
-
|
| 216 |
tune_summary=gr.Markdown(""); tune_log=gr.Textbox(label="Log",lines=8,max_lines=15,visible=False)
|
| 217 |
summary_md=gr.Markdown("*Upload β Auto-Tune or Extract*")
|
| 218 |
with gr.Row():
|
|
@@ -220,24 +221,18 @@ def build_app():
|
|
| 220 |
rend_out=gr.Audio(type='numpy',label='π Reconstruction',interactive=False)
|
| 221 |
gr.Markdown("### Downloads")
|
| 222 |
with gr.Row():
|
| 223 |
-
arc=gr.File(label="π¦ ZIP",interactive=False)
|
| 224 |
-
mid=gr.File(label="πΉ MIDI",interactive=False)
|
| 225 |
smp=gr.File(label="WAVs",file_count="multiple",interactive=False)
|
| 226 |
met=gr.Dataframe(label="Samples"); stx=gr.Textbox(visible=False)
|
| 227 |
-
|
| 228 |
-
dm.change(fn=lambda m:gr.update(choices=DEMUCS_STEMS.get(m,["drums","bass","other","vocals"])+["all"]),
|
| 229 |
-
inputs=[dm],outputs=[st])
|
| 230 |
tune_btn.click(run_auto_tune,[audio_in,st,dm,dsh,dov,om,od,ed,mg,tmin,tmax,lock_od,lock_ed,lock_mg,lock_tgt],
|
| 231 |
[od,ed,mg,tmin,tmax,tune_summary,tune_log])
|
| 232 |
-
extract_btn.click(run_extraction,
|
| 233 |
-
[audio_in,st,dm,dsh,dov,om,od,ed,pp,mnd,mxd,mg,nt,atk,lnk,tmin,tmax,syn,qmidi,subdiv],
|
| 234 |
[stem_out,summary_md,rend_out,smp,mid,arc,stx,met])
|
| 235 |
-
|
| 236 |
with gr.Tab("π Evaluate"):
|
| 237 |
with gr.Row():
|
| 238 |
ep=gr.Dropdown(['rock','funk','halftime'],value='rock',label='Pattern')
|
| 239 |
-
eb=gr.Slider(80,200,value=120,step=2,label='BPM')
|
| 240 |
-
ebs=gr.Slider(2,8,value=4,step=1,label='Bars')
|
| 241 |
with gr.Row():
|
| 242 |
en=gr.Slider(0.3,0.99,value=0.80,step=0.01,label='NCC')
|
| 243 |
etm=gr.Number(value=0,label='Min',precision=0); etx=gr.Number(value=0,label='Max',precision=0)
|
|
@@ -248,17 +243,14 @@ def build_app():
|
|
| 248 |
evs=gr.Dataframe(); evm2=gr.Dataframe()
|
| 249 |
es1=gr.Textbox(visible=False); es2=gr.Textbox(visible=False)
|
| 250 |
evb.click(run_eval,[ep,eb,ebs,en,etm,etx],[evm,evr,evs,evm2,es1,es2])
|
| 251 |
-
|
| 252 |
with gr.Tab("π Optimize"):
|
| 253 |
with gr.Row():
|
| 254 |
-
on=gr.Slider(2,30,value=5,step=1,label='Iters')
|
| 255 |
-
|
| 256 |
-
osv=gr.Checkbox(value=True,label='Save')
|
| 257 |
ob=gr.Button("π Run",variant="primary",size="lg")
|
| 258 |
-
ol=gr.Textbox(label="Log",lines=20,max_lines=40)
|
| 259 |
-
|
| 260 |
ob.click(run_optimize,[on,ocn,oa,osv],[ol,oh,op,oc])
|
| 261 |
-
|
| 262 |
with gr.Tab("π Leaderboard"):
|
| 263 |
lbb=gr.Button("π Refresh"); lt=gr.Dataframe(); ls=gr.Textbox(visible=False)
|
| 264 |
lbb.click(refresh_lb,[],[lt,ls])
|
|
|
|
| 3 |
SuperFlux onsets, transient NCC, mel pre-filter, MIDI quantization, param locking.
|
| 4 |
"""
|
| 5 |
|
| 6 |
+
import os, sys
|
| 7 |
+
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
| 8 |
+
|
| 9 |
+
# βββ HOTFIX: patch _sf() keyword argument bug ββββββββββββββββββββββββββββββββ
|
| 10 |
+
_src = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'sample_extractor.py')
|
| 11 |
+
with open(_src, 'r') as _f: _content = _f.read()
|
| 12 |
+
if '_sf(yh,lag=2,ms=5)' in _content:
|
| 13 |
+
_content = _content.replace('_sf(yh,lag=2,ms=5)', '_sf(yh,l=2,ms=5)')
|
| 14 |
+
with open(_src, 'w') as _f: _f.write(_content)
|
| 15 |
+
print("[HOTFIX] Fixed _sf() kwarg: lag=2 β l=2")
|
| 16 |
+
del _src, _content
|
| 17 |
+
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 18 |
+
|
| 19 |
import gradio as gr
|
| 20 |
+
import numpy as np, pandas as pd, json, tempfile
|
| 21 |
import soundfile as sf, librosa
|
| 22 |
import matplotlib; matplotlib.use('Agg')
|
| 23 |
import matplotlib.pyplot as plt
|
| 24 |
|
|
|
|
|
|
|
| 25 |
from sample_extractor import (
|
| 26 |
extract_stem, detect_onsets, classify_hits,
|
| 27 |
cluster_hits, select_best, synthesize_from_cluster,
|
|
|
|
| 89 |
sa,ssr=extract_stem(tmp,stem=stem_choice,device="cpu",
|
| 90 |
model_name=demucs_model,shifts=int(demucs_shifts),overlap=float(demucs_overlap))
|
| 91 |
progress(0.15,desc="BPM..."); bpm=detect_bpm(sa,ssr)
|
| 92 |
+
progress(0.25,desc="Onsets...")
|
| 93 |
hits=detect_onsets(sa,ssr,mode=onset_mode,onset_delta=float(onset_delta),
|
| 94 |
energy_threshold_db=float(energy_db),pre_pad=float(pre_pad),
|
| 95 |
min_dur=float(min_dur),max_dur=float(max_dur),min_gap=float(min_gap))
|
| 96 |
if not hits:
|
| 97 |
return (audio_tuple(sa,ssr),f"**BPM: {bpm}** β No hits.",None,None,None,None,"",pd.DataFrame())
|
| 98 |
progress(0.35,desc="Classify..."); hits=classify_hits(hits)
|
| 99 |
+
progress(0.45,desc="Cluster...")
|
| 100 |
cl=cluster_hits(hits,audio=sa,sr=ssr,ncc_threshold=float(ncc_threshold),
|
| 101 |
attack_ms=float(attack_ms),target_min=int(target_min),target_max=int(target_max),linkage=str(linkage))
|
| 102 |
progress(0.65,desc="Select..."); select_best(cl)
|
|
|
|
| 123 |
sm=f"**BPM: {bpm}** Β· **{len(cl)} samples** from {len(hits)} hits\n\n"
|
| 124 |
sm+=f"`{demucs_model}` Β· Ξ΄=`{onset_delta}` Β· E=`{energy_db}dB` Β· attack=`{attack_ms}ms`"
|
| 125 |
if int(target_min)>0 and int(target_max)>0: sm+=f" Β· clusters `{int(target_min)}β{int(target_max)}`"
|
| 126 |
+
if quantize_midi: sm+=f" Β· MIDI 1/{int(subdivision)}"
|
| 127 |
sm+="\n\n| Sample | Hits | MIDI |\n|---|---|---|\n"
|
| 128 |
for c in sorted(cl,key=lambda x:x.count,reverse=True): sm+=f"| {c.label} | {c.count} | {c.midi_note} |\n"
|
| 129 |
progress(1.0)
|
|
|
|
| 173 |
with gr.Blocks(title="π΅ Sample Extractor",theme=gr.themes.Soft(),
|
| 174 |
css=".gradio-container{max-width:1300px!important}") as app:
|
| 175 |
gr.Markdown("# π΅ Sample Extractor v9\n"
|
| 176 |
+
"**SuperFlux** onsets Β· **Transient NCC** (25ms attack) Β· "
|
| 177 |
"**Mel pre-filter** Β· **MIDI quantization** Β· **Auto-Tune** with π locks")
|
|
|
|
| 178 |
with gr.Tabs():
|
| 179 |
with gr.Tab("π΅ Extract"):
|
| 180 |
audio_in=gr.Audio(sources=['upload'],type='numpy',label='Upload Audio')
|
|
|
|
| 181 |
with gr.Accordion("π§ Stem Separation",open=False):
|
| 182 |
with gr.Row():
|
| 183 |
dm=gr.Dropdown(DEMUCS_MODELS,value="htdemucs_ft",label="Model")
|
| 184 |
st=gr.Dropdown(['drums','bass','other','vocals','all'],value='drums',label='Stem')
|
| 185 |
dsh=gr.Slider(0,5,value=1,step=1,label='Shifts')
|
| 186 |
dov=gr.Slider(0.0,0.5,value=0.25,step=0.05,label='Overlap')
|
| 187 |
+
with gr.Accordion("π― Onset Detection",open=False):
|
|
|
|
| 188 |
with gr.Row(): om=gr.Dropdown(['auto','percussive','harmonic','broadband'],value='auto',label='Mode')
|
| 189 |
with gr.Row():
|
| 190 |
+
od=gr.Slider(0.01,0.5,value=0.12,step=0.01,label='Delta'); lock_od=gr.Checkbox(value=False,label='π',scale=0)
|
|
|
|
| 191 |
with gr.Row():
|
| 192 |
+
ed=gr.Slider(-70,-10,value=-35,step=1,label='Energy (dB)'); lock_ed=gr.Checkbox(value=False,label='π',scale=0)
|
|
|
|
| 193 |
with gr.Row():
|
| 194 |
+
mg=gr.Slider(0.005,0.2,value=0.03,step=0.005,label='Min gap'); lock_mg=gr.Checkbox(value=False,label='π',scale=0)
|
|
|
|
| 195 |
with gr.Row():
|
| 196 |
+
pp=gr.Slider(0.0,0.05,value=0.003,step=0.001,label='Pre-pad')
|
| 197 |
+
mnd=gr.Slider(0.005,0.2,value=0.02,step=0.005,label='Min dur')
|
| 198 |
+
mxd=gr.Slider(0.1,5.0,value=1.5,step=0.1,label='Max dur')
|
| 199 |
+
with gr.Accordion("π Clustering",open=True):
|
|
|
|
| 200 |
with gr.Row():
|
| 201 |
tmin=gr.Number(value=5,label='Target min',precision=0)
|
| 202 |
tmax=gr.Number(value=20,label='Target max',precision=0)
|
| 203 |
lock_tgt=gr.Checkbox(value=True,label='π Lock range',scale=0)
|
| 204 |
+
gr.Markdown("*π = auto-tune keeps this value fixed*")
|
| 205 |
with gr.Row():
|
| 206 |
nt=gr.Slider(0.3,0.99,value=0.80,step=0.01,label='NCC threshold')
|
| 207 |
+
atk=gr.Slider(10,100,value=25,step=5,label='Attack (ms)')
|
| 208 |
lnk=gr.Dropdown(['average','complete','single'],value='average',label='Linkage')
|
| 209 |
+
with gr.Accordion("πΉ MIDI & Post",open=False):
|
|
|
|
| 210 |
with gr.Row():
|
| 211 |
+
syn=gr.Checkbox(value=True,label='Synthesize')
|
| 212 |
qmidi=gr.Checkbox(value=True,label='Quantize MIDI')
|
| 213 |
subdiv=gr.Dropdown([('8th',8),('16th',16),('32nd',32)],value=16,label='Grid')
|
|
|
|
| 214 |
with gr.Row():
|
| 215 |
tune_btn=gr.Button("ποΈ Auto-Tune",variant="secondary",size="lg")
|
| 216 |
extract_btn=gr.Button("π¬ Extract",variant="primary",size="lg")
|
|
|
|
| 217 |
tune_summary=gr.Markdown(""); tune_log=gr.Textbox(label="Log",lines=8,max_lines=15,visible=False)
|
| 218 |
summary_md=gr.Markdown("*Upload β Auto-Tune or Extract*")
|
| 219 |
with gr.Row():
|
|
|
|
| 221 |
rend_out=gr.Audio(type='numpy',label='π Reconstruction',interactive=False)
|
| 222 |
gr.Markdown("### Downloads")
|
| 223 |
with gr.Row():
|
| 224 |
+
arc=gr.File(label="π¦ ZIP",interactive=False); mid=gr.File(label="πΉ MIDI",interactive=False)
|
|
|
|
| 225 |
smp=gr.File(label="WAVs",file_count="multiple",interactive=False)
|
| 226 |
met=gr.Dataframe(label="Samples"); stx=gr.Textbox(visible=False)
|
| 227 |
+
dm.change(fn=lambda m:gr.update(choices=DEMUCS_STEMS.get(m,["drums","bass","other","vocals"])+["all"]),inputs=[dm],outputs=[st])
|
|
|
|
|
|
|
| 228 |
tune_btn.click(run_auto_tune,[audio_in,st,dm,dsh,dov,om,od,ed,mg,tmin,tmax,lock_od,lock_ed,lock_mg,lock_tgt],
|
| 229 |
[od,ed,mg,tmin,tmax,tune_summary,tune_log])
|
| 230 |
+
extract_btn.click(run_extraction,[audio_in,st,dm,dsh,dov,om,od,ed,pp,mnd,mxd,mg,nt,atk,lnk,tmin,tmax,syn,qmidi,subdiv],
|
|
|
|
| 231 |
[stem_out,summary_md,rend_out,smp,mid,arc,stx,met])
|
|
|
|
| 232 |
with gr.Tab("π Evaluate"):
|
| 233 |
with gr.Row():
|
| 234 |
ep=gr.Dropdown(['rock','funk','halftime'],value='rock',label='Pattern')
|
| 235 |
+
eb=gr.Slider(80,200,value=120,step=2,label='BPM'); ebs=gr.Slider(2,8,value=4,step=1,label='Bars')
|
|
|
|
| 236 |
with gr.Row():
|
| 237 |
en=gr.Slider(0.3,0.99,value=0.80,step=0.01,label='NCC')
|
| 238 |
etm=gr.Number(value=0,label='Min',precision=0); etx=gr.Number(value=0,label='Max',precision=0)
|
|
|
|
| 243 |
evs=gr.Dataframe(); evm2=gr.Dataframe()
|
| 244 |
es1=gr.Textbox(visible=False); es2=gr.Textbox(visible=False)
|
| 245 |
evb.click(run_eval,[ep,eb,ebs,en,etm,etx],[evm,evr,evs,evm2,es1,es2])
|
|
|
|
| 246 |
with gr.Tab("π Optimize"):
|
| 247 |
with gr.Row():
|
| 248 |
+
on=gr.Slider(2,30,value=5,step=1,label='Iters'); ocn=gr.Textbox(value="opt",label='Name')
|
| 249 |
+
oa=gr.Textbox(value="",label='Author'); osv=gr.Checkbox(value=True,label='Save')
|
|
|
|
| 250 |
ob=gr.Button("π Run",variant="primary",size="lg")
|
| 251 |
+
ol=gr.Textbox(label="Log",lines=20,max_lines=40); oh=gr.Dataframe(); op=gr.Plot()
|
| 252 |
+
oc=gr.Code(label="Config",language="json")
|
| 253 |
ob.click(run_optimize,[on,ocn,oa,osv],[ol,oh,op,oc])
|
|
|
|
| 254 |
with gr.Tab("π Leaderboard"):
|
| 255 |
lbb=gr.Button("π Refresh"); lt=gr.Dataframe(); ls=gr.Textbox(visible=False)
|
| 256 |
lbb.click(refresh_lb,[],[lt,ls])
|