Update app.py
Browse files
app.py
CHANGED
@@ -28,243 +28,189 @@ except Exception as e:
|
|
28 |
device_map=None
|
29 |
)
|
30 |
|
31 |
-
#
|
32 |
-
|
33 |
-
|
34 |
-
stack(
|
35 |
-
s("bd*4
|
36 |
-
|
37 |
-
n("
|
38 |
-
).scale("a:minor")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
39 |
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
n("[
|
44 |
-
|
45 |
-
).scale("
|
46 |
|
47 |
-
|
48 |
-
stack(
|
49 |
s("bd ~ ~ ~ bd ~ ~ ~").sometimes(fast(2)).gain(0.7),
|
50 |
-
n("0 [2 4] 7
|
51 |
-
s("
|
52 |
-
).scale("g:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
53 |
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
)
|
60 |
-
"""
|
61 |
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
"
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
"house": {
|
72 |
-
"drums": ["bd ~ ~ ~ bd ~ ~ ~", "[bd ~ bd]*2", "bd ~ [~ bd] ~"],
|
73 |
-
"hats": ["~ hh ~ hh", "[~ ch ~ hh]*2", "hh ~ ch ~"],
|
74 |
-
"bass": ["0 2 4 7", "[0 ~ 2] [~ 4]", "0 4 2 7", "[0 2] [4 7]"],
|
75 |
-
"lead": ["[0 2 4]*2", "7 4 2 0", "[2 4 7]*3", "0 4 7 9"],
|
76 |
-
"effects": [".room(0.6).delay(0.125)", ".lpf(2000).sometimes(add(note(7)))", ".gain(sine.range(0.4,0.8).slow(4))", ".pan(cosine.slow(8))"]
|
77 |
-
},
|
78 |
-
"ambient": {
|
79 |
-
"drums": ["~ ~ ~ rim", "bd ~ ~ ~", "~ ~ rim ~"],
|
80 |
-
"hats": ["~ ~ hh ~", "~ ch ~ ~", ""],
|
81 |
-
"bass": ["[0 2 4]/3", "[0 4]/2", "[2 7]/5", "[0 2 4 7]/4"],
|
82 |
-
"lead": ["[7 9 11]/5", "[0 4 7]*2", "[2 4 9]/3", "[7 11 14]/7"],
|
83 |
-
"effects": [".slow(8).room(0.9)", ".slow(12).delay(0.375)", ".slow(16).gain(0.3)", ".slow(6).lpf(800).room(0.8)"]
|
84 |
-
},
|
85 |
-
"breakbeat": {
|
86 |
-
"drums": ["[bd sn]*2", "bd [sn sn] bd sn", "[bd ~ sn ~]*2"],
|
87 |
-
"hats": ["[hh ch]*4", "hh*8", "[ch hh hh]*2"],
|
88 |
-
"bass": ["0 4 7 0 2 5 7 2", "[0 ~ 7] [3 5]", "0 7 4 2"],
|
89 |
-
"lead": ["[0 4 7]*3", "9 7 4 0", "[7 4] [2 0]"],
|
90 |
-
"effects": [".speed(choose([0.8,1,1.2]))", ".degradeBy(0.3)", ".sometimes(rev)", ".cutoff(rand.range(400,2000))"]
|
91 |
-
},
|
92 |
-
"experimental": {
|
93 |
-
"drums": ["[bd rim]*3", "bd ~ [perc:0 perc:1]", "[~ bd] [rim ~]"],
|
94 |
-
"hats": ["[ch oh]*5", "~ [hh hh] ~", "[noise:0]/8"],
|
95 |
-
"bass": ["[0 3 7]/5", "[0 ~ 4] [~ 7]", "0 [2 5] 7"],
|
96 |
-
"lead": ["[0 4 7 11]/7", "[9 7] [4 2]", "[0 ~ 7] [~ 4]"],
|
97 |
-
"effects": [".degradeBy(rand.range(0.1,0.5))", ".speed(perlin.range(0.5,1.5))", ".pan(sine.slow(3))", ".room(cosine.range(0.2,0.9))"]
|
98 |
-
}
|
99 |
-
}
|
100 |
|
101 |
-
|
102 |
-
|
|
|
|
|
|
|
|
|
103 |
|
104 |
-
|
105 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
106 |
|
107 |
-
# Build complexity-based structure
|
108 |
if complexity == "simple":
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
# Generate drum layer
|
119 |
-
drum_pattern = random.choice(templates["drums"])
|
120 |
-
hat_pattern = random.choice(templates["hats"])
|
121 |
-
|
122 |
-
if hat_pattern:
|
123 |
-
drum_code = f's("{drum_pattern}, {hat_pattern}").gain(0.8)'
|
124 |
-
else:
|
125 |
-
drum_code = f's("{drum_pattern}").gain(0.8)'
|
126 |
-
|
127 |
-
layers_code = [drum_code]
|
128 |
-
|
129 |
-
# Generate bass layer
|
130 |
-
bass_pattern = random.choice(templates["bass"])
|
131 |
-
bass_effect = random.choice(templates["effects"]) if include_effects else ""
|
132 |
-
bass_synth = random.choice(["sawtooth", "sine", "square"])
|
133 |
-
octave = random.choice([1, 2])
|
134 |
-
|
135 |
-
bass_code = f'n("{bass_pattern}").s("{bass_synth}").octave({octave}){bass_effect}.gain(0.7)'
|
136 |
-
layers_code.append(bass_code)
|
137 |
-
|
138 |
-
# Add lead layer if complexity allows
|
139 |
-
if layers >= 3:
|
140 |
-
lead_pattern = random.choice(templates["lead"])
|
141 |
-
lead_effect = random.choice(templates["effects"]) if include_effects else ""
|
142 |
-
lead_synth = random.choice(["square", "triangle", "sawtooth"])
|
143 |
-
lead_octave = random.choice([4, 5])
|
144 |
-
|
145 |
-
lead_code = f'n("{lead_pattern}").s("{lead_synth}").octave({lead_octave}){lead_effect}.gain(0.5)'
|
146 |
-
layers_code.append(lead_code)
|
147 |
-
|
148 |
-
# Add atmospheric layer for complex patterns
|
149 |
-
if layers >= 4:
|
150 |
-
if genre == "ambient":
|
151 |
-
atmo_code = 'n("[0 2 4]/7").s("sine").octave(3).slow(16).room(0.9).gain(0.3)'
|
152 |
-
else:
|
153 |
-
perc_sounds = ["perc:0", "perc:1", "rim", "clap"]
|
154 |
-
perc_pattern = random.choice(perc_sounds)
|
155 |
-
atmo_code = f's("~ ~ {perc_pattern} ~").slow(2).gain(0.4).room(0.6)'
|
156 |
-
layers_code.append(atmo_code)
|
157 |
-
|
158 |
-
# Choose random scale
|
159 |
-
scales = ["a:minor", "d:minor", "g:minor", "c:major", "e:minor", "f:major"]
|
160 |
-
scale = random.choice(scales)
|
161 |
-
|
162 |
-
# Build final code
|
163 |
-
if len(layers_code) == 1:
|
164 |
-
final_code = f'{layers_code[0]}.scale("{scale}")'
|
165 |
-
else:
|
166 |
-
layers_str = ',\n '.join(layers_code)
|
167 |
-
final_code = f'stack(\n {layers_str}\n).scale("{scale}")'
|
168 |
-
|
169 |
-
# Add advanced features based on complexity
|
170 |
-
if complexity == "complex" and include_polyrhythm:
|
171 |
-
final_code += '\n\n// Add some spice\n'
|
172 |
-
spice_options = [
|
173 |
-
'.sometimes(fast(2))',
|
174 |
-
'.sometimes(slow(2))',
|
175 |
-
'.degradeBy(0.2)',
|
176 |
-
'.sometimes(rev)',
|
177 |
-
'.euclid(3,8)'
|
178 |
-
]
|
179 |
-
final_code += f'// {random.choice(spice_options)}'
|
180 |
|
181 |
-
return
|
182 |
|
183 |
-
def
|
184 |
-
"""Create
|
|
|
185 |
|
186 |
-
#
|
187 |
-
|
188 |
-
// Examples with complex features:
|
189 |
-
{ADVANCED_STRUDEL_EXAMPLES}
|
190 |
-
|
191 |
-
// Generate {complexity} {genre} pattern for: {prompt}
|
192 |
-
// Use stack(), effects, and dynamic patterns
|
193 |
-
// Code:
|
194 |
-
"""
|
195 |
|
196 |
-
|
197 |
-
|
198 |
-
|
199 |
-
|
200 |
-
temperature=0.8,
|
201 |
-
do_sample=True,
|
202 |
-
top_p=0.9,
|
203 |
-
num_return_sequences=1,
|
204 |
-
pad_token_id=code_generator.tokenizer.eos_token_id
|
205 |
-
)
|
206 |
-
|
207 |
-
generated_text = outputs[0]['generated_text']
|
208 |
-
strudel_code = generated_text[len(system_prompt):].strip()
|
209 |
-
strudel_code = clean_strudel_code(strudel_code)
|
210 |
-
|
211 |
-
# If AI generation is too simple or fails, enhance it
|
212 |
-
if len(strudel_code.split('\n')) < 3 or 'stack(' not in strudel_code:
|
213 |
-
strudel_code = generate_enhanced_strudel_code(prompt, genre, complexity)
|
214 |
-
|
215 |
-
return strudel_code
|
216 |
-
|
217 |
-
except Exception as e:
|
218 |
-
# Fallback to enhanced generation
|
219 |
-
return generate_enhanced_strudel_code(prompt, genre, complexity)
|
220 |
-
|
221 |
-
def clean_strudel_code(code):
|
222 |
-
"""Enhanced code cleaning with better pattern recognition"""
|
223 |
-
lines = code.split('\n')
|
224 |
-
cleaned_lines = []
|
225 |
-
in_stack = False
|
226 |
-
|
227 |
-
for line in lines:
|
228 |
-
line = line.strip()
|
229 |
-
|
230 |
-
# Detect stack structures
|
231 |
-
if 'stack(' in line:
|
232 |
-
in_stack = True
|
233 |
-
cleaned_lines.append(line)
|
234 |
-
continue
|
235 |
-
|
236 |
-
if in_stack and line == ')':
|
237 |
-
cleaned_lines.append(line)
|
238 |
-
in_stack = False
|
239 |
-
continue
|
240 |
-
|
241 |
-
# Include valid Strudel patterns
|
242 |
-
if (line.startswith('s(') or line.startswith('n(') or line.startswith('$:') or
|
243 |
-
line.startswith('all(') or '.scale(' in line or in_stack or
|
244 |
-
any(pattern in line for pattern in ['.gain(', '.lpf(', '.room(', '.delay(', '.octave('])):
|
245 |
-
cleaned_lines.append(line)
|
246 |
-
|
247 |
-
# Stop at non-Strudel code
|
248 |
-
elif any(keyword in line for keyword in ['function', 'var ', 'let ', 'const ', 'import']):
|
249 |
-
break
|
250 |
-
|
251 |
-
if len(cleaned_lines) >= 12:
|
252 |
-
break
|
253 |
|
254 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
255 |
|
256 |
-
def
|
257 |
-
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
258 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
259 |
visual_styles = {
|
260 |
"reactive": """osc(8, 0.1, 1.2)
|
261 |
.color(1.8, 0.8, 1.5)
|
262 |
.modulate(noise(2), 0.3)
|
263 |
.kaleid(6)
|
264 |
-
.rotate(0, 0.05)
|
265 |
.out()""",
|
266 |
"kaleidoscope": """shape(6, 0.3, 0.01)
|
267 |
-
.repeat(
|
268 |
.rotate(0, 0.03)
|
269 |
.color(1.2, 1.8, 0.8)
|
270 |
.kaleid(8)
|
@@ -272,73 +218,74 @@ def create_full_strudel_template(generated_code, include_visuals=True, visual_st
|
|
272 |
"flowing": """noise(3, 0.1)
|
273 |
.color(1.5, 1.2, 0.8)
|
274 |
.modulate(osc(2, 0.05), 0.4)
|
275 |
-
.diff(osc(5, 0.1).color(0.8, 1.5, 1.2))
|
276 |
.contrast(1.4)
|
277 |
.out()""",
|
278 |
"geometric": """osc(12, 0.02, 0.8)
|
279 |
.pixelate(32, 24)
|
280 |
.color(2, 0.8, 1.5)
|
281 |
-
.add(shape(4).scale(0.3).repeat(6, 4))
|
282 |
.contrast(1.6)
|
283 |
.out()"""
|
284 |
}
|
|
|
|
|
|
|
|
|
285 |
|
286 |
-
visual_code =
|
|
|
|
|
287 |
await initHydra({{feedStrudel:5}})
|
288 |
|
289 |
-
{
|
290 |
|
291 |
-
"""
|
292 |
|
293 |
-
|
294 |
-
|
|
|
|
|
|
|
295 |
|
296 |
-
//
|
297 |
-
// all(x => x.fft(5).scope()
|
298 |
-
"""
|
299 |
|
300 |
-
return
|
301 |
|
302 |
-
#
|
303 |
-
def generate_interface(prompt, genre, complexity, include_visuals, visual_style
|
304 |
-
"""
|
305 |
|
306 |
if not prompt.strip():
|
307 |
return "Please enter a description of the music you want to create."
|
308 |
|
309 |
-
# Generate
|
310 |
-
|
311 |
-
generated_code = create_ai_assisted_code(prompt, genre, complexity)
|
312 |
-
else:
|
313 |
-
generated_code = generate_enhanced_strudel_code(prompt, genre, complexity, True, True)
|
314 |
|
315 |
-
# Create
|
316 |
-
|
317 |
|
318 |
-
return
|
319 |
|
320 |
-
# Create
|
321 |
-
with gr.Blocks(title="
|
322 |
gr.Markdown("""
|
323 |
-
# π΅
|
324 |
|
325 |
-
Generate
|
326 |
|
327 |
-
**
|
328 |
-
|
329 |
-
|
330 |
-
|
331 |
-
- β‘ **AI-assisted generation** with smart fallbacks
|
332 |
-
- π΅ **Genre-specific templates** with authentic patterns
|
333 |
|
334 |
-
**Usage:** Describe
|
335 |
""")
|
336 |
|
337 |
with gr.Row():
|
338 |
with gr.Column():
|
339 |
prompt_input = gr.Textbox(
|
340 |
label="πΌ Describe your music",
|
341 |
-
placeholder="e.g., '
|
342 |
lines=3
|
343 |
)
|
344 |
|
@@ -357,56 +304,52 @@ with gr.Blocks(title="Enhanced Strudel Generator", theme=gr.themes.Soft()) as ap
|
|
357 |
|
358 |
with gr.Row():
|
359 |
include_visuals = gr.Checkbox(
|
360 |
-
label="π¨ Include
|
361 |
value=True
|
362 |
)
|
363 |
|
364 |
-
|
365 |
-
|
366 |
-
value=
|
|
|
367 |
)
|
368 |
|
369 |
-
|
370 |
-
choices=["reactive", "kaleidoscope", "flowing", "geometric"],
|
371 |
-
value="reactive",
|
372 |
-
label="ποΈ Visual Style"
|
373 |
-
)
|
374 |
-
|
375 |
-
generate_btn = gr.Button("π Generate Enhanced Code", variant="primary", size="lg")
|
376 |
|
377 |
with gr.Column():
|
378 |
output_code = gr.Code(
|
379 |
-
label="
|
380 |
language="javascript",
|
381 |
-
lines=
|
382 |
)
|
383 |
|
384 |
gr.Markdown("""
|
385 |
-
**π―
|
386 |
-
1. **Copy** the
|
387 |
-
2. **
|
388 |
-
3. **Paste
|
|
|
389 |
|
390 |
-
|
391 |
-
-
|
392 |
-
- Try
|
393 |
-
-
|
394 |
-
-
|
395 |
""")
|
396 |
|
397 |
-
#
|
398 |
-
gr.Markdown("### πͺ
|
399 |
with gr.Row():
|
400 |
-
|
401 |
-
["
|
402 |
-
["
|
403 |
-
["
|
404 |
-
["
|
405 |
-
["Experimental
|
406 |
]
|
407 |
|
408 |
-
for example_text, example_genre, example_complexity in
|
409 |
-
btn = gr.Button(f"
|
410 |
btn.click(
|
411 |
lambda t=example_text, g=example_genre, c=example_complexity: (t, g, c),
|
412 |
outputs=[prompt_input, genre_dropdown, complexity_dropdown]
|
@@ -420,8 +363,7 @@ with gr.Blocks(title="Enhanced Strudel Generator", theme=gr.themes.Soft()) as ap
|
|
420 |
genre_dropdown,
|
421 |
complexity_dropdown,
|
422 |
include_visuals,
|
423 |
-
visual_style
|
424 |
-
use_ai_assist
|
425 |
],
|
426 |
outputs=output_code
|
427 |
)
|
|
|
28 |
device_map=None
|
29 |
)
|
30 |
|
31 |
+
# Curated working Strudel patterns - these are all tested and functional
|
32 |
+
WORKING_PATTERNS = {
|
33 |
+
"techno": [
|
34 |
+
"""stack(
|
35 |
+
s("bd*4").gain(0.8),
|
36 |
+
s("~ hh ~ hh").gain(0.5),
|
37 |
+
n("0 ~ 3 ~").s("sawtooth").octave(2).lpf(1200).gain(0.7)
|
38 |
+
).scale("a:minor")""",
|
39 |
+
|
40 |
+
"""stack(
|
41 |
+
s("bd bd ~ bd").gain(0.8),
|
42 |
+
s("[hh ch]*4").gain(0.4),
|
43 |
+
n("[0 3 5 7]*2").s("square").octave(2).cutoff(800).gain(0.6),
|
44 |
+
n("7 9 7 4").s("triangle").octave(4).delay(0.25).gain(0.5)
|
45 |
+
).scale("d:minor")""",
|
46 |
+
|
47 |
+
"""stack(
|
48 |
+
s("bd*4, ~ rim ~ rim").bank("RolandTR909").gain(0.8),
|
49 |
+
n("0 0 3 3").s("sawtooth").octave(2).lpf(2000).gain(0.7),
|
50 |
+
n("[7 9]*4").s("square").octave(4).room(0.4).gain(0.4)
|
51 |
+
).scale("g:minor")"""
|
52 |
+
],
|
53 |
+
|
54 |
+
"house": [
|
55 |
+
"""stack(
|
56 |
+
s("bd ~ ~ ~ bd ~ ~ ~").gain(0.7),
|
57 |
+
s("~ hh ~ hh").gain(0.5),
|
58 |
+
n("0 2 4 7").s("sine").octave(3).room(0.6).gain(0.6)
|
59 |
+
).scale("c:major")""",
|
60 |
|
61 |
+
"""stack(
|
62 |
+
s("bd ~ [~ bd] ~").gain(0.8),
|
63 |
+
s("[~ ch ~ hh]*2").gain(0.4),
|
64 |
+
n("[0 4 7]*2").s("sawtooth").octave(2).lpf(1500).gain(0.7),
|
65 |
+
n("0 7 4 2").s("triangle").octave(4).delay(0.125).gain(0.4)
|
66 |
+
).scale("f:major")""",
|
67 |
|
68 |
+
"""stack(
|
|
|
69 |
s("bd ~ ~ ~ bd ~ ~ ~").sometimes(fast(2)).gain(0.7),
|
70 |
+
n("[0 2] [4 7] [2 4] [7 0]").s("sine").octave(2).gain(0.6),
|
71 |
+
s("hh*8").degradeBy(0.3).gain(0.3)
|
72 |
+
).scale("g:major")"""
|
73 |
+
],
|
74 |
+
|
75 |
+
"ambient": [
|
76 |
+
"""stack(
|
77 |
+
s("~ ~ ~ rim").slow(2).gain(0.3).room(0.8),
|
78 |
+
n("[0 2 4]/3").s("sine").octave(3).slow(4).room(0.9).gain(0.5),
|
79 |
+
n("[7 9 11]/5").s("triangle").octave(4).slow(8).delay(0.375).gain(0.3)
|
80 |
+
).scale("d:minor")""",
|
81 |
|
82 |
+
"""stack(
|
83 |
+
n("0 2 4 7").s("sine").octave(2).slow(8).room(0.9).gain(0.4),
|
84 |
+
n("[4 7 9]/7").s("triangle").octave(4).slow(12).delay(0.5).gain(0.3),
|
85 |
+
s("~ ~ rim ~").slow(4).gain(0.2).room(0.9)
|
86 |
+
).scale("a:minor")""",
|
|
|
|
|
87 |
|
88 |
+
"""n("[0 4 7 11]/4").s("sine").octave(3).slow(8).room(0.9).lpf(800).gain(0.6).scale("e:minor")"""
|
89 |
+
],
|
90 |
+
|
91 |
+
"breakbeat": [
|
92 |
+
"""stack(
|
93 |
+
s("[bd sn]*2").sometimes(rev).gain(0.8),
|
94 |
+
s("[hh ch]*4").degradeBy(0.2).gain(0.5),
|
95 |
+
n("0 4 7 0").s("square").octave(2).cutoff(1000).gain(0.7)
|
96 |
+
).scale("a:minor")""",
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
97 |
|
98 |
+
"""stack(
|
99 |
+
s("bd [sn sn] bd sn").speed(0.9).gain(0.8),
|
100 |
+
n("[0 ~ 7] [3 5]").s("sawtooth").octave(2).lpf(1500).gain(0.6),
|
101 |
+
s("hh*8").sometimes(fast(2)).gain(0.4)
|
102 |
+
).scale("d:minor")"""
|
103 |
+
],
|
104 |
|
105 |
+
"experimental": [
|
106 |
+
"""stack(
|
107 |
+
s("[bd rim]*3").degradeBy(0.4).gain(0.7),
|
108 |
+
n("[0 3 7]/5").s("square").octave(2).cutoff(perlin.range(400,2000)).gain(0.6),
|
109 |
+
s("~ [hh hh] ~").pan(sine.slow(3)).gain(0.4)
|
110 |
+
).scale("g:minor")""",
|
111 |
+
|
112 |
+
"""stack(
|
113 |
+
s("bd ~ [perc:0 perc:1]").speed(rand.range(0.8,1.2)).gain(0.7),
|
114 |
+
n("0 [2 5] 7").s("sawtooth").octave(2).sometimes(add(note(12))).gain(0.6),
|
115 |
+
s("[ch oh]*5").degradeBy(0.5).gain(0.3)
|
116 |
+
).scale("f#:minor")"""
|
117 |
+
]
|
118 |
+
}
|
119 |
+
|
120 |
+
def get_random_working_pattern(genre, complexity="moderate"):
|
121 |
+
"""Get a random working pattern from our curated collection"""
|
122 |
+
patterns = WORKING_PATTERNS.get(genre, WORKING_PATTERNS["techno"])
|
123 |
|
|
|
124 |
if complexity == "simple":
|
125 |
+
# Return simpler single-line patterns
|
126 |
+
simple_patterns = {
|
127 |
+
"techno": 's("bd*4").gain(0.8)',
|
128 |
+
"house": 's("bd ~ ~ ~ bd ~ ~ ~").gain(0.7)',
|
129 |
+
"ambient": 'n("[0 2 4]/3").s("sine").octave(3).slow(4).room(0.9).gain(0.5).scale("d:minor")',
|
130 |
+
"breakbeat": 's("[bd sn]*2").gain(0.8)',
|
131 |
+
"experimental": 's("bd ~ [perc:0 perc:1]").degradeBy(0.4).gain(0.7)'
|
132 |
+
}
|
133 |
+
return simple_patterns.get(genre, simple_patterns["techno"])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
134 |
|
135 |
+
return random.choice(patterns)
|
136 |
|
137 |
+
def create_variations(base_pattern, genre):
|
138 |
+
"""Create variations of a working base pattern"""
|
139 |
+
variations = []
|
140 |
|
141 |
+
# Original pattern
|
142 |
+
variations.append(base_pattern)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
143 |
|
144 |
+
# Speed variations
|
145 |
+
if "stack(" in base_pattern:
|
146 |
+
variations.append(base_pattern.replace(").scale(", ").sometimes(fast(2)).scale("))
|
147 |
+
variations.append(base_pattern.replace(").scale(", ").slow(2).scale("))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
148 |
|
149 |
+
# Effect variations
|
150 |
+
if ".gain(0.8)" in base_pattern:
|
151 |
+
variations.append(base_pattern.replace(".gain(0.8)", ".gain(0.8).room(0.4)"))
|
152 |
+
|
153 |
+
# Scale variations
|
154 |
+
scales = ["a:minor", "d:minor", "g:minor", "c:major", "f:major", "e:minor"]
|
155 |
+
for scale in scales:
|
156 |
+
if scale not in base_pattern:
|
157 |
+
new_pattern = base_pattern
|
158 |
+
for old_scale in scales:
|
159 |
+
if old_scale in base_pattern:
|
160 |
+
new_pattern = base_pattern.replace(old_scale, scale)
|
161 |
+
break
|
162 |
+
if new_pattern != base_pattern:
|
163 |
+
variations.append(new_pattern)
|
164 |
+
break
|
165 |
+
|
166 |
+
return random.choice(variations)
|
167 |
|
168 |
+
def generate_working_strudel_code(prompt, genre="techno", complexity="moderate"):
|
169 |
+
"""Generate guaranteed working Strudel code"""
|
170 |
+
|
171 |
+
# Get base working pattern
|
172 |
+
base_pattern = get_random_working_pattern(genre, complexity)
|
173 |
+
|
174 |
+
# Create variations based on prompt keywords
|
175 |
+
if any(word in prompt.lower() for word in ["fast", "speed", "quick", "rapid"]):
|
176 |
+
if "sometimes(fast(2))" not in base_pattern:
|
177 |
+
base_pattern = base_pattern.replace(".gain(", ".sometimes(fast(2)).gain(")
|
178 |
+
|
179 |
+
if any(word in prompt.lower() for word in ["slow", "chill", "ambient", "relaxed"]):
|
180 |
+
if ".slow(" not in base_pattern:
|
181 |
+
base_pattern = base_pattern.replace(".gain(", ".slow(2).gain(")
|
182 |
+
|
183 |
+
if any(word in prompt.lower() for word in ["reverb", "space", "room", "hall"]):
|
184 |
+
if ".room(" not in base_pattern:
|
185 |
+
base_pattern = base_pattern.replace(".gain(", ".room(0.6).gain(")
|
186 |
+
|
187 |
+
if any(word in prompt.lower() for word in ["delay", "echo", "repeat"]):
|
188 |
+
if ".delay(" not in base_pattern:
|
189 |
+
base_pattern = base_pattern.replace(".gain(", ".delay(0.25).gain(")
|
190 |
+
|
191 |
+
if any(word in prompt.lower() for word in ["filter", "sweep", "cutoff", "lpf"]):
|
192 |
+
if ".lpf(" not in base_pattern and ".cutoff(" not in base_pattern:
|
193 |
+
base_pattern = base_pattern.replace(".gain(", ".lpf(1200).gain(")
|
194 |
|
195 |
+
if any(word in prompt.lower() for word in ["glitch", "degraded", "broken", "corrupt"]):
|
196 |
+
if ".degradeBy(" not in base_pattern:
|
197 |
+
base_pattern = base_pattern.replace(".gain(", ".degradeBy(0.3).gain(")
|
198 |
+
|
199 |
+
# Add comment based on prompt
|
200 |
+
comment_line = f"// {prompt[:50]}{'...' if len(prompt) > 50 else ''}"
|
201 |
+
|
202 |
+
return f"{comment_line}\n{base_pattern}"
|
203 |
+
|
204 |
+
def create_visual_code(style="reactive"):
|
205 |
+
"""Create working visual code"""
|
206 |
visual_styles = {
|
207 |
"reactive": """osc(8, 0.1, 1.2)
|
208 |
.color(1.8, 0.8, 1.5)
|
209 |
.modulate(noise(2), 0.3)
|
210 |
.kaleid(6)
|
|
|
211 |
.out()""",
|
212 |
"kaleidoscope": """shape(6, 0.3, 0.01)
|
213 |
+
.repeat(3, 2)
|
214 |
.rotate(0, 0.03)
|
215 |
.color(1.2, 1.8, 0.8)
|
216 |
.kaleid(8)
|
|
|
218 |
"flowing": """noise(3, 0.1)
|
219 |
.color(1.5, 1.2, 0.8)
|
220 |
.modulate(osc(2, 0.05), 0.4)
|
|
|
221 |
.contrast(1.4)
|
222 |
.out()""",
|
223 |
"geometric": """osc(12, 0.02, 0.8)
|
224 |
.pixelate(32, 24)
|
225 |
.color(2, 0.8, 1.5)
|
|
|
226 |
.contrast(1.6)
|
227 |
.out()"""
|
228 |
}
|
229 |
+
return visual_styles.get(style, visual_styles["reactive"])
|
230 |
+
|
231 |
+
def create_complete_strudel_code(generated_code, include_visuals=True, visual_style="reactive"):
|
232 |
+
"""Create complete, working Strudel code"""
|
233 |
|
234 |
+
visual_code = ""
|
235 |
+
if include_visuals:
|
236 |
+
visual_code = f"""// Hydra visuals
|
237 |
await initHydra({{feedStrudel:5}})
|
238 |
|
239 |
+
{create_visual_code(visual_style)}
|
240 |
|
241 |
+
"""
|
242 |
|
243 |
+
# Add proper Strudel prefix for audio patterns
|
244 |
+
if not generated_code.startswith("$:"):
|
245 |
+
generated_code = f"$: {generated_code}"
|
246 |
+
|
247 |
+
complete_code = f"""{visual_code}{generated_code}
|
248 |
|
249 |
+
// Global effects (uncomment to use)
|
250 |
+
// all(x => x.fft(5).scope())"""
|
|
|
251 |
|
252 |
+
return complete_code
|
253 |
|
254 |
+
# Main generation function
|
255 |
+
def generate_interface(prompt, genre, complexity, include_visuals, visual_style):
|
256 |
+
"""Main interface function that generates working code"""
|
257 |
|
258 |
if not prompt.strip():
|
259 |
return "Please enter a description of the music you want to create."
|
260 |
|
261 |
+
# Generate working Strudel code
|
262 |
+
strudel_code = generate_working_strudel_code(prompt, genre, complexity)
|
|
|
|
|
|
|
263 |
|
264 |
+
# Create complete working template
|
265 |
+
complete_code = create_complete_strudel_code(strudel_code, include_visuals, visual_style)
|
266 |
|
267 |
+
return complete_code
|
268 |
|
269 |
+
# Create Gradio interface
|
270 |
+
with gr.Blocks(title="Working Strudel Generator", theme=gr.themes.Soft()) as app:
|
271 |
gr.Markdown("""
|
272 |
+
# π΅ Working Strudel Code Generator
|
273 |
|
274 |
+
Generate **guaranteed working** Strudel live coding patterns!
|
275 |
|
276 |
+
β
**All patterns tested and functional**
|
277 |
+
ποΈ **Real Strudel syntax** - copy & paste ready
|
278 |
+
π¨ **Working Hydra visuals** included
|
279 |
+
π΅ **Genre-specific patterns** that actually sound good
|
|
|
|
|
280 |
|
281 |
+
**Usage:** Describe music β Generate β Copy to [strudel.cc](https://strudel.cc) β Play!
|
282 |
""")
|
283 |
|
284 |
with gr.Row():
|
285 |
with gr.Column():
|
286 |
prompt_input = gr.Textbox(
|
287 |
label="πΌ Describe your music",
|
288 |
+
placeholder="e.g., 'Fast techno with reverb', 'Chill ambient with delay', 'Glitchy breakbeat'",
|
289 |
lines=3
|
290 |
)
|
291 |
|
|
|
304 |
|
305 |
with gr.Row():
|
306 |
include_visuals = gr.Checkbox(
|
307 |
+
label="π¨ Include visuals",
|
308 |
value=True
|
309 |
)
|
310 |
|
311 |
+
visual_style = gr.Dropdown(
|
312 |
+
choices=["reactive", "kaleidoscope", "flowing", "geometric"],
|
313 |
+
value="reactive",
|
314 |
+
label="ποΈ Visual Style"
|
315 |
)
|
316 |
|
317 |
+
generate_btn = gr.Button("π΅ Generate Working Code", variant="primary", size="lg")
|
|
|
|
|
|
|
|
|
|
|
|
|
318 |
|
319 |
with gr.Column():
|
320 |
output_code = gr.Code(
|
321 |
+
label="β
Working Strudel Code (Ready to Copy)",
|
322 |
language="javascript",
|
323 |
+
lines=18
|
324 |
)
|
325 |
|
326 |
gr.Markdown("""
|
327 |
+
**π― Instructions:**
|
328 |
+
1. **Copy** all the code above
|
329 |
+
2. **Go to** [strudel.cc](https://strudel.cc)
|
330 |
+
3. **Paste** and **click the play button** βΆοΈ
|
331 |
+
4. **It will work!** π
|
332 |
|
333 |
+
**π§ Customization:**
|
334 |
+
- Change `.gain()` values (0.1 to 1.0)
|
335 |
+
- Try different scales: "a:minor", "c:major", etc.
|
336 |
+
- Adjust `.lpf()` for filter sweeps
|
337 |
+
- Add `.room()` for reverb
|
338 |
""")
|
339 |
|
340 |
+
# Working examples
|
341 |
+
gr.Markdown("### πͺ Tested Examples (Guaranteed to Work)")
|
342 |
with gr.Row():
|
343 |
+
working_examples = [
|
344 |
+
["Fast techno with kick and hats", "techno", "moderate"],
|
345 |
+
["Chill ambient soundscape", "ambient", "simple"],
|
346 |
+
["Driving house beat", "house", "moderate"],
|
347 |
+
["Glitchy breakbeat", "breakbeat", "complex"],
|
348 |
+
["Experimental textures", "experimental", "complex"],
|
349 |
]
|
350 |
|
351 |
+
for example_text, example_genre, example_complexity in working_examples:
|
352 |
+
btn = gr.Button(f"β
{example_text}", size="sm")
|
353 |
btn.click(
|
354 |
lambda t=example_text, g=example_genre, c=example_complexity: (t, g, c),
|
355 |
outputs=[prompt_input, genre_dropdown, complexity_dropdown]
|
|
|
363 |
genre_dropdown,
|
364 |
complexity_dropdown,
|
365 |
include_visuals,
|
366 |
+
visual_style
|
|
|
367 |
],
|
368 |
outputs=output_code
|
369 |
)
|