akhaliq HF Staff commited on
Commit
cffc867
·
1 Parent(s): 8da1cb7

Remove the LLM compose step (Inference Providers cost)

Browse files

Lyrics + structured caption are now editable reference nodes wired
directly into generate_song, with the official-guide defaults. Drops the
openai dep, the MiniMax-M3 prompts, OAuth token plumbing, and the
inference-api scope.

Files changed (4) hide show
  1. README.md +5 -11
  2. app.py +5 -67
  3. requirements.txt +0 -1
  4. workflow.json +108 -95
README.md CHANGED
@@ -9,25 +9,19 @@ app_file: app.py
9
  pinned: true
10
  suggested_hardware: zero-a10g
11
  hf_oauth: true
12
- hf_oauth_scopes:
13
- - inference-api
14
  ---
15
 
16
  # MiniMax Music 3 — diffusers demo as a `gr.Workflow`
17
 
18
- Generates full songs from a one-line idea using the `MiniMaxMusic3Pipeline` diffusers port, presented as a
19
- visual, node-based `gr.Workflow` canvas (`workflow.json`):
20
 
21
- **Song Idea + Duration `compose_song` (MiniMax-M3 LLM: tagged lyrics + structured caption per the official
22
  prompting guide) → `generate_song` (`@spaces.GPU` ZeroGPU worker: AR frames → windowed DiT decode → vocoder →
23
  wav) → Output Song / Seed Used / Stats → `make_video` (CPU ffmpeg visualizer) → Share Video.**
24
 
