ronantakizawa commited on
Commit
b4d99c2
·
1 Parent(s): a3cb350

Switch to file upload instead of YouTube download (YouTube blocks cloud servers)

Browse files
Files changed (1) hide show
  1. app.py +54 -60
app.py CHANGED
@@ -1,4 +1,4 @@
1
- """SampleFlip — Describe a beat, get a beat."""
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
- """Main generation function called by Gradio."""
39
- if not prompt or not prompt.strip():
40
- raise gr.Error("Please describe the beat you want.")
41
 
42
- from sampleflip.agent import plan_beat, pick_best_result, generate_drums, pick_bass_pattern
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: Plan
54
- log("Planning beat...")
55
- genre_arg = genre_override if genre_override != "auto" else None
56
- bpm_arg = bpm_override if bpm_override and bpm_override > 0 else None
57
-
58
- plan = plan_beat(prompt, genre_override=genre_arg, bpm_override=bpm_arg)
59
- g = plan['genre']
60
- b = plan['bpm']
61
- q = plan['search_query']
62
- name = plan['name']
63
- log(f" Genre: {g} | BPM: {b} | Name: {name}")
64
- log(f' Query: "{q}"')
65
-
66
- # Step 2: Search YouTube
67
- log("Searching YouTube...")
68
- results = search_youtube(q, count=5)
69
- if not results:
70
- raise gr.Error("No YouTube results found. Try a different description.")
71
-
72
- # Step 3: LLM picks best result
73
- log(f" Found {len(results)} results, picking best...")
74
- try:
75
- best_idx = pick_best_result(results, g, prompt)
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 6: Drum pattern
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 7: Render
113
- log(f"Generating {g} beat: \"{name}\"...")
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
- sample_path, name, genre=g,
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("Describe a beat, get a beat. AI searches YouTube for the right sample and generates a full produced beat.")
145
 
146
  with gr.Row():
147
- with gr.Column(scale=3):
 
 
 
 
148
  prompt = gr.Textbox(
149
- label="Describe your beat",
150
- placeholder='e.g. "dark trap beat with piano" or "jazzy house with sax"',
151
- lines=2,
152
  )
153
  with gr.Column(scale=1):
154
  genre = gr.Dropdown(
155
  choices=["auto"] + GENRES,
156
  value="auto",
157
- label="Genre (optional)",
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=12, interactive=False)
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
- **Try these:** dark trap beat with sad piano melody | jazzy house with saxophone | aggressive UK drill with strings | chill lo-fi boom bap
 
 
 
 
 
 
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)