41-807 commited on
Commit
e97ab99
·
verified ·
1 Parent(s): 20da8e4

Upload folder using huggingface_hub

Browse files
Files changed (6) hide show
  1. .gitignore +11 -0
  2. README.md +43 -7
  3. app.py +426 -0
  4. lyrics_gen.py +271 -0
  5. presets.py +507 -0
  6. requirements.txt +16 -0
.gitignore ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ __pycache__/
2
+ *.py[cod]
3
+ .venv/
4
+ venv/
5
+ .env
6
+ .cache/
7
+ data/
8
+ *.wav
9
+ *.flac
10
+ *.mp3
11
+ .DS_Store
README.md CHANGED
@@ -1,13 +1,49 @@
1
  ---
2
- title: ACE Step Inspire
3
- emoji: 🏢
4
- colorFrom: yellow
5
- colorTo: green
6
  sdk: gradio
7
- sdk_version: 6.22.0
8
- python_version: '3.12'
9
  app_file: app.py
10
  pinned: false
 
 
 
 
 
 
 
 
 
 
 
11
  ---
12
 
13
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
+ title: ACE-Step Inspire
3
+ emoji: 🎶
4
+ colorFrom: green
5
+ colorTo: blue
6
  sdk: gradio
7
+ sdk_version: 5.49.1
8
+ python_version: "3.11"
9
  app_file: app.py
10
  pinned: false
11
+ license: mit
12
+ short_description: Creative ACE-Step 1.5 text-to-song with genre-first UI
13
+ suggested_hardware: zero-a10g
14
+ models:
15
+ - ACE-Step/acestep-v15-sft
16
+ - ACE-Step/acestep-v15-base
17
+ - ACE-Step/acestep-v15-xl-turbo-diffusers
18
+ tags:
19
+ - music
20
+ - text-to-audio
21
+ - ace-step
22
  ---
23
 
