sampleflip / app.py
ronantakizawa's picture
Switch to file upload instead of YouTube download (YouTube blocks cloud servers)
b4d99c2
"""SampleFlip — Upload a sample, get a beat."""
import os
import sys
import json
import tempfile
# Download drum kits from HF dataset repo on startup
SPACE_DIR = os.path.dirname(os.path.abspath(__file__))
KITS_DIR = os.path.join(tempfile.gettempdir(), 'sampleflip_kits')
if not os.path.exists(KITS_DIR) or len(os.listdir(KITS_DIR)) < 5:
print('Downloading drum kits from HF dataset...')
from huggingface_hub import snapshot_download
KITS_DIR = snapshot_download(
'ronantakizawa/sampleflip-kits',
repo_type='dataset',
local_dir=KITS_DIR,
)
print(f'Drum kits ready: {KITS_DIR}')
os.environ['SAMPLEFLIP_KIT_DIR'] = KITS_DIR
os.environ['SAMPLEFLIP_OUTPUT_DIR'] = tempfile.mkdtemp(prefix='sampleflip_out_')
# Add source to path
sys.path.insert(0, os.path.join(SPACE_DIR, 'src'))
import gradio as gr
GENRES = [
'trap', 'boombap', 'jazzhouse', 'progressive_house', 'rnb',
'drill', 'melodic_trap', '2hollis', 'techno', 'breakcore',
]
def generate_beat(audio_file, prompt, genre_override, bpm_override):
"""Generate a beat from an uploaded sample."""
if audio_file is None:
raise gr.Error("Please upload an audio file (WAV or MP3).")
from sampleflip.agent import plan_beat, generate_drums, pick_bass_pattern
from sampleflip.core.render_beat import GENRE_CONFIGS
logs = []
def log(msg):
logs.append(msg)
print(msg)
# Step 1: LLM plans genre/bpm/name from prompt (or use defaults)
if prompt and prompt.strip():
log("Planning beat from description...")
genre_arg = genre_override if genre_override != "auto" else None
bpm_arg = bpm_override if bpm_override and bpm_override > 0 else None
try:
plan = plan_beat(prompt, genre_override=genre_arg, bpm_override=bpm_arg)
g = plan['genre']
b = plan['bpm']
name = plan['name']
except Exception as e:
log(f" Planning failed ({e}), using defaults")
g = genre_override if genre_override != "auto" else "trap"
b = bpm_override if bpm_override and bpm_override > 0 else None
name = "Beat"
else:
g = genre_override if genre_override != "auto" else "trap"
b = bpm_override if bpm_override and bpm_override > 0 else None
name = "Beat"
log(f" Genre: {g} | BPM: {b or 'auto'} | Name: {name}")
# Step 2: Bass pattern
log("Designing bass pattern...")
try:
bass_pat = pick_bass_pattern(prompt or f"{g} beat", g)
os.environ['SAMPLEFLIP_BASS_PATTERN'] = bass_pat
log(f" Bass: {bass_pat}")
except Exception:
pass
# Step 3: Drum pattern
cfg = GENRE_CONFIGS[g]
nbars = cfg['bars']
arrangement = cfg['arrangement']
log("Generating drum pattern...")
drums_json = None
try:
drum_data = generate_drums(prompt or f"{g} beat", g, b or cfg['bpm_default'], nbars, arrangement)
n_pats = len([k for k in drum_data['patterns'] if k != 'silent'])
log(f" Created {n_pats} patterns for {nbars} bars")
drums_json = os.path.join(tempfile.gettempdir(), f'sf_drums_{name}.json')
with open(drums_json, 'w') as f:
json.dump(drum_data, f)
except Exception as e:
log(f" Drum generation failed ({e}), using defaults")
# Step 4: Render
log(f"Generating {g} beat...")
output_dir = os.environ['SAMPLEFLIP_OUTPUT_DIR']
core_dir = os.path.join(SPACE_DIR, 'src', 'sampleflip', 'core')
if core_dir not in sys.path:
sys.path.insert(0, core_dir)
from sampleflip.core.render_beat import render
render(
audio_file, name, genre=g,
bpm_hint=b, nbars=None,
loop_start=None, loop_end=None,
lofi=None, no_bass=False,
vinyl_slow=False, drums_json=drums_json,
)
# Find output MP3
mp3_files = [f for f in os.listdir(output_dir) if f.endswith('.mp3') and name in f]
if not mp3_files:
raise gr.Error("Render completed but no MP3 found.")
mp3_path = os.path.join(output_dir, sorted(mp3_files)[-1])
log(f"Done! {mp3_path}")
return mp3_path, "\n".join(logs)
# Gradio UI
with gr.Blocks(title="SampleFlip", theme=gr.themes.Base()) as demo:
gr.Markdown("# SampleFlip")
gr.Markdown("Upload a sample, get a full produced beat with drums, bass, arrangement, and mastering.")
with gr.Row():
with gr.Column(scale=2):
audio_input = gr.Audio(
label="Upload your sample (WAV or MP3)",
type="filepath",
)
prompt = gr.Textbox(
label="Describe the beat (optional)",
placeholder='e.g. "dark trap beat" or "jazzy house groove"',
lines=1,
)
with gr.Column(scale=1):
genre = gr.Dropdown(
choices=["auto"] + GENRES,
value="auto",
label="Genre",
)
bpm = gr.Slider(
minimum=0, maximum=200, step=1, value=0,
label="BPM (0 = auto)",
)
generate_btn = gr.Button("Generate Beat", variant="primary", size="lg")
with gr.Row():
audio_out = gr.Audio(label="Your Beat", type="filepath")
logs_out = gr.Textbox(label="Generation Log", lines=10, interactive=False)
generate_btn.click(
fn=generate_beat,
inputs=[audio_input, prompt, genre, bpm],
outputs=[audio_out, logs_out],
api_name="generate_beat",
)
gr.Markdown("""
### How to use
1. **Upload** a melody loop, chord progression, or any audio sample (WAV/MP3)
2. **Describe** what kind of beat you want (optional — helps the AI pick drums and bass)
3. **Select** a genre and BPM (or leave on auto)
4. **Click Generate** — AI adds drums, bass, arrangement, FX, and masters to -14 LUFS
**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.
""")
demo.launch(server_name="0.0.0.0", server_port=7860)