Spaces:
Running
Running
Commit ·
b4d99c2
1
Parent(s): a3cb350
Switch to file upload instead of YouTube download (YouTube blocks cloud servers)
Browse files
app.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
| 1 |
-
"""SampleFlip —
|
| 2 |
|
| 3 |
import os
|
| 4 |
import sys
|
|
@@ -27,72 +27,57 @@ sys.path.insert(0, os.path.join(SPACE_DIR, 'src'))
|
|
| 27 |
|
| 28 |
import gradio as gr
|
| 29 |
|
| 30 |
-
|
| 31 |
GENRES = [
|
| 32 |
'trap', 'boombap', 'jazzhouse', 'progressive_house', 'rnb',
|
| 33 |
'drill', 'melodic_trap', '2hollis', 'techno', 'breakcore',
|
| 34 |
]
|
| 35 |
|
| 36 |
|
| 37 |
-
def generate_beat(prompt, genre_override, bpm_override):
|
| 38 |
-
"""
|
| 39 |
-
if
|
| 40 |
-
raise gr.Error("Please
|
| 41 |
|
| 42 |
-
from sampleflip.agent import plan_beat,
|
| 43 |
-
from sampleflip.search import search_youtube
|
| 44 |
-
from sampleflip.download import download_youtube
|
| 45 |
from sampleflip.core.render_beat import GENRE_CONFIGS
|
| 46 |
|
| 47 |
logs = []
|
| 48 |
-
|
| 49 |
def log(msg):
|
| 50 |
logs.append(msg)
|
| 51 |
print(msg)
|
| 52 |
|
| 53 |
-
# Step 1:
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
|
| 57 |
-
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
|
| 63 |
-
|
| 64 |
-
|
| 65 |
-
|
| 66 |
-
|
| 67 |
-
|
| 68 |
-
|
| 69 |
-
|
| 70 |
-
|
| 71 |
-
|
| 72 |
-
|
| 73 |
-
log(f"
|
| 74 |
-
|
| 75 |
-
|
| 76 |
-
except Exception:
|
| 77 |
-
best_idx = 0
|
| 78 |
-
selected = results[best_idx]
|
| 79 |
-
log(f" Selected: {selected['title']} ({selected['duration_str']})")
|
| 80 |
-
|
| 81 |
-
# Step 4: Download
|
| 82 |
-
log("Downloading...")
|
| 83 |
-
sample_path, size_mb = download_youtube(selected['url'])
|
| 84 |
-
log(f" Done ({size_mb:.1f} MB)")
|
| 85 |
-
|
| 86 |
-
# Step 5: Bass pattern
|
| 87 |
log("Designing bass pattern...")
|
| 88 |
try:
|
| 89 |
-
bass_pat = pick_bass_pattern(prompt, g)
|
| 90 |
os.environ['SAMPLEFLIP_BASS_PATTERN'] = bass_pat
|
| 91 |
log(f" Bass: {bass_pat}")
|
| 92 |
except Exception:
|
| 93 |
pass
|
| 94 |
|
| 95 |
-
# Step
|
| 96 |
cfg = GENRE_CONFIGS[g]
|
| 97 |
nbars = cfg['bars']
|
| 98 |
arrangement = cfg['arrangement']
|
|
@@ -100,7 +85,7 @@ def generate_beat(prompt, genre_override, bpm_override):
|
|
| 100 |
log("Generating drum pattern...")
|
| 101 |
drums_json = None
|
| 102 |
try:
|
| 103 |
-
drum_data = generate_drums(prompt, g, b, nbars, arrangement)
|
| 104 |
n_pats = len([k for k in drum_data['patterns'] if k != 'silent'])
|
| 105 |
log(f" Created {n_pats} patterns for {nbars} bars")
|
| 106 |
drums_json = os.path.join(tempfile.gettempdir(), f'sf_drums_{name}.json')
|
|
@@ -109,18 +94,17 @@ def generate_beat(prompt, genre_override, bpm_override):
|
|
| 109 |
except Exception as e:
|
| 110 |
log(f" Drum generation failed ({e}), using defaults")
|
| 111 |
|
| 112 |
-
# Step
|
| 113 |
-
log(f"Generating {g} beat
|
| 114 |
output_dir = os.environ['SAMPLEFLIP_OUTPUT_DIR']
|
| 115 |
|
| 116 |
-
# Add core to path
|
| 117 |
core_dir = os.path.join(SPACE_DIR, 'src', 'sampleflip', 'core')
|
| 118 |
if core_dir not in sys.path:
|
| 119 |
sys.path.insert(0, core_dir)
|
| 120 |
|
| 121 |
from sampleflip.core.render_beat import render
|
| 122 |
render(
|
| 123 |
-
|
| 124 |
bpm_hint=b, nbars=None,
|
| 125 |
loop_start=None, loop_end=None,
|
| 126 |
lofi=None, no_bass=False,
|
|
@@ -141,20 +125,24 @@ def generate_beat(prompt, genre_override, bpm_override):
|
|
| 141 |
# Gradio UI
|
| 142 |
with gr.Blocks(title="SampleFlip", theme=gr.themes.Base()) as demo:
|
| 143 |
gr.Markdown("# SampleFlip")
|
| 144 |
-
gr.Markdown("
|
| 145 |
|
| 146 |
with gr.Row():
|
| 147 |
-
with gr.Column(scale=
|
|
|
|
|
|
|
|
|
|
|
|
|
| 148 |
prompt = gr.Textbox(
|
| 149 |
-
label="Describe
|
| 150 |
-
placeholder='e.g. "dark trap beat
|
| 151 |
-
lines=
|
| 152 |
)
|
| 153 |
with gr.Column(scale=1):
|
| 154 |
genre = gr.Dropdown(
|
| 155 |
choices=["auto"] + GENRES,
|
| 156 |
value="auto",
|
| 157 |
-
label="Genre
|
| 158 |
)
|
| 159 |
bpm = gr.Slider(
|
| 160 |
minimum=0, maximum=200, step=1, value=0,
|
|
@@ -166,17 +154,23 @@ with gr.Blocks(title="SampleFlip", theme=gr.themes.Base()) as demo:
|
|
| 166 |
with gr.Row():
|
| 167 |
audio_out = gr.Audio(label="Your Beat", type="filepath")
|
| 168 |
|
| 169 |
-
logs_out = gr.Textbox(label="Generation Log", lines=
|
| 170 |
|
| 171 |
generate_btn.click(
|
| 172 |
fn=generate_beat,
|
| 173 |
-
inputs=[prompt, genre, bpm],
|
| 174 |
outputs=[audio_out, logs_out],
|
| 175 |
api_name="generate_beat",
|
| 176 |
)
|
| 177 |
|
| 178 |
gr.Markdown("""
|
| 179 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 180 |
""")
|
| 181 |
|
| 182 |
demo.launch(server_name="0.0.0.0", server_port=7860)
|
|
|
|
| 1 |
+
"""SampleFlip — Upload a sample, get a beat."""
|
| 2 |
|
| 3 |
import os
|
| 4 |
import sys
|
|
|
|
| 27 |
|
| 28 |
import gradio as gr
|
| 29 |
|
|
|
|
| 30 |
GENRES = [
|
| 31 |
'trap', 'boombap', 'jazzhouse', 'progressive_house', 'rnb',
|
| 32 |
'drill', 'melodic_trap', '2hollis', 'techno', 'breakcore',
|
| 33 |
]
|
| 34 |
|
| 35 |
|
| 36 |
+
def generate_beat(audio_file, prompt, genre_override, bpm_override):
|
| 37 |
+
"""Generate a beat from an uploaded sample."""
|
| 38 |
+
if audio_file is None:
|
| 39 |
+
raise gr.Error("Please upload an audio file (WAV or MP3).")
|
| 40 |
|
| 41 |
+
from sampleflip.agent import plan_beat, generate_drums, pick_bass_pattern
|
|
|
|
|
|
|
| 42 |
from sampleflip.core.render_beat import GENRE_CONFIGS
|
| 43 |
|
| 44 |
logs = []
|
|
|
|
| 45 |
def log(msg):
|
| 46 |
logs.append(msg)
|
| 47 |
print(msg)
|
| 48 |
|
| 49 |
+
# Step 1: LLM plans genre/bpm/name from prompt (or use defaults)
|
| 50 |
+
if prompt and prompt.strip():
|
| 51 |
+
log("Planning beat from description...")
|
| 52 |
+
genre_arg = genre_override if genre_override != "auto" else None
|
| 53 |
+
bpm_arg = bpm_override if bpm_override and bpm_override > 0 else None
|
| 54 |
+
try:
|
| 55 |
+
plan = plan_beat(prompt, genre_override=genre_arg, bpm_override=bpm_arg)
|
| 56 |
+
g = plan['genre']
|
| 57 |
+
b = plan['bpm']
|
| 58 |
+
name = plan['name']
|
| 59 |
+
except Exception as e:
|
| 60 |
+
log(f" Planning failed ({e}), using defaults")
|
| 61 |
+
g = genre_override if genre_override != "auto" else "trap"
|
| 62 |
+
b = bpm_override if bpm_override and bpm_override > 0 else None
|
| 63 |
+
name = "Beat"
|
| 64 |
+
else:
|
| 65 |
+
g = genre_override if genre_override != "auto" else "trap"
|
| 66 |
+
b = bpm_override if bpm_override and bpm_override > 0 else None
|
| 67 |
+
name = "Beat"
|
| 68 |
+
|
| 69 |
+
log(f" Genre: {g} | BPM: {b or 'auto'} | Name: {name}")
|
| 70 |
+
|
| 71 |
+
# Step 2: Bass pattern
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 72 |
log("Designing bass pattern...")
|
| 73 |
try:
|
| 74 |
+
bass_pat = pick_bass_pattern(prompt or f"{g} beat", g)
|
| 75 |
os.environ['SAMPLEFLIP_BASS_PATTERN'] = bass_pat
|
| 76 |
log(f" Bass: {bass_pat}")
|
| 77 |
except Exception:
|
| 78 |
pass
|
| 79 |
|
| 80 |
+
# Step 3: Drum pattern
|
| 81 |
cfg = GENRE_CONFIGS[g]
|
| 82 |
nbars = cfg['bars']
|
| 83 |
arrangement = cfg['arrangement']
|
|
|
|
| 85 |
log("Generating drum pattern...")
|
| 86 |
drums_json = None
|
| 87 |
try:
|
| 88 |
+
drum_data = generate_drums(prompt or f"{g} beat", g, b or cfg['bpm_default'], nbars, arrangement)
|
| 89 |
n_pats = len([k for k in drum_data['patterns'] if k != 'silent'])
|
| 90 |
log(f" Created {n_pats} patterns for {nbars} bars")
|
| 91 |
drums_json = os.path.join(tempfile.gettempdir(), f'sf_drums_{name}.json')
|
|
|
|
| 94 |
except Exception as e:
|
| 95 |
log(f" Drum generation failed ({e}), using defaults")
|
| 96 |
|
| 97 |
+
# Step 4: Render
|
| 98 |
+
log(f"Generating {g} beat...")
|
| 99 |
output_dir = os.environ['SAMPLEFLIP_OUTPUT_DIR']
|
| 100 |
|
|
|
|
| 101 |
core_dir = os.path.join(SPACE_DIR, 'src', 'sampleflip', 'core')
|
| 102 |
if core_dir not in sys.path:
|
| 103 |
sys.path.insert(0, core_dir)
|
| 104 |
|
| 105 |
from sampleflip.core.render_beat import render
|
| 106 |
render(
|
| 107 |
+
audio_file, name, genre=g,
|
| 108 |
bpm_hint=b, nbars=None,
|
| 109 |
loop_start=None, loop_end=None,
|
| 110 |
lofi=None, no_bass=False,
|
|
|
|
| 125 |
# Gradio UI
|
| 126 |
with gr.Blocks(title="SampleFlip", theme=gr.themes.Base()) as demo:
|
| 127 |
gr.Markdown("# SampleFlip")
|
| 128 |
+
gr.Markdown("Upload a sample, get a full produced beat with drums, bass, arrangement, and mastering.")
|
| 129 |
|
| 130 |
with gr.Row():
|
| 131 |
+
with gr.Column(scale=2):
|
| 132 |
+
audio_input = gr.Audio(
|
| 133 |
+
label="Upload your sample (WAV or MP3)",
|
| 134 |
+
type="filepath",
|
| 135 |
+
)
|
| 136 |
prompt = gr.Textbox(
|
| 137 |
+
label="Describe the beat (optional)",
|
| 138 |
+
placeholder='e.g. "dark trap beat" or "jazzy house groove"',
|
| 139 |
+
lines=1,
|
| 140 |
)
|
| 141 |
with gr.Column(scale=1):
|
| 142 |
genre = gr.Dropdown(
|
| 143 |
choices=["auto"] + GENRES,
|
| 144 |
value="auto",
|
| 145 |
+
label="Genre",
|
| 146 |
)
|
| 147 |
bpm = gr.Slider(
|
| 148 |
minimum=0, maximum=200, step=1, value=0,
|
|
|
|
| 154 |
with gr.Row():
|
| 155 |
audio_out = gr.Audio(label="Your Beat", type="filepath")
|
| 156 |
|
| 157 |
+
logs_out = gr.Textbox(label="Generation Log", lines=10, interactive=False)
|
| 158 |
|
| 159 |
generate_btn.click(
|
| 160 |
fn=generate_beat,
|
| 161 |
+
inputs=[audio_input, prompt, genre, bpm],
|
| 162 |
outputs=[audio_out, logs_out],
|
| 163 |
api_name="generate_beat",
|
| 164 |
)
|
| 165 |
|
| 166 |
gr.Markdown("""
|
| 167 |
+
### How to use
|
| 168 |
+
1. **Upload** a melody loop, chord progression, or any audio sample (WAV/MP3)
|
| 169 |
+
2. **Describe** what kind of beat you want (optional — helps the AI pick drums and bass)
|
| 170 |
+
3. **Select** a genre and BPM (or leave on auto)
|
| 171 |
+
4. **Click Generate** — AI adds drums, bass, arrangement, FX, and masters to -14 LUFS
|
| 172 |
+
|
| 173 |
+
**Find samples:** Search YouTube for "free melody loop" or "sample pack preview", download with [yt-dlp](https://github.com/yt-dlp/yt-dlp), and upload here.
|
| 174 |
""")
|
| 175 |
|
| 176 |
demo.launch(server_name="0.0.0.0", server_port=7860)
|