"""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)