25
- Seed, randomize-seed, flow-matching steps, guidance scale, duration, and video title are editable reference
26
- nodes. `hf_oauth: true` lets the owner edit the canvas; visitors sign in to run the pipeline, and the
27
- `compose_song` LLM step uses the visitor's own OAuth token (`inference-api` scope), so inference bills the
28
- visitor directly. For logged-out visitors the LLM step is skipped (it's an add-on) and the pipeline
29
- generates from the built-in demo lyrics + structured caption; set an `HF_TOKEN` secret to let anonymous
30
- runs use the owner's token instead. The original Blocks app
31
  (with the live-streaming PCM player) is preserved as `app_blocks.py` — workflow fn nodes are plain callables,
32
  so chunk-by-chunk streaming is replaced by a final audio subject.
33
 
 
9
  pinned: true
10
  suggested_hardware: zero-a10g
11
  hf_oauth: true
 
 
12
  ---
13
 
14
  # MiniMax Music 3 — diffusers demo as a `gr.Workflow`
15
 
16
+ Generates full songs from tagged lyrics + a structured caption using the `MiniMaxMusic3Pipeline` diffusers
17
+ port, presented as a visual, node-based `gr.Workflow` canvas (`workflow.json`):
18
 
19
+ **Lyrics + Global Metadata + Vocal Details + Arrangement (editable reference nodes, defaults per the official
20
  prompting guide) → `generate_song` (`@spaces.GPU` ZeroGPU worker: AR frames → windowed DiT decode → vocoder →
21
  wav) → Output Song / Seed Used / Stats → `make_video` (CPU ffmpeg visualizer) → Share Video.**
22
 
23
+ Duration, seed, randomize-seed, flow-matching steps, guidance scale, and video title are also editable
24
+ reference nodes. `hf_oauth: true` lets the owner edit the canvas; visitors can run the pipeline. The original Blocks app
 
 
 
 
25
  (with the live-streaming PCM player) is preserved as `app_blocks.py` — workflow fn nodes are plain callables,
26
  so chunk-by-chunk streaming is replaced by a final audio subject.
27
 
app.py CHANGED
@@ -470,66 +470,6 @@ DEFAULT_ARRANGEMENT = (
470
  "long, starlit reverb wash."
471
  )
472
 
473
- _CAPTION_CONTRACT = """The three caption fields follow the exact labeled style the model was trained on. Be concrete and musical; describe an energy arc and instrument lifecycles, never a static equipment list or decorative adjectives. Never contradict an explicit user constraint: instrumental stays instrumental; never reverse a required vocal gender, tempo limit, required instrument, or exclusion. Do not quote or paraphrase lyric lines inside the caption. Total caption length roughly 250-400 words.
474
-
475
- global_metadata: one paragraph, in order: "Basic Attributes: bpm is <number>. key is <letter>, and scale is <major|minor>. <Genre / Subgenre>." then "Global Emotional Progression: <how the emotion evolves from the opening through the final section>." then "Application Scenarios & Imagery: <two or three vivid listening scenarios>." then "Sonics & Production Profile: <soundstage, frequency balance, dynamics, production character>."
476
-
477
- vocal_details: one paragraph: "Vocal Gender & Timbre: Singer A (<Male|Female>), <timbre and register>." then "Vocal Style: <delivery, and how it shifts per section>." then "Harmony/Backing Vocals: <where harmonies or doubles appear and their character>." then "Vocal FX: <restrained treatment: reverb, delay, light compression>." For instrumental pieces write "Instrumental, no vocals." and name the instrument or texture carrying the lead melodic role.
478
-
479
- arrangement: one paragraph: "Instrument Lifecycle Description (Primary/Secondary Layering): Primary: <core instruments present start to finish and their role>. Secondary: <instruments that enter, exit or intensify, and in which sections>." then "Groove & Foundation Progression: <how drums, bass and groove develop across sections>." then "Embellishments, Textures & Spatial FX: <fills, textures, transitional gestures, stereo and space treatment where relevant>." State what enters, exits, changes or intensifies for every section of the song, aligned with the lyric section tags."""
480
-
481
- _LYRICS_RULES = """lyrics: singable lyrics using ONLY these section tags, each ALWAYS ALONE on its own line: [intro] [verse] [pre-chorus] [chorus] [post-chorus] [bridge] [instrumental] [solo] [outro]. Never put words on the same line as a tag. Size the structure to the duration: <=30s: one verse + one chorus; ~60s: verse/pre-chorus/chorus/verse/chorus; >=120s: full structure with bridge and outro. Roughly 12-16 sung words per 10 seconds. Musical instructions (tempo, instruments, dynamics) never belong in the lyrics. If the song is instrumental, use [instrumental] sections with no words."""
482
-
483
- _COMPOSER_SYSTEM = f"""You write inputs for MiniMax Music 3, a lyrics+description music generation model.
484
- Given a song description and a target duration, produce:
485
- 1. {_LYRICS_RULES}
486
- 2-4. global_metadata, vocal_details, arrangement — a structured caption. {_CAPTION_CONTRACT}
487
- Answer with ONLY a JSON object with keys: lyrics, global_metadata, vocal_details, arrangement."""
488
-
489
- _LYRICS_SYSTEM = f"""You write lyrics for MiniMax Music 3, a lyrics+description music generation model.
490
- Given a lyrics instruction, the current structured prompt (global metadata, vocal details, arrangement) and a target duration, write lyrics coherent with that structured prompt.
491
- {_LYRICS_RULES}
492
- Answer with ONLY a JSON object with key: lyrics."""
493
-
494
- _PROMPT_SYSTEM = f"""You write the structured caption for MiniMax Music 3, a lyrics+description music generation model.
495
- Given a sound instruction and/or lyrics, produce global_metadata, vocal_details and arrangement. Build the arrangement timeline around the lyric section tags when lyrics are provided. {_CAPTION_CONTRACT}
496
- Answer with ONLY a JSON object with keys: global_metadata, vocal_details, arrangement."""
497
-
498
-
499
- def _llm_json(system, user, token=None):
500
- import json as _json
501
-
502
- from openai import OpenAI
503
-
504
- # Prefer the visitor's OAuth token (hf_oauth + inference-api scope bills the visitor
505
- # directly); fall back to the Space owner's HF_TOKEN secret for local/anonymous runs.
506
- api_key = getattr(token, "token", None) or os.environ.get("HF_TOKEN")
507
- if not api_key:
508
- return None # logged-out visitor, no owner secret: caller skips the LLM step
509
- client = OpenAI(base_url="https://router.huggingface.co/v1", api_key=api_key)
510
- completion = client.chat.completions.create(
511
- model="MiniMaxAI/MiniMax-M3:fastest",
512
- messages=[{"role": "system", "content": system}, {"role": "user", "content": user}],
513
- )
514
- text = completion.choices[0].message.content.strip()
515
- if text.startswith("```"):
516
- text = text.split("```")[1].removeprefix("json").strip()
517
- return _json.loads(text)
518
-
519
-
520
- def compose_song(description, duration, token: gr.OAuthToken = None):
521
- # `token` is injected by gr.Workflow (OAuthToken params are excluded from the canvas
522
- # ports and filled with the signed-in visitor's OAuth token at run time).
523
- if not description.strip():
524
- raise gr.Error("Describe the song you want first.")
525
- data = _llm_json(_COMPOSER_SYSTEM, f"Song description: {description}\nTarget duration: {int(duration)} seconds.", token)
526
- if data is None:
527
- # No OAuth token (logged out) and no HF_TOKEN secret: the LLM compose step is an
528
- # add-on — skip it and generate from the built-in demo lyrics + structured caption.
529
- return DEFAULT_LYRICS, DEFAULT_GLOBAL, DEFAULT_VOCALS, DEFAULT_ARRANGEMENT
530
- return data["lyrics"], data["global_metadata"], data["vocal_details"], data["arrangement"]
531
-
532
-
533
  def render_video(wav_path, title):
534
  # Social share visualizer: warm citrus bars on a dark gradient, rendered via numpy -> ffmpeg pipe (CPU).
535
  if not wav_path:
@@ -637,13 +577,12 @@ def render_video(wav_path, title):
637
 
638
 
639
  # ---------------------------------------------------------------------------
640
- # gr.Workflow app. The canvas (workflow.json) wires three fn operators:
641
- # compose_song — MiniMax-M3 LLM writes lyrics + structured caption from a one-line idea
642
  # generate_song — @spaces.GPU ZeroGPU worker: AR frames -> windowed DiT decode -> vocoder -> wav
643
  # make_video — CPU ffmpeg visualizer for the share video
644
- # Workflow fn nodes are plain callables (no streaming), so the live PCM player of the
645
- # Blocks version becomes a final audio subject; everything else (compose assist, structured
646
- # caption fields, duration/seed/steps/guidance knobs, share video, seed reporting) is kept.
647
  # ---------------------------------------------------------------------------
648
 
649
  import tempfile
@@ -677,7 +616,7 @@ def _friendly_gpu_error(err):
677
  def _generate_song_gpu(lyrics, global_meta, vocal_details, arrangement, duration, seed, randomize_seed, steps, guidance):
678
  caption = "\n".join(s.strip() for s in (global_meta, vocal_details, arrangement) if s and s.strip())
679
  if not caption:
680
- raise gr.Error("Fill in the structured prompt (or run compose_song) first.")
681
  if not lyrics or not lyrics.strip():
682
  raise gr.Error("Lyrics are required (section tags like [verse] must be on their own line).")
683
  if randomize_seed:
@@ -751,7 +690,6 @@ def make_video(audio, title: str):
751
  demo = gr.Workflow(
752
  graph="workflow.json",
753
  bind={
754
- "compose_song": compose_song,
755
  "generate_song": generate_song,
756
  "make_video": make_video,
757
  },
 
470
  "long, starlit reverb wash."
471
  )
472
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
473
  def render_video(wav_path, title):
474
  # Social share visualizer: warm citrus bars on a dark gradient, rendered via numpy -> ffmpeg pipe (CPU).
475
  if not wav_path:
 
577
 
578
 
579
  # ---------------------------------------------------------------------------
580
+ # gr.Workflow app. The canvas (workflow.json) wires two fn operators:
 
581
  # generate_song — @spaces.GPU ZeroGPU worker: AR frames -> windowed DiT decode -> vocoder -> wav
582
  # make_video — CPU ffmpeg visualizer for the share video
583
+ # Lyrics + structured caption are editable reference nodes (defaults from the official
584
+ # prompting guide). Workflow fn nodes are plain callables (no streaming), so the live PCM
585
+ # player of the Blocks version becomes a final audio subject.
586
  # ---------------------------------------------------------------------------
587
 
588
  import tempfile
 
616
  def _generate_song_gpu(lyrics, global_meta, vocal_details, arrangement, duration, seed, randomize_seed, steps, guidance):
617
  caption = "\n".join(s.strip() for s in (global_meta, vocal_details, arrangement) if s and s.strip())
618
  if not caption:
619
+ raise gr.Error("Fill in the structured prompt (Global metadata / Vocal details / Arrangement) first.")
620
  if not lyrics or not lyrics.strip():
621
  raise gr.Error("Lyrics are required (section tags like [verse] must be on their own line).")
622
  if randomize_seed:
 
690
  demo = gr.Workflow(
691
  graph="workflow.json",
692
  bind={
 
693
  "generate_song": generate_song,
694
  "make_video": make_video,
695
  },
requirements.txt CHANGED
@@ -3,4 +3,3 @@ transformers
3
  accelerate
4
  scipy
5
  spaces
6
- openai
 
3
  accelerate
4
  scipy
5
  spaces
 
workflow.json CHANGED
@@ -1,7 +1,7 @@
1
  {
2
  "schema_version": "2",
3
  "name": "MiniMax Music 3",
4
- "description": "Full-song generation with MiniMax Music 3 on ZeroGPU: a MiniMax-M3 LLM composes tagged lyrics + a structured caption from a one-line idea, then the diffusers pipeline (AR frames -> windowed DiT decode -> vocoder) renders the song, plus an ffmpeg share-video visualizer. Tip: sign in with Hugging Face (optional) to have the compose_song LLM step write custom lyrics + caption from your Song Idea on your own inference quota \u2014 logged-out runs skip it and use the built-in demo lyrics.",
5
  "runtime": {
6
  "default": "client"
7
  },
@@ -10,30 +10,111 @@
10
  },
11
  "references": [
12
  {
13
- "id": "ref_description",
14
- "label": "Song Idea",
15
  "role": "reference",
16
  "asset_type": "text",
17
  "inputs": [
18
  {
19
  "id": "in",
20
- "label": "Song Idea",
21
  "type": "text"
22
  }
23
  ],
24
  "outputs": [
25
  {
26
  "id": "out",
27
- "label": "Song Idea",
28
  "type": "text"
29
  }
30
  ],
31
  "x": 40,
32
- "y": 120,
33
- "width": 260,
34
- "height": 130,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
35
  "data": {
36
- "out": "a shimmering synth-pop anthem about a night drive under neon city lights, warm female vocals, euphoric chorus"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37
  }
38
  },
39
  {
@@ -56,7 +137,7 @@
56
  }
57
  ],
58
  "x": 40,
59
- "y": 280,
60
  "width": 200,
61
  "height": 90,
62
  "data": {
@@ -82,8 +163,8 @@
82
  "type": "number"
83
  }
84
  ],
85
- "x": 40,
86
- "y": 400,
87
  "width": 200,
88
  "height": 90,
89
  "data": {
@@ -110,7 +191,7 @@
110
  }
111
  ],