24
+ # ACE-Step Inspire
25
+
26
+ A simplified, inspiration-first Hugging Face Space for **ACE-Step 1.5** text-to-song generation.
27
+
28
+ ## Why this Space
29
+
30
+ Official demos expose many technical controls and can feel unreliable. This Space:
31
+
32
+ - Guides you with **genre → style → mood → tempo → lyrics**
33
+ - Keeps expert diffusion settings collapsed
34
+ - Defaults to the **non-XL SFT** checkpoint, with **XL Turbo** available for faster ZeroGPU runs
35
+ - Treats **lyric generation as the primary path** (editable afterward)
36
+
37
+ ## Models
38
+
39
+ | UI label | Repo |
40
+ |----------|------|
41
+ | SFT (default) | `ACE-Step/acestep-v15-sft` |
42
+ | Base | `ACE-Step/acestep-v15-base` |
43
+ | XL Turbo | `ACE-Step/acestep-v15-xl-turbo-diffusers` |
44
+
45
+ Built with [`diffusers.AceStepPipeline`](https://huggingface.co/docs/diffusers/api/pipelines/ace_step).
46
+
47
+ ## License
48
+
49
+ MIT. ACE-Step models are MIT-licensed by ACE Studio / StepFun. Please disclose AI use and respect copyright when sharing outputs.
app.py ADDED
@@ -0,0 +1,426 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """ACE-Step Inspire — creative text-to-song Space for ACE-Step 1.5."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import os
6
+ import random
7
+ import tempfile
8
+ from typing import Optional
9
+
10
+ # ZeroGPU: import spaces BEFORE torch
11
+ try:
12
+ import spaces
13
+
14
+ HAS_SPACES = True
15
+ except ImportError:
16
+ HAS_SPACES = False
17
+
18
+ for _proxy in ("http_proxy", "https_proxy", "HTTP_PROXY", "HTTPS_PROXY", "ALL_PROXY"):
19
+ os.environ.pop(_proxy, None)
20
+ os.environ.setdefault("GRADIO_ANALYTICS_ENABLED", "False")
21
+ os.environ.setdefault("HF_MODULES_CACHE", "/tmp/hf_modules")
22
+ os.environ.setdefault("MPLCONFIGDIR", "/tmp/matplotlib")
23
+
24
+ import gradio as gr
25
+ import soundfile as sf
26
+ import torch
27
+ from diffusers import AceStepPipeline
28
+
29
+ from lyrics_gen import build_caption, generate_lyrics
30
+ from presets import (
31
+ DEFAULT_DURATION,
32
+ DEFAULT_GENRE,
33
+ DEFAULT_MODEL,
34
+ GENRES,
35
+ KEYS_ALL,
36
+ MODELS,
37
+ MOOD_EXTRA,
38
+ STRUCTURES,
39
+ TIME_SIGNATURES,
40
+ USE_CASES,
41
+ VOCAL_LANGUAGES,
42
+ )
43
+
44
+ # ── Pipeline cache (CPU-resident; moved to CUDA inside @spaces.GPU) ──────────
45
+ _pipes: dict[str, AceStepPipeline] = {}
46
+ _current_repo: Optional[str] = None
47
+
48
+
49
+ def _load_pipe(repo_id: str) -> AceStepPipeline:
50
+ global _current_repo
51
+ if repo_id in _pipes:
52
+ return _pipes[repo_id]
53
+ # Keep only one heavy pipeline in memory on ZeroGPU
54
+ _pipes.clear()
55
+ pipe = AceStepPipeline.from_pretrained(repo_id, torch_dtype=torch.bfloat16)
56
+ if hasattr(pipe, "vae") and hasattr(pipe.vae, "enable_tiling"):
57
+ pipe.vae.enable_tiling()
58
+ _pipes[repo_id] = pipe
59
+ _current_repo = repo_id
60
+ return pipe
61
+
62
+
63
+ def _model_cfg(model_name: str) -> dict:
64
+ return MODELS.get(model_name, MODELS[DEFAULT_MODEL])
65
+
66
+
67
+ def apply_genre(genre: str):
68
+ """Fill creative defaults from genre (all still user-overridable)."""
69
+ g = GENRES[genre]
70
+ styles = g["styles"]
71
+ moods = sorted(set(g["moods"] + MOOD_EXTRA))
72
+ vocals = g["vocal"]
73
+ instruments = g["instruments"]
74
+ keys = g["keys"]
75
+ return (
76
+ gr.update(choices=styles, value=styles[0]),
77
+ gr.update(choices=moods, value=g["moods"][0]),
78
+ gr.update(choices=vocals, value=vocals[0]),
79
+ gr.update(choices=instruments, value=instruments[:3]),
80
+ gr.update(value=g["default_bpm"]),
81
+ gr.update(value=keys[0] if keys else "C major"),
82
+ gr.update(value=g["time_signature"]),
83
+ )
84
+
85
+
86
+ def surprise_me(genre: str, theme: str):
87
+ """Randomize creative controls within the selected genre."""
88
+ g = GENRES.get(genre, GENRES[DEFAULT_GENRE])
89
+ style = random.choice(g["styles"])
90
+ mood = random.choice(g["moods"])
91
+ vocal = random.choice(g["vocal"])
92
+ n_inst = min(3, len(g["instruments"]))
93
+ instruments = random.sample(g["instruments"], k=n_inst)
94
+ bpm = random.randint(*g["bpm"])
95
+ key = random.choice(g["keys"])
96
+ structure = random.choice([s for s in STRUCTURES if s != "Instrumental (no lyrics)"])
97
+ use_case = random.choice(USE_CASES[1:])
98
+ lyrics = generate_lyrics(genre, mood, theme or mood, structure, instrumental=False)
99
+ caption = build_caption(
100
+ genre, style, mood, instruments, vocal, bpm, use_case, instrumental=False
101
+ )
102
+ return (
103
+ style,
104
+ mood,
105
+ vocal,
106
+ instruments,
107
+ bpm,
108
+ key,
109
+ g["time_signature"],
110
+ structure,
111
+ use_case,
112
+ lyrics,
113
+ caption,
114
+ )
115
+
116
+
117
+ def on_generate_lyrics(genre, mood, theme, structure, instrumental, seed):
118
+ seed_i = int(seed) if seed is not None and int(seed) >= 0 else None
119
+ text = generate_lyrics(
120
+ genre=genre,
121
+ mood=mood or "emotional",
122
+ theme=theme or mood or genre,
123
+ structure_name=structure,
124
+ instrumental=bool(instrumental),
125
+ seed=seed_i,
126
+ )
127
+ return text
128
+
129
+
130
+ def on_build_prompt(genre, style, mood, instruments, vocal, bpm, use_case, extra, instrumental):
131
+ inst_list = instruments if isinstance(instruments, list) else []
132
+ return build_caption(
133
+ genre=genre,
134
+ style=style,
135
+ mood=mood,
136
+ instruments=inst_list,
137
+ vocal=vocal,
138
+ bpm=int(bpm),
139
+ use_case=use_case,
140
+ extra=extra or "",
141
+ instrumental=bool(instrumental),
142
+ )
143
+
144
+
145
+ def _generate_impl(
146
+ model_name,
147
+ prompt,
148
+ lyrics,
149
+ duration,
150
+ bpm,
151
+ keyscale,
152
+ timesignature,
153
+ language_label,
154
+ instrumental,
155
+ steps,
156
+ guidance,
157
+ shift,
158
+ seed,
159
+ random_seed,
160
+ genre=None,
161
+ style=None,
162
+ mood=None,
163
+ instruments=None,
164
+ vocal=None,
165
+ use_case=None,
166
+ extra_tags=None,
167
+ ):
168
+ # Auto-build prompt if the user skipped that step
169
+ if not prompt or not str(prompt).strip():
170
+ prompt = build_caption(
171
+ genre=genre or DEFAULT_GENRE,
172
+ style=style or "",
173
+ mood=mood or "",
174
+ instruments=instruments if isinstance(instruments, list) else [],
175
+ vocal=vocal or "",
176
+ bpm=int(bpm) if bpm else 120,
177
+ use_case=use_case or "(none)",
178
+ extra=extra_tags or "",
179
+ instrumental=bool(instrumental),
180
+ )
181
+ if not prompt or not str(prompt).strip():
182
+ raise gr.Error("Prompt/caption is empty. Click Build prompt or fill creative controls.")
183
+
184
+ cfg = _model_cfg(model_name)
185
+ repo_id = cfg["repo_id"]
186
+ pipe = _load_pipe(repo_id)
187
+ pipe.to("cuda")
188
+
189
+ duration = int(duration)
190
+ duration = max(10, min(duration, 600))
191
+
192
+ if instrumental:
193
+ lyrics_text = "[Instrumental]"
194
+ else:
195
+ lyrics_text = (lyrics or "").strip() or "[Instrumental]"
196
+
197
+ seed_val = -1 if seed is None else int(seed)
198
+ use_seed = random.randint(0, 2**31 - 1) if random_seed or seed_val < 0 else seed_val
199
+ generator = torch.Generator(device="cuda").manual_seed(use_seed)
200
+
201
+ steps = int(steps) if steps else cfg["steps"]
202
+ guidance = float(guidance) if guidance is not None else cfg["guidance"]
203
+ shift = float(shift) if shift is not None else cfg["shift"]
204
+ if cfg["turbo"]:
205
+ guidance = 1.0
206
+
207
+ lang = VOCAL_LANGUAGES.get(language_label, "en")
208
+
209
+ kwargs = dict(
210
+ prompt=str(prompt).strip(),
211
+ lyrics=lyrics_text,
212
+ audio_duration=float(duration),
213
+ vocal_language=lang,
214
+ num_inference_steps=steps,
215
+ guidance_scale=guidance,
216
+ shift=shift,
217
+ generator=generator,
218
+ bpm=int(bpm) if bpm else None,
219
+ keyscale=keyscale or None,
220
+ timesignature=str(timesignature) if timesignature else None,
221
+ task_type="text2music",
222
+ )
223
+
224
+ output = pipe(**kwargs)
225
+ audio = output.audios[0]
226
+ if isinstance(audio, torch.Tensor):
227
+ audio = audio.detach().float().cpu().numpy()
228
+ if audio.ndim == 2:
229
+ # (channels, samples) -> (samples, channels)
230
+ if audio.shape[0] <= 8 and audio.shape[0] < audio.shape[1]:
231
+ audio = audio.T
232
+
233
+ sr = getattr(pipe, "sample_rate", 48000)
234
+ tmp = tempfile.NamedTemporaryFile(suffix=".wav", delete=False)
235
+ sf.write(tmp.name, audio, samplerate=sr)
236
+
237
+ meta = (
238
+ f"Model: {repo_id}\n"
239
+ f"Seed: {use_seed} | Steps: {steps} | Guidance: {guidance} | Shift: {shift}\n"
240
+ f"Duration: {duration}s | BPM: {bpm} | Key: {keyscale} | Meter: {timesignature}/4\n"
241
+ f"Language: {lang}"
242
+ )
243
+ return tmp.name, meta
244
+
245
+
246
+ if HAS_SPACES:
247
+ # ZeroGPU max duration is set high; actual budget depends on model
248
+ generate_music = spaces.GPU(duration=300)(_generate_impl)
249
+ else:
250
+ generate_music = _generate_impl
251
+
252
+
253
+ # ── UI ───────────────────────────────────────────────────────────────────────
254
+
255
+ CUSTOM_CSS = """
256
+ .gradio-container { max-width: 1100px !important; }
257
+ footer { display: none !important; }
258
+ """
259
+
260
+ INTRO = """
261
+ # ACE-Step Inspire
262
+ Creative **text-to-song** studio powered by [ACE-Step 1.5](https://github.com/ace-step/ACE-Step-1.5).
263
+
264
+ Pick a **genre**, tune mood / style / tempo, **generate lyrics**, then render.
265
+ Technical knobs stay tucked away for debugging — defaults are chosen to just work on ZeroGPU.
266
+ """
267
+
268
+ with gr.Blocks(title="ACE-Step Inspire", theme=gr.themes.Soft(primary_hue="teal", secondary_hue="slate"), css=CUSTOM_CSS) as demo:
269
+ gr.Markdown(INTRO)
270
+
271
+ with gr.Row():
272
+ model = gr.Dropdown(
273
+ choices=list(MODELS.keys()),
274
+ value=DEFAULT_MODEL,
275
+ label="Model",
276
+ info="Default is non-XL SFT. Switch to XL Turbo if ZeroGPU queues feel slow.",
277
+ )
278
+ duration = gr.Number(
279
+ value=DEFAULT_DURATION,
280
+ precision=0,
281
+ minimum=10,
282
+ maximum=600,
283
+ label="Duration (seconds)",
284
+ )
285
+
286
+ with gr.Row():
287
+ with gr.Column(scale=1):
288
+ gr.Markdown("### Creative direction")
289
+ genre = gr.Dropdown(choices=list(GENRES.keys()), value=DEFAULT_GENRE, label="Genre")
290
+ style = gr.Dropdown(choices=GENRES[DEFAULT_GENRE]["styles"], value=GENRES[DEFAULT_GENRE]["styles"][0], label="Style")
291
+ mood = gr.Dropdown(
292
+ choices=sorted(set(GENRES[DEFAULT_GENRE]["moods"] + MOOD_EXTRA)),
293
+ value=GENRES[DEFAULT_GENRE]["moods"][0],
294
+ label="Mood",
295
+ )
296
+ use_case = gr.Dropdown(choices=USE_CASES, value="(none)", label="Use case / scene")
297
+ theme = gr.Textbox(
298
+ label="Song theme / idea",
299
+ placeholder="e.g. leaving a small town, neon heartbreak, victory after failure…",
300
+ lines=2,
301
+ )
302
+ vocal = gr.Dropdown(choices=GENRES[DEFAULT_GENRE]["vocal"], value=GENRES[DEFAULT_GENRE]["vocal"][0], label="Vocal character")
303
+ instruments = gr.CheckboxGroup(
304
+ choices=GENRES[DEFAULT_GENRE]["instruments"],
305
+ value=GENRES[DEFAULT_GENRE]["instruments"][:3],
306
+ label="Instruments / textures",
307
+ )
308
+ instrumental = gr.Checkbox(label="Instrumental (ignore lyrics)", value=False)
309
+ language = gr.Dropdown(choices=list(VOCAL_LANGUAGES.keys()), value="English", label="Lyric language")
310
+
311
+ with gr.Row():
312
+ bpm = gr.Slider(30, 220, value=GENRES[DEFAULT_GENRE]["default_bpm"], step=1, label="Tempo (BPM)")
313
+ keyscale = gr.Dropdown(choices=KEYS_ALL, value=GENRES[DEFAULT_GENRE]["keys"][0], label="Key")
314
+ timesignature = gr.Dropdown(choices=TIME_SIGNATURES, value="4", label="Time signature")
315
+
316
+ surprise_btn = gr.Button("Surprise me", variant="secondary")
317
+
318
+ with gr.Column(scale=1):
319
+ gr.Markdown("### Lyrics (generate first, then edit)")
320
+ structure = gr.Dropdown(
321
+ choices=list(STRUCTURES.keys()),
322
+ value=list(STRUCTURES.keys())[0],
323
+ label="Song structure",
324
+ )
325
+ lyric_seed = gr.Number(value=-1, precision=0, label="Lyric seed (-1 = random)")
326
+ with gr.Row():
327
+ gen_lyrics_btn = gr.Button("Generate lyrics", variant="primary")
328
+ regen_lyrics_btn = gr.Button("Regenerate")
329
+ lyrics = gr.Textbox(
330
+ label="Lyrics",
331
+ lines=16,
332
+ placeholder="Click Generate lyrics, or paste your own with [verse] / [chorus] tags…",
333
+ )
334
+
335
+ gr.Markdown("### Prompt / caption")
336
+ extra_tags = gr.Textbox(label="Extra tags (optional)", placeholder="warm tape saturation, wide stereo, radio mix…")
337
+ build_prompt_btn = gr.Button("Build prompt from controls")
338
+ prompt = gr.Textbox(label="Final prompt sent to the model", lines=3)
339
+
340
+ with gr.Accordion("Expert / debugging", open=False):
341
+ steps = gr.Slider(4, 60, value=_model_cfg(DEFAULT_MODEL)["steps"], step=1, label="Inference steps")
342
+ guidance = gr.Slider(1.0, 15.0, value=_model_cfg(DEFAULT_MODEL)["guidance"], step=0.5, label="Guidance scale (ignored by turbo)")
343
+ shift = gr.Slider(1.0, 5.0, value=3.0, step=0.5, label="Shift")
344
+ seed = gr.Number(value=-1, precision=0, label="Audio seed (-1 = random)")
345
+ random_seed = gr.Checkbox(value=True, label="Randomize audio seed each run")
346
+
347
+ generate_btn = gr.Button("Generate song", variant="primary", size="lg")
348
+ audio_out = gr.Audio(label="Generated song", type="filepath")
349
+ meta_out = gr.Textbox(label="Run info", lines=4)
350
+
351
+ # ── Wiring ───────────────────────────────────────────────────────────────
352
+ genre.change(
353
+ fn=apply_genre,
354
+ inputs=[genre],
355
+ outputs=[style, mood, vocal, instruments, bpm, keyscale, timesignature],
356
+ )
357
+
358
+ def _sync_expert(model_name):
359
+ cfg = _model_cfg(model_name)
360
+ return cfg["steps"], cfg["guidance"], cfg["shift"]
361
+
362
+ model.change(fn=_sync_expert, inputs=[model], outputs=[steps, guidance, shift])
363
+
364
+ gen_lyrics_btn.click(
365
+ fn=on_generate_lyrics,
366
+ inputs=[genre, mood, theme, structure, instrumental, lyric_seed],
367
+ outputs=[lyrics],
368
+ )
369
+ regen_lyrics_btn.click(
370
+ fn=on_generate_lyrics,
371
+ inputs=[genre, mood, theme, structure, instrumental, lyric_seed],
372
+ outputs=[lyrics],
373
+ )
374
+
375
+ build_prompt_btn.click(
376
+ fn=on_build_prompt,
377
+ inputs=[genre, style, mood, instruments, vocal, bpm, use_case, extra_tags, instrumental],
378
+ outputs=[prompt],
379
+ )
380
+
381
+ surprise_btn.click(
382
+ fn=surprise_me,
383
+ inputs=[genre, theme],
384
+ outputs=[style, mood, vocal, instruments, bpm, keyscale, timesignature, structure, use_case, lyrics, prompt],
385
+ )
386
+
387
+ generate_btn.click(
388
+ fn=generate_music,
389
+ inputs=[
390
+ model,
391
+ prompt,
392
+ lyrics,
393
+ duration,
394
+ bpm,
395
+ keyscale,
396
+ timesignature,
397
+ language,
398
+ instrumental,
399
+ steps,
400
+ guidance,
401
+ shift,
402
+ seed,
403
+ random_seed,
404
+ genre,
405
+ style,
406
+ mood,
407
+ instruments,
408
+ vocal,
409
+ use_case,
410
+ extra_tags,
411
+ ],
412
+ outputs=[audio_out, meta_out],
413
+ )
414
+
415
+ gr.Markdown(
416
+ """
417
+ ---
418
+ **Tips:** Start with **Generate lyrics** → **Build prompt** → **Generate song**.
419
+ For long tracks on ZeroGPU, prefer **XL Turbo**. Non-XL SFT is the quality default.
420
+ Lyrics here use a creative composer tuned to genre/mood (fast & reliable). ACE-Step’s own 5Hz planner LM is part of the heavy full stack and is intentionally not loaded in this Space.
421
+ """
422
+ )
423
+
424
+
425
+ if __name__ == "__main__":
426
+ demo.queue(max_size=10).launch(server_name="0.0.0.0", server_port=7860)
lyrics_gen.py ADDED
@@ -0,0 +1,271 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Genre-aware lyric and caption builders."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import random
6
+ from typing import Optional
7
+
8
+ from presets import GENRES, STRUCTURES
9
+
10
+ # Theme fragments for combinatorial lyric writing
11
+ _OPENINGS = [
12
+ "In the quiet of the {place}",
13
+ "I remember when we {action}",
14
+ "Every neon light says {hook}",
15
+ "They told me not to {action}",
16
+ "Under skies of {color}",
17
+ "Walking through the {place}",
18
+ "Your voice still echoes in the {place}",
19
+ "Midnight on the {place}",
20
+ "I built a fire out of {object}",
21
+ "Counting heartbeats in the {place}",
22
+ ]
23
+
24
+ _PLACES = [
25
+ "city",
26
+ "rain",
27
+ "hallway",
28
+ "station",
29
+ "kitchen light",
30
+ "empty road",
31
+ "crowded room",
32
+ "morning fog",
33
+ "rooftop",
34
+ "backseat",
35
+ "ocean edge",
36
+ "glow of screens",
37
+ ]
38
+
39
+ _ACTIONS = [
40
+ "left",
41
+ "stayed",
42
+ "danced",
43
+ "promised",
44
+ "ran",
45
+ "believed",
46
+ "broke the rules",
47
+ "held on",
48
+ "let go",
49
+ "called your name",
50
+ ]
51
+
52
+ _COLORS = ["blue", "gold", "violet", "silver", "crimson", "amber", "indigo"]
53
+ _OBJECTS = ["paper dreams", "old photos", "broken clocks", "borrowed time", "static nights"]
54
+
55
+ _HOOKS = [
56
+ "don't look back",
57
+ "we are still alive",
58
+ "hold me closer",
59
+ "this is our night",
60
+ "I won't fade away",
61
+ "run with me",
62
+ "say my name",
63
+ "keep the fire burning",
64
+ "never let me go",
65
+ "feel the thunder",
66
+ "dance until dawn",
67
+ "heart on the line",
68
+ ]
69
+
70
+ _CHORUS_TEMPLATES = [
71
+ "{hook}\n{hook}\nWe turn the silence into sound\n{hook}",
72
+ "Oh {hook}\nOh {hook}\nEvery heartbeat loud and proud\n{hook}",
73
+ "{hook}, {hook}\nRising higher than the crowd\n{hook}, {hook}",
74
+ "Sing it with me: {hook}\nLouder than the storm tonight\nSing it with me: {hook}",
75
+ ]
76
+
77
+ _VERSE_TEMPLATES = [
78
+ "{line1}\n{line2}\n{line3}\n{line4}",
79
+ ]
80
+
81
+ _BRIDGE_TEMPLATES = [
82
+ "If tomorrow never comes\nI'll still remember this\nAll the words we never said\nMeet me in the {place}",
83
+ "Break it down, rebuild it slow\nLet the feeling overflow\nIn the {place} we both know\nThis is where we let it go",
84
+ "No map, no script, just light\nWe rewrite the ending tonight\nFrom the {place} to the sky\nWe were never meant to hide",
85
+ ]
86
+
87
+ _GENRE_FLAVOR = {
88
+ "Hip-Hop / Rap": {
89
+ "verse_extra": [
90
+ "Bars stacked like city bricks, I carve my name in time",
91
+ "Pocket full of lessons, every scar a silver line",
92
+ "Beat knocks twice, then I step into the rhyme",
93
+ ],
94
+ "hook_bias": ["rise up", "keep grinding", "real talk", "no fake love"],
95
+ },
96
+ "Metal": {
97
+ "verse_extra": [
98
+ "Iron skies and thunder veins, we roar against the night",
99
+ "Ash and steel, we stand unbroken in the fight",
100
+ ],
101
+ "hook_bias": ["rise from the ashes", "we will never die", "feel the fire"],
102
+ },
103
+ "Latin": {
104
+ "verse_extra": [
105
+ "Bailando bajo estrellas, corazón en flor",
106
+ "La noche nos llama con sabor y calor",
107
+ ],
108
+ "hook_bias": ["baila conmigo", "esta noche", "mi corazón"],
109
+ },
110
+ "K-Pop": {
111
+ "verse_extra": [
112
+ "Spotlight hits, we sparkle through the frame",
113
+ "Heartbeat sync, we rewrite our name",
114
+ ],
115
+ "hook_bias": ["shine with me", "don't stop now", "we glow tonight"],
116
+ },
117
+ "Reggae / Dancehall": {
118
+ "verse_extra": [
119
+ "Irons in the fire, still we keep the vibe",
120
+ "One love in the valley, feel the rhythm thrive",
121
+ ],
122
+ "hook_bias": ["easy now", "feel the vibe", "one love"],
123
+ },
124
+ "Country": {
125
+ "verse_extra": [
126
+ "Dust on the dashboard, radio low",
127
+ "Two-lane highway, nowhere left to go",
128
+ ],
129
+ "hook_bias": ["take me home", "small town lights", "hold on tight"],
130
+ },
131
+ "Blues": {
132
+ "verse_extra": [
133
+ "Guitar cries midnight, whiskey in my hand",
134
+ "Lord, I tried to leave, but the blues still stand",
135
+ ],
136
+ "hook_bias": ["have mercy", "gone too long", "blue as night"],
137
+ },
138
+ }
139
+
140
+
141
+ def _pick(seq, rng: random.Random):
142
+ return rng.choice(seq)
143
+
144
+
145
+ def _line(rng: random.Random, theme: str = "") -> str:
146
+ opening = _pick(_OPENINGS, rng).format(
147
+ place=_pick(_PLACES, rng),
148
+ action=_pick(_ACTIONS, rng),
149
+ color=_pick(_COLORS, rng),
150
+ object=_pick(_OBJECTS, rng),
151
+ hook=_pick(_HOOKS, rng),
152
+ )
153
+ if theme.strip():
154
+ # Lightly weave theme words into a supporting line
155
+ words = [w for w in theme.replace(",", " ").split() if len(w) > 2]
156
+ if words:
157
+ w = _pick(words, rng)
158
+ return f"{opening}\nThinking about {w}, making it mine"
159
+ return opening
160
+
161
+
162
+ def _hook(rng: random.Random, genre: str, theme: str) -> str:
163
+ flavor = _GENRE_FLAVOR.get(genre, {})
164
+ pool = list(_HOOKS) + list(flavor.get("hook_bias", []))
165
+ if theme.strip():
166
+ # Use a short theme phrase as hook sometimes
167
+ short = " ".join(theme.strip().split()[:4])
168
+ if len(short) > 3 and rng.random() < 0.45:
169
+ return short
170
+ return _pick(pool, rng)
171
+
172
+
173
+ def _verse(rng: random.Random, genre: str, theme: str) -> str:
174
+ flavor = _GENRE_FLAVOR.get(genre, {})
175
+ pulse = _pick(["pulse", "weight", "glow", "storm", "quiet"], rng)
176
+ subject = theme.strip() or "this moment"
177
+ lines = [
178
+ _line(rng, theme).split("\n")[0],
179
+ f"I feel the {pulse} of {subject}",
180
+ _pick(
181
+ [
182
+ f"We {_pick(_ACTIONS, rng)} like the world forgot our names",
183
+ f"Shadows on the {_pick(_PLACES, rng)}, playing little games",
184
+ f"Promises in {_pick(_COLORS, rng)}, catching fire in frames",
185
+ ],
186
+ rng,
187
+ ),
188
+ _pick(flavor.get("verse_extra", ["And still we find a way to shine"]), rng),
189
+ ]
190
+ # Clean overly long theme echo
191
+ lines[1] = lines[1][:90]
192
+ return "\n".join(lines)
193
+
194
+
195
+ def _chorus(rng: random.Random, hook: str) -> str:
196
+ return _pick(_CHORUS_TEMPLATES, rng).format(hook=hook)
197
+
198
+
199
+ def _bridge(rng: random.Random) -> str:
200
+ return _pick(_BRIDGE_TEMPLATES, rng).format(place=_pick(_PLACES, rng))
201
+
202
+
203
+ def generate_lyrics(
204
+ genre: str,
205
+ mood: str,
206
+ theme: str,
207
+ structure_name: str,
208
+ instrumental: bool = False,
209
+ seed: Optional[int] = None,
210
+ ) -> str:
211
+ """Generate structured lyrics. Primary creative path for the Space."""
212
+ if instrumental or structure_name == "Instrumental (no lyrics)":
213
+ return "[Instrumental]"
214
+
215
+ rng = random.Random(seed)
216
+ code = STRUCTURES.get(structure_name, "vcvcbc")
217
+ hook = _hook(rng, genre, theme)
218
+ parts: list[str] = []
219
+
220
+ mapping = {
221
+ "i": ("[intro]", f"(instrumental intro — {mood})"),
222
+ "v": ("[verse]", _verse(rng, genre, theme)),
223
+ "p": ("[pre-chorus]", f"Building up the feeling\n{mood.title()} hearts are stealing\nCloser to the meaning"),
224
+ "c": ("[chorus]", _chorus(rng, hook)),
225
+ "b": ("[bridge]", _bridge(rng)),
226
+ "o": ("[outro]", f"{hook}\n({mood} fade out)"),
227
+ }
228
+
229
+ # Avoid duplicate identical choruses looking empty — regenerate lightly
230
+ for ch in code:
231
+ if ch not in mapping:
232
+ continue
233
+ tag, body = mapping[ch]
234
+ if ch == "v":
235
+ body = _verse(rng, genre, theme)
236
+ if ch == "c":
237
+ body = _chorus(rng, hook)
238
+ parts.append(f"{tag}\n{body}")
239
+
240
+ return "\n\n".join(parts)
241
+
242
+
243
+ def build_caption(
244
+ genre: str,
245
+ style: str,
246
+ mood: str,
247
+ instruments: list[str],
248
+ vocal: str,
249
+ bpm: int,
250
+ use_case: str,
251
+ extra: str = "",
252
+ instrumental: bool = False,
253
+ ) -> str:
254
+ """Assemble an ACE-Step style prompt/caption from UI choices."""
255
+ bits = [
256
+ f"{style} {genre.lower()} track",
257
+ f"{mood} mood",
258
+ f"{bpm} BPM",
259
+ ]
260
+ if instruments:
261
+ bits.append("featuring " + ", ".join(instruments[:4]))
262
+ if instrumental or vocal.lower() == "instrumental":
263
+ bits.append("instrumental, no vocals")
264
+ else:
265
+ bits.append(f"{vocal}")
266
+ if use_case and use_case != "(none)":
267
+ bits.append(f"perfect for {use_case}")
268
+ if extra.strip():
269
+ bits.append(extra.strip())
270
+ # ACE-Step likes comma-separated descriptive tags
271
+ return ", ".join(bits)
presets.py ADDED
@@ -0,0 +1,507 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Creative presets for ACE-Step Inspire."""
2
+
3
+ from __future__ import annotations
4
+
5
+ GENRES: dict[str, dict] = {
6
+ "Pop": {
7
+ "styles": [
8
+ "mainstream pop",
9
+ "synth-pop",
10
+ "dance-pop",
11
+ "indie pop",
12
+ "electropop",
13
+ "power pop",
14
+ "bubblegum pop",
15
+ "art pop",
16
+ ],
17
+ "bpm": (100, 128),
18
+ "default_bpm": 118,
19
+ "keys": ["C major", "G major", "D major", "A major", "F major", "A minor", "E minor"],
20
+ "time_signature": "4",
21
+ "instruments": [
22
+ "bright synths",
23
+ "punchy drums",
24
+ "bass guitar",
25
+ "electric guitar",
26
+ "piano",
27
+ "vocal chops",
28
+ ],
29
+ "moods": ["uplifting", "romantic", "confident", "nostalgic", "playful", "bittersweet"],
30
+ "vocal": ["female lead", "male lead", "duet", "stacked harmonies"],
31
+ },
32
+ "Hip-Hop / Rap": {
33
+ "styles": [
34
+ "boom bap",
35
+ "trap",
36
+ "lo-fi hip-hop",
37
+ "conscious rap",
38
+ "cloud rap",
39
+ "drill",
40
+ "old-school hip-hop",
41
+ "melodic rap",
42
+ ],
43
+ "bpm": (70, 160),
44
+ "default_bpm": 92,
45
+ "keys": ["C minor", "A minor", "D minor", "F minor", "G minor", "Bb minor"],
46
+ "time_signature": "4",
47
+ "instruments": [
48
+ "808 bass",
49
+ "crisp hi-hats",
50
+ "sampled piano",
51
+ "vinyl crackle",
52
+ "trap snares",
53
+ "atmospheric pads",
54
+ ],
55
+ "moods": ["confident", "introspective", "aggressive", "chill", "storytelling", "dark"],
56
+ "vocal": ["male rapper", "female rapper", "sung chorus", "spoken word"],
57
+ },
58
+ "Rock": {
59
+ "styles": [
60
+ "classic rock",
61
+ "indie rock",
62
+ "alternative rock",
63
+ "punk rock",
64
+ "hard rock",
65
+ "soft rock",
66
+ "garage rock",
67
+ "post-rock",
68
+ ],
69
+ "bpm": (90, 160),
70
+ "default_bpm": 128,
71
+ "keys": ["E minor", "A minor", "D major", "G major", "C major", "B minor"],
72
+ "time_signature": "4",
73
+ "instruments": [
74
+ "distorted electric guitars",
75
+ "live drums",
76
+ "bass guitar",
77
+ "organ",
78
+ "clean guitar arpeggios",
79
+ ],
80
+ "moods": ["rebellious", "anthemic", "melancholic", "energetic", "raw", "hopeful"],
81
+ "vocal": ["male grit vocals", "female rock vocals", "gang vocals", "shouted chorus"],
82
+ },
83
+ "Electronic / Dance": {
84
+ "styles": [
85
+ "house",
86
+ "techno",
87
+ "trance",
88
+ "drum and bass",
89
+ "synthwave",
90
+ "future bass",
91
+ "deep house",
92
+ "ambient techno",
93
+ ],
94
+ "bpm": (110, 174),
95
+ "default_bpm": 124,
96
+ "keys": ["A minor", "F minor", "C minor", "D minor", "G minor", "E minor"],
97
+ "time_signature": "4",
98
+ "instruments": [
99
+ "four-on-the-floor kick",
100
+ "analog synths",
101
+ "arpeggiators",
102
+ "sidechain bass",
103
+ "filtered pads",
104
+ "risers",
105
+ ],
106
+ "moods": ["euphoric", "hypnotic", "dark", "dreamy", "peak-time", "late-night"],
107
+ "vocal": ["female vocal hooks", "male vocal chops", "instrumental", "spoken phrases"],
108
+ },
109
+ "R&B / Soul": {
110
+ "styles": [
111
+ "contemporary R&B",
112
+ "neo-soul",
113
+ "classic soul",
114
+ "quiet storm",
115
+ "funk-soul",
116
+ "alt-R&B",
117
+ ],
118
+ "bpm": (65, 110),
119
+ "default_bpm": 84,
120
+ "keys": ["Db major", "Ab major", "F minor", "Bb minor", "C minor", "Eb major"],
121
+ "time_signature": "4",
122
+ "instruments": [
123
+ "warm electric piano",
124
+ "smooth bass",
125
+ "brushed drums",
126
+ "horn section",
127
+ "gospel organ",
128
+ "guitar licks",
129
+ ],
130
+ "moods": ["sensual", "vulnerable", "smooth", "late-night", "romantic", "empowered"],
131
+ "vocal": ["silky female vocals", "silky male vocals", "falsetto lead", "harmony stacks"],
132
+ },
133
+ "Jazz": {
134
+ "styles": [
135
+ "jazz trio",
136
+ "smooth jazz",
137
+ "bebop",
138
+ "nu-jazz",
139
+ "jazz-funk",
140
+ "vocal jazz",
141
+ "cool jazz",
142
+ ],
143
+ "bpm": (70, 180),
144
+ "default_bpm": 112,
145
+ "keys": ["Bb major", "F major", "Eb major", "C minor", "D minor", "A minor"],
146
+ "time_signature": "4",
147
+ "instruments": [
148
+ "upright bass",
149
+ "piano",
150
+ "brushed drums",
151
+ "saxophone",
152
+ "trumpet",
153
+ "rhodes",
154
+ ],
155
+ "moods": ["sophisticated", "smoky", "playful", "intimate", "swinging", "nocturnal"],
156
+ "vocal": ["crooner", "scat vocals", "instrumental", "lounge singer"],
157
+ },
158
+ "Folk / Acoustic": {
159
+ "styles": [
160
+ "contemporary folk",
161
+ "indie folk",
162
+ "singer-songwriter",
163
+ "americana",
164
+ "campfire folk",
165
+ "celtic folk",
166
+ ],
167
+ "bpm": (70, 120),
168
+ "default_bpm": 96,
169
+ "keys": ["G major", "D major", "C major", "A major", "E minor", "A minor"],
170
+ "time_signature": "4",
171
+ "instruments": [
172
+ "acoustic guitar",
173
+ "fingerpicking",
174
+ "harmonica",
175
+ "light percussion",
176
+ "mandolin",
177
+ "soft piano",
178
+ ],
179
+ "moods": ["wistful", "warm", "storytelling", "pastoral", "intimate", "hopeful"],
180
+ "vocal": ["gentle male vocals", "gentle female vocals", "harmony duet", "whispered verses"],
181
+ },
182
+ "Metal": {
183
+ "styles": [
184
+ "heavy metal",
185
+ "metalcore",
186
+ "power metal",
187
+ "doom metal",
188
+ "progressive metal",
189
+ "thrash",
190
+ ],
191
+ "bpm": (90, 200),
192
+ "default_bpm": 140,
193
+ "keys": ["E minor", "D minor", "B minor", "F# minor", "A minor", "C minor"],
194
+ "time_signature": "4",
195
+ "instruments": [
196
+ "distorted guitars",
197
+ "double bass drums",
198
+ "thunderous bass",
199
+ "shred solos",
200
+ "orchestral hits",
201
+ ],
202
+ "moods": ["aggressive", "epic", "dark", "triumphant", "brooding", "chaotic"],
203
+ "vocal": ["harsh vocals", "clean epic vocals", "growls + cleans", "choir backing"],
204
+ },
205
+ "Latin": {
206
+ "styles": [
207
+ "reggaeton",
208
+ "latin pop",
209
+ "salsa",
210
+ "bachata",
211
+ "cumbia",
212
+ "latin trap",
213
+ "bossa nova",
214
+ ],
215
+ "bpm": (85, 130),
216
+ "default_bpm": 96,
217
+ "keys": ["A minor", "D minor", "G major", "C major", "E minor", "F major"],
218
+ "time_signature": "4",
219
+ "instruments": [
220
+ "syncopated percussion",
221
+ "nylon guitar",
222
+ "tropical synths",
223
+ "congas",
224
+ "bass groove",
225
+ "brass stabs",
226
+ ],
227
+ "moods": ["romantic", "festive", "sensual", "sunny", "dancefloor", "nostalgic"],
228
+ "vocal": ["Spanish male vocals", "Spanish female vocals", "bilingual vocals", "reggaeton flow"],
229
+ },
230
+ "Country": {
231
+ "styles": [
232
+ "modern country",
233
+ "country pop",
234
+ "outlaw country",
235
+ "bluegrass",
236
+ "country ballad",
237
+ "americana",
238
+ ],
239
+ "bpm": (70, 140),
240
+ "default_bpm": 108,
241
+ "keys": ["G major", "D major", "A major", "C major", "E major", "A minor"],
242
+ "time_signature": "4",
243
+ "instruments": [
244
+ "acoustic guitar",
245
+ "pedal steel",
246
+ "fiddle",
247
+ "banjo",
248
+ "twangy electric guitar",
249
+ "kick-snare groove",
250
+ ],
251
+ "moods": ["heartfelt", "road-trip", "heartbroken", "celebratory", "homey", "bittersweet"],
252
+ "vocal": ["male country vocals", "female country vocals", "harmony chorus", "storyteller"],
253
+ },
254
+ "Lo-Fi / Chill": {
255
+ "styles": [
256
+ "lo-fi hip-hop",
257
+ "chillhop",
258
+ "study beats",
259
+ "downtempo",
260
+ "chillwave",
261
+ "bedroom pop",
262
+ ],
263
+ "bpm": (70, 95),
264
+ "default_bpm": 82,
265
+ "keys": ["C major", "A minor", "F major", "D minor", "G major", "E minor"],
266
+ "time_signature": "4",
267
+ "instruments": [
268
+ "dusty piano samples",
269
+ "soft drums",
270
+ "vinyl crackle",
271
+ "mellow bass",
272
+ "tape hiss",
273
+ "jazzy chords",
274
+ ],
275
+ "moods": ["relaxed", "cozy", "melancholy", "dreamy", "focus", "rainy-day"],
276
+ "vocal": ["soft mumbled vocals", "instrumental", "distant vocal sample", "whispered hooks"],
277
+ },
278
+ "Cinematic / Soundtrack": {
279
+ "styles": [
280
+ "epic orchestral",
281
+ "hybrid trailer",
282
+ "intimate score",
283
+ "adventure theme",
284
+ "dark ambient score",
285
+ "heroic fanfare",
286
+ ],
287
+ "bpm": (60, 140),
288
+ "default_bpm": 100,
289
+ "keys": ["D minor", "C minor", "Bb major", "F major", "A minor", "E minor"],
290
+ "time_signature": "4",
291
+ "instruments": [
292
+ "string orchestra",
293
+ "brass section",
294
+ "timpani",
295
+ "choir",
296
+ "piano motif",
297
+ "hybrid percussion",
298
+ ],
299
+ "moods": ["epic", "tender", "suspenseful", "triumphant", "mysterious", "emotional"],
300
+ "vocal": ["instrumental", "wordless choir", "operatic vocals", "narrative spoken word"],
301
+ },
302
+ "K-Pop": {
303
+ "styles": [
304
+ "bright K-pop",
305
+ "dark K-pop",
306
+ "EDM K-pop",
307
+ "ballad K-pop",
308
+ "hip-hop K-pop",
309
+ "city-pop flavored K-pop",
310
+ ],
311
+ "bpm": (95, 140),
312
+ "default_bpm": 122,
313
+ "keys": ["C major", "G major", "A minor", "F major", "D major", "E minor"],
314
+ "time_signature": "4",
315
+ "instruments": [
316
+ "polished synths",
317
+ "tight drums",
318
+ "bass drops",
319
+ "vocal stacks",
320
+ "brass hits",
321
+ "guitar hooks",
322
+ ],
323
+ "moods": ["catchy", "confident", "emotional", "playful", "dramatic", "glossy"],
324
+ "vocal": ["group vocals", "female idol lead", "male idol lead", "rap bridge"],
325
+ },
326
+ "Reggae / Dancehall": {
327
+ "styles": [
328
+ "roots reggae",
329
+ "dancehall",
330
+ "dub",
331
+ "ska",
332
+ "lovers rock",
333
+ "modern reggae fusion",
334
+ ],
335
+ "bpm": (70, 110),
336
+ "default_bpm": 78,
337
+ "keys": ["A minor", "G major", "C major", "D major", "E minor", "F major"],
338
+ "time_signature": "4",
339
+ "instruments": [
340
+ "offbeat guitar skank",
341
+ "deep bass",
342
+ "rimshot drums",
343
+ "organ bubble",
344
+ "horn section",
345
+ "echo delays",
346
+ ],
347
+ "moods": ["sunny", "relaxed", "spiritual", "groovy", "romantic", "protest"],
348
+ "vocal": ["reggae toasting", "smooth singers", "dancehall toast", "harmony chorus"],
349
+ },
350
+ "Blues": {
351
+ "styles": [
352
+ "electric blues",
353
+ "delta blues",
354
+ "blues rock",
355
+ "soul blues",
356
+ "chicago blues",
357
+ ],
358
+ "bpm": (60, 120),
359
+ "default_bpm": 88,
360
+ "keys": ["E major", "A major", "G major", "C major", "A minor", "E minor"],
361
+ "time_signature": "4",
362
+ "instruments": [
363
+ "blues guitar",
364
+ "harmonica",
365
+ "upright piano",
366
+ "shuffle drums",
367
+ "walking bass",
368
+ ],
369
+ "moods": ["soulful", "heartbroken", "gritty", "late-night", "resilient"],
370
+ "vocal": ["raspy male vocals", "powerful female blues", "call and response", "instrumental"],
371
+ },
372
+ "World / Fusion": {
373
+ "styles": [
374
+ "afrobeats",
375
+ "afro-fusion",
376
+ "middle-eastern fusion",
377
+ "indian classical fusion",
378
+ "balkan beat",
379
+ "global pop",
380
+ ],
381
+ "bpm": (90, 130),
382
+ "default_bpm": 110,
383
+ "keys": ["D minor", "A minor", "G minor", "C major", "F major", "E minor"],
384
+ "time_signature": "4",
385
+ "instruments": [
386
+ "hand percussion",
387
+ "world flutes",
388
+ "plucked strings",
389
+ "modern drums",
390
+ "synth bass",
391
+ "choir textures",
392
+ ],
393
+ "moods": ["celebratory", "spiritual", "adventurous", "warm", "hypnotic", "joyful"],
394
+ "vocal": ["multilingual vocals", "chant-like vocals", "smooth lead", "call and response"],
395
+ },
396
+ }
397
+
398
+ MOOD_EXTRA: list[str] = [
399
+ "uplifting",
400
+ "melancholic",
401
+ "euphoric",
402
+ "intimate",
403
+ "dark",
404
+ "playful",
405
+ "cinematic",
406
+ "nostalgic",
407
+ "aggressive",
408
+ "dreamy",
409
+ "romantic",
410
+ "mysterious",
411
+ ]
412
+
413
+ USE_CASES: list[str] = [
414
+ "(none)",
415
+ "workout / gym",
416
+ "study / focus",
417
+ "road trip",
418
+ "late-night drive",
419
+ "party / club",
420
+ "wedding first dance",
421
+ "game trailer",
422
+ "YouTube intro",
423
+ "meditation / calm",
424
+ "rainy afternoon",
425
+ "sunrise / morning",
426
+ ]
427
+
428
+ STRUCTURES: dict[str, str] = {
429
+ "Verse–Chorus–Verse–Chorus–Bridge–Chorus": "vcvcbc",
430
+ "Verse–Chorus–Verse–Chorus": "vcvc",
431
+ "Intro–Verse–Chorus–Verse–Chorus–Outro": "ivcvco",
432
+ "Verse–Pre–Chorus–Chorus (x2) + Bridge": "vppcpb",
433
+ "Instrumental (no lyrics)": "instrumental",
434
+ }
435
+
436
+ KEYS_ALL: list[str] = [
437
+ "C major",
438
+ "C minor",
439
+ "C# major",
440
+ "C# minor",
441
+ "D major",
442
+ "D minor",
443
+ "Eb major",
444
+ "Eb minor",
445
+ "E major",
446
+ "E minor",
447
+ "F major",
448
+ "F minor",
449
+ "F# major",
450
+ "F# minor",
451
+ "G major",
452
+ "G minor",
453
+ "Ab major",
454
+ "Ab minor",
455
+ "A major",
456
+ "A minor",
457
+ "Bb major",
458
+ "Bb minor",
459
+ "B major",
460
+ "B minor",
461
+ ]
462
+
463
+ TIME_SIGNATURES: list[str] = ["2", "3", "4", "6"]
464
+
465
+ VOCAL_LANGUAGES: dict[str, str] = {
466
+ "English": "en",
467
+ "German": "de",
468
+ "Spanish": "es",
469
+ "French": "fr",
470
+ "Japanese": "ja",
471
+ "Korean": "ko",
472
+ "Chinese": "zh",
473
+ "Portuguese": "pt",
474
+ "Italian": "it",
475
+ "Russian": "ru",
476
+ }
477
+
478
+ MODELS: dict[str, dict] = {
479
+ "SFT (quality, non-XL) — default": {
480
+ "repo_id": "ACE-Step/acestep-v15-sft",
481
+ "steps": 28,
482
+ "guidance": 7.0,
483
+ "shift": 3.0,
484
+ "turbo": False,
485
+ "gpu_seconds": 300,
486
+ },
487
+ "Base (more diversity, non-XL)": {
488
+ "repo_id": "ACE-Step/acestep-v15-base",
489
+ "steps": 32,
490
+ "guidance": 7.0,
491
+ "shift": 3.0,
492
+ "turbo": False,
493
+ "gpu_seconds": 320,
494
+ },
495
+ "XL Turbo (fast, ZeroGPU-friendly)": {
496
+ "repo_id": "ACE-Step/acestep-v15-xl-turbo-diffusers",
497
+ "steps": 8,
498
+ "guidance": 1.0,
499
+ "shift": 3.0,
500
+ "turbo": True,
501
+ "gpu_seconds": 180,
502
+ },
503
+ }
504
+
505
+ DEFAULT_MODEL = "SFT (quality, non-XL) — default"
506
+ DEFAULT_GENRE = "Pop"
507
+ DEFAULT_DURATION = 180
requirements.txt ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ACE-Step Inspire — Hugging Face Space deps
2
+ torch
3
+ torchaudio
4
+ torchvision
5
+ diffusers>=0.37.0
6
+ transformers>=4.51.0
7
+ accelerate>=1.12.0
8
+ safetensors
9
+ sentencepiece
10
+ protobuf
11
+ gradio>=5.0.0
12
+ spaces
13
+ soundfile
14
+ numpy
15
+ scipy
16
+ huggingface_hub>=0.20.0