112
  "x": 40,
113
- "y": 520,
114
  "width": 200,
115
  "height": 90,
116
  "data": {
@@ -136,8 +217,8 @@
136
  "type": "number"
137
  }
138
  ],
139
- "x": 40,
140
- "y": 640,
141
  "width": 200,
142
  "height": 90,
143
  "data": {
@@ -164,7 +245,7 @@
164
  }
165
  ],
166
  "x": 40,
167
- "y": 760,
168
  "width": 200,
169
  "height": 90,
170
  "data": {
@@ -200,58 +281,6 @@
200
  }
201
  ],
202
  "operators": [
203
- {
204
- "id": "op_compose",
205
- "label": "compose_song",
206
- "role": "operator",
207
- "kind": "fn",
208
- "source": "fn",
209
- "fn": "compose_song",
210
- "inputs": [
211
- {
212
- "id": "in_0",
213
- "label": "description",
214
- "type": "text",
215
- "required": true
216
- },
217
- {
218
- "id": "in_1",
219
- "label": "duration",
220
- "type": "number"
221
- }
222
- ],
223
- "outputs": [
224
- {
225
- "id": "out_0",
226
- "label": "lyrics",
227
- "type": "text",
228
- "output_index": 0
229
- },
230
- {
231
- "id": "out_1",
232
- "label": "global_metadata",
233
- "type": "text",
234
- "output_index": 1
235
- },
236
- {
237
- "id": "out_2",
238
- "label": "vocal_details",
239
- "type": "text",
240
- "output_index": 2
241
- },
242
- {
243
- "id": "out_3",
244
- "label": "arrangement",
245
- "type": "text",
246
- "output_index": 3
247
- }
248
- ],
249
- "x": 380,
250
- "y": 150,
251
- "width": 260,
252
- "height": 180,
253
- "data": {}
254
- },
255
  {
256
  "id": "op_generate",
257
  "label": "generate_song",
@@ -327,8 +356,8 @@
327
  "output_index": 2
328
  }
329
  ],
330
- "x": 720,
331
- "y": 300,
332
  "width": 280,
333
  "height": 300,
334
  "data": {}
@@ -471,56 +500,40 @@
471
  }
472
  ],
473
  "edges": [
474
- {
475
- "id": "e_desc",
476
- "from_node_id": "ref_description",
477
- "from_port_id": "out",
478
- "to_node_id": "op_compose",
479
- "to_port_id": "in_0",
480
- "type": "text"
481
- },
482
- {
483
- "id": "e_dur_c",
484
- "from_node_id": "ref_duration",
485
- "from_port_id": "out",
486
- "to_node_id": "op_compose",
487
- "to_port_id": "in_1",
488
- "type": "number"
489
- },
490
  {
491
  "id": "e_lyr",
492
- "from_node_id": "op_compose",
493
- "from_port_id": "out_0",
494
  "to_node_id": "op_generate",
495
  "to_port_id": "in_0",
496
  "type": "text"
497
  },
498
  {
499
  "id": "e_gm",
500
- "from_node_id": "op_compose",
501
- "from_port_id": "out_1",
502
  "to_node_id": "op_generate",
503
  "to_port_id": "in_1",
504
  "type": "text"
505
  },
506
  {
507
  "id": "e_vd",
508
- "from_node_id": "op_compose",
509
- "from_port_id": "out_2",
510
  "to_node_id": "op_generate",
511
  "to_port_id": "in_2",
512
  "type": "text"
513
  },
514
  {
515
  "id": "e_arr",
516
- "from_node_id": "op_compose",
517
- "from_port_id": "out_3",
518
  "to_node_id": "op_generate",
519
  "to_port_id": "in_3",
520
  "type": "text"
521
  },
522
  {
523
- "id": "e_dur_g",
524
  "from_node_id": "ref_duration",
525
  "from_port_id": "out",
526
  "to_node_id": "op_generate",
 
1
  {
2
  "schema_version": "2",
3
  "name": "MiniMax Music 3",
4
+ "description": "Full-song generation with MiniMax Music 3 on ZeroGPU: tagged lyrics + a structured caption (Global metadata / Vocal details / Arrangement, per the official prompting guide) feed the diffusers pipeline (AR frames -> windowed DiT decode -> vocoder), plus an ffmpeg share-video visualizer. Edit the Lyrics and caption reference nodes to shape the song.",
5
  "runtime": {
6
  "default": "client"
7
  },
 
10
  },
11
  "references": [
12
  {
13
+ "id": "ref_lyrics",
14
+ "label": "Lyrics",
15
  "role": "reference",
16
  "asset_type": "text",
17
  "inputs": [
18
  {
19
  "id": "in",
20
+ "label": "Lyrics",
21
  "type": "text"
22
  }
23
  ],
24
  "outputs": [
25
  {
26
  "id": "out",
27
+ "label": "Lyrics",
28
  "type": "text"
29
  }
30
  ],
31
  "x": 40,
32
+ "y": 80,
33
+ "width": 300,
34
+ "height": 200,
35
+ "data": {
36
+ "out": "[intro]\n\n[verse]\nRiding on a beam of light tonight\nEvery little star is burning bright\n[pre-chorus]\nHold your breath, the sky is opening\n[chorus]\nWe are made of sound and time\nEvery heartbeat keeps the rhyme\n[outro]"
37
+ }
38
+ },
39
+ {
40
+ "id": "ref_global",
41
+ "label": "Global Metadata",
42
+ "role": "reference",
43
+ "asset_type": "text",
44
+ "inputs": [
45
+ {
46
+ "id": "in",
47
+ "label": "Global Metadata",
48
+ "type": "text"
49
+ }
50
+ ],
51
+ "outputs": [
52
+ {
53
+ "id": "out",
54
+ "label": "Global Metadata",
55
+ "type": "text"
56
+ }
57
+ ],
58
+ "x": 40,
59
+ "y": 310,
60
+ "width": 300,
61
+ "height": 160,
62
+ "data": {
63
+ "out": "Basic Attributes: bpm is 120. key is C, and scale is major. Synth-Pop / Electropop. Global Emotional Progression: The track opens in shimmering anticipation, a filtered pulse like city lights coming on at dusk. The verse glides forward with hopeful momentum, the pre-chorus holds its breath as the arrangement tightens and rises, and the chorus bursts open into wide-screen euphoria \u2014 bright, weightless, celebratory. The outro drifts back down into a starry afterglow, ending on air and quiet wonder. Application Scenarios & Imagery: a night drive under neon overpasses with the windows down; a planetarium dome igniting as the lights dim; a rooftop countdown at midnight. Sonics & Production Profile: a polished, modern pop mix with a wide stereo image \u2014 airy sparkling highs, present mid-range vocals, and a tight, punchy low end; side-chained compression gives the chorus a gentle pumping lift, and the outro dissolves into long reverb tails."
64
+ }
65
+ },
66
+ {
67
+ "id": "ref_vocals",
68
+ "label": "Vocal Details",
69
+ "role": "reference",
70
+ "asset_type": "text",
71
+ "inputs": [
72
+ {
73
+ "id": "in",
74
+ "label": "Vocal Details",
75
+ "type": "text"
76
+ }
77
+ ],
78
+ "outputs": [
79
+ {
80
+ "id": "out",
81
+ "label": "Vocal Details",
82
+ "type": "text"
83
+ }
84
+ ],
85
+ "x": 40,
86
+ "y": 500,
87
+ "width": 300,
88
+ "height": 140,
89
  "data": {
90
+ "out": "Vocal Gender & Timbre: Singer A (Female), a warm mezzo-soprano with an intimate, breathy texture in her low register and a clear, ringing brightness when she lifts. Vocal Style: soft and close-miked through the verse, phrasing like a secret; the pre-chorus rises with held, urgent notes, and the chorus opens into a confident, soaring belt with sustained tones riding the beat; over the outro she dissolves into wordless, airy ad-libs echoing the chorus melody. Harmony/Backing Vocals: a single ghost double shadows the pre-chorus; stacked parallel harmonies in thirds widen the chorus into a glowing wall; the verse stays solo and intimate. Vocal FX: light plate reverb throughout, tempo-synced delay throws on chorus line endings, subtle saturation for chorus presence, and a longer, washier reverb on the outro ad-libs."
91
+ }
92
+ },
93
+ {
94
+ "id": "ref_arrangement",
95
+ "label": "Arrangement",
96
+ "role": "reference",
97
+ "asset_type": "text",
98
+ "inputs": [
99
+ {
100
+ "id": "in",
101
+ "label": "Arrangement",
102
+ "type": "text"
103
+ }
104
+ ],
105
+ "outputs": [
106
+ {
107
+ "id": "out",
108
+ "label": "Arrangement",
109
+ "type": "text"
110
+ }
111
+ ],
112
+ "x": 40,
113
+ "y": 670,
114
+ "width": 300,
115
+ "height": 160,
116
+ "data": {
117
+ "out": "Instrument Lifecycle Description (Primary/Secondary Layering): Primary: a round, side-chained analog-style synth bass anchors the harmony from the first verse through the chorus, under a soft pad bed that opens the intro and never fully leaves. Secondary: a shimmering arpeggio enters at the pre-chorus and runs through the chorus; wide analog pads and a bright synth counter-melody appear only in the chorus to lift it; a sparse felt piano takes over the outro as the synths fall away. Groove & Foundation Progression: the intro pulses on a filtered four-on-the-floor kick; the verse keeps drums minimal \u2014 kick, soft clap, ticking closed hat; the pre-chorus adds open hats and a rising snare build, and the chorus lands with the full kit: punchy kick on every beat, layered claps, driving crash accents. After the chorus the drums drop out entirely, leaving piano, pad, and air for the outro. Embellishments, Textures & Spatial FX: a white-noise riser and reverse swell launch the chorus; glittering bell accents answer the vocal there; and the final piano chord rings into a long, starlit reverb wash."
118
  }
119
  },
120
  {
 
137
  }
138
  ],
139
  "x": 40,
140
+ "y": 860,
141
  "width": 200,
142
  "height": 90,
143
  "data": {
 
163
  "type": "number"
164
  }
165
  ],
166
+ "x": 260,
167
+ "y": 860,
168
  "width": 200,
169
  "height": 90,
170
  "data": {
 
191
  }
192
  ],
193
  "x": 40,
194
+ "y": 980,
195
  "width": 200,
196
  "height": 90,
197
  "data": {
 
217
  "type": "number"
218
  }
219
  ],
220
+ "x": 260,
221
+ "y": 980,
222
  "width": 200,
223
  "height": 90,
224
  "data": {
 
245
  }
246
  ],
247
  "x": 40,
248
+ "y": 1100,
249
  "width": 200,
250
  "height": 90,
251
  "data": {
 
281
  }
282
  ],
283
  "operators": [
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
284
  {
285
  "id": "op_generate",
286
  "label": "generate_song",
 
356
  "output_index": 2
357
  }
358
  ],
359
+ "x": 560,
360
+ "y": 400,
361
  "width": 280,
362
  "height": 300,
363
  "data": {}
 
500
  }
501
  ],
502
  "edges": [
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
503
  {
504
  "id": "e_lyr",
505
+ "from_node_id": "ref_lyrics",
506
+ "from_port_id": "out",
507
  "to_node_id": "op_generate",
508
  "to_port_id": "in_0",
509
  "type": "text"
510
  },
511
  {
512
  "id": "e_gm",
513
+ "from_node_id": "ref_global",
514
+ "from_port_id": "out",
515
  "to_node_id": "op_generate",
516
  "to_port_id": "in_1",
517
  "type": "text"
518
  },
519
  {
520
  "id": "e_vd",
521
+ "from_node_id": "ref_vocals",
522
+ "from_port_id": "out",
523
  "to_node_id": "op_generate",
524
  "to_port_id": "in_2",
525
  "type": "text"
526
  },
527
  {
528
  "id": "e_arr",
529
+ "from_node_id": "ref_arrangement",
530
+ "from_port_id": "out",
531
  "to_node_id": "op_generate",
532
  "to_port_id": "in_3",
533
  "type": "text"
534
  },
535
  {
536
+ "id": "e_dur",
537
  "from_node_id": "ref_duration",
538
  "from_port_id": "out",
539
  "to_node_id": "op_generate",