Nodes/Kinburg-Nodes/Siren Score (Lyrics β†’ Plan) 🧜
ComfyUI Node

Siren Score (Lyrics β†’ Plan) 🧜

Stop Letting the LLM Decide Your Song Structure β€” Siren Score Reads the Lyrics

By KinburgΒ·Created 3 months agoΒ·Updated 6 days agoΒ· 1
Siren Score (Lyrics β†’ Plan) 🧜
  • voice_1
  • voice_2
  • plan
  • report
β—„lyricsβ€”β–Ί
β—„bpm120β–Ί
β—„timesignature4β–Ί
β—„pad_to_seconds150β–Ί
β—„tail_bars4β–Ί
β—„pad_placementafter the vocalsβ–Ί
β—„pad_block_bars16β–Ί
β—„min_bars4β–Ί
β—„instrumental_bars4β–Ί
β—„duetslead + split on inner markersβ–Ί
β—„arrangement_notesfalseβ–Ί
β—„verbosetrueβ–Ί

If you've ever asked an LLM to write a song plan for ACE-Step and watched it come back with a four-minute structure for a two-minute lyric, you already know why this node exists. Siren Score builds Siren Cast's plan deterministically from the lyrics themselves - the section list, the order, the voice, and each section's length - so the song is exactly as long as its words. No language model in the loop, no bar budget that quietly invents forty bars of instrumental behind a six-section lyric.

ACE-Step is the local Suno (runs on a 4GB card, instrumentals genuinely good, vocals the weak spot - that's the community consensus, and it's why the more you control the vocal plan, the better your take). Siren Cast is the node that turns a plan into captions; Siren Score is what writes that plan when you want the text to be the boss.

How it works

Everything the plan needs is already in the text. A bracketed line whose first word is a section name - [Verse 1 - Nina], [Chorus - powerful FEMALE belt] - opens a section. Other bracketed lines are just annotations of the current section, which is where a voice often hides. Each section's length is derived from its line count, then the whole thing is sized backwards from your target: pad_to_seconds says how long the song is, tail_bars takes its slice off the end, instrumental-only markers get instrumental_bars, and everything left is shared among the sung sections in proportion to their syllables.

That "length as input, not rate as input" inversion is the whole trick. If you dial a singing rate and let the length be whatever's left over, the slack hides inside the vocal sections - and a section with more room than its words need doesn't get sung slower, the model just fills it. That's how a 6-second intro becomes a 40-second one that eats the first verse. Run length backwards and slack can only land where you asked for it.

The inputs you'll actually touch

  • lyrics - wire the same text that goes into Siren Cast. Markers need the section name first, then the member's name (or MALE/FEMALE), then how they sing it. A marker that names a member splits the section into alternating sub-sections - the one form of duet ACE-Step actually does well. Two names in a header with nothing to split on become lead plus a short backing note.
  • pad_to_seconds - the target length. The report prints the singing rate it came out at, and if that lands outside 2–8 syllables a second the node tells you and names the length your lyric actually suits.
  • tail_bars - 2–8 bars of outro is a clean ending; a long tail is actively bad. The model repeats the last phrase to fill it.
  • pad_placement - after the vocals is the safe default, intro + outro sounds more like a record, between sections is the most song-shaped but the only one that can make the lyric drift.
  • Optional voice_1 / voice_2 - wire the same Character Cards you give Siren Cast, and marker names resolve against a real roster.

Install & issues

Install the whole pack once: ComfyUI Manager (search "Kinburg-Nodes"), or:

cd ComfyUI/custom_nodes
git clone https://github.com/Kinburg/Kinburg-Nodes

Restart ComfyUI. No extra dependencies for this node. The one thing that trips people up: the section name must be the first word in the marker. [Final Chorus - …] and [Guitar Solo] aren't sections here - but the report catches the near miss and hands back the corrected line. If a take comes back muddy or sung by one indistinct voice, turning arrangement_notes off is the cheapest thing to try. Outputs are plan (wire into Siren Cast) and report (read it - it tells you what it decided and what it couldn't).

CategoryKinburg-Nodes/Bestiary/Siren

Inputs (14)

NameTypeDefaultDescription
lyricsSTRINGThe lyrics, with '[Verse 1 - ...]' style markers. The SAME text that goes to Siren Cast β€” wire one source into both. What the markers need: the section name FIRST (Intro / Verse 1 / Pre-Chorus / Chorus / Post-Chorus / Bridge / Solo / Break / Outro, or a synonym), then ideally the member's name, then how they sing it. 'MALE'/'FEMALE' works instead of a name when the roster has exactly one of each. A bracketed line that NAMES a member splits the section β€” that is how an exchange between two singers is written, and it is the only way the model can actually do two voices. Any other bracketed line is an annotation of the current section.
bpmINT12010–300Tempo β€” only used to report the resulting length in seconds. The plan itself is written in bars, so Siren Cast converts it with ITS bpm; wire both from one source or the report will lie to you.
timesignatureSTRING4Beats per bar, for the same reason as 'bpm'. A plain field rather than a dropdown because a combo input cannot accept the STRING a text parser hands it β€” anything with a digit in it is read (4, '4', '4/4').
pad_to_secondsFLOAT15010–2000How long the song should be, in seconds β€” the reference the whole plan is built from. The bars it buys are shared out like this: instrumental sections (a marker with no sung lines) take 'instrumental_bars' each, 'tail_bars' comes off the end, and everything left goes to the sung sections IN PROPORTION TO THEIR SYLLABLES. So the singing rate is not a dial here, it is a consequence β€” the report prints what it came out at. If that lands outside 2-8 syllables a second the node says so and names the length this lyric would actually suit, because a section given more room than its words need does not get sung slower: the model FILLS it, and at the top of a song that reads as the intro running on.
tail_barsCOMBO4Bars of instrumental added after the vocals, so the track does not stop dead on the last word. 0 = none. Measured with 'lyrics_in_negative' off, which is the setting that makes the voices land: a SHORT tail (2-8 bars) gives a last chorus, a proper outro and a clean ending. A LONG one is actively bad β€” the model repeats the last phrase over and over to fill it, and starts eating the ends of notes the singer should be holding. It also lengthens the intro, which is the other thing to listen for. This replaced a target-duration dial. That one made the tail whatever was left over between the lyrics and the target, which is how a six-section lyric ended up with forty bars of instrumental behind it.
pad_placementCOMBOafter the vocalsWhere the instrumental blocks go. The trade-off is not taste β€” it is where a lyric can be interrupted. AceStep gets the words with NO timing in them, matched against the plan, so a gap in the middle asks the model to hold the line until the singing resumes; if it doesn't, everything after it shifts. β€’ after the vocals β€” everything at the end. The least song-shaped, and the only one with a take behind it, which is why it is the default. β€’ intro + outro β€” a third opens the record, the rest is an outro jam. Still entirely outside the lyric, so it carries the same zero risk and sounds more like a record. Try this second. β€’ between sections β€” one block opens, the rest go after choruses and bridges (never inside a verse running into its own pre-chorus), remainder at the end. The most song-shaped and the only one that can make the lyric drift.
pad_block_barsINT162–64Longest instrumental section the padding may use. The tail is split into as FEW rows as it takes, each at most this, sharing the bars out evenly β€” 40 bars at 16 becomes 14 + 13 + 13, not twenty rows of 2. Why it matters: every row is its own LM decode with its own caption, so a tail chopped into 2-bar rows is a restart every ten audio codes β€” no room for the model to develop anything, and a seam at each one. Merging them by hand was the first thing that sounded better, so this does it by default. Raise it for one long jam, lower it for a tail that changes character more often.
min_barsINT42–32Floor for a whole section. Below about 4 bars a section is too short for a voice to establish itself. Sub-sections made by inner markers are exempt β€” an exchange of single shouted lines is meant to be short, and they get a floor of 2 bars instead.
instrumental_barsINT42–32Length for a section with no sung lines at all (an instrumental intro, a solo, a break). There is no line count to derive it from, so it is simply this.
duetsCOMBOlead + split on inner markersWhat to do with a section header that names more than one singer AND has no inner markers to split on. The plan carries ONE audio code per 200 ms and the caption is one description, so two timbres over the same frames come back as their average β€” measured once as 'two female vocals' where a man and a woman were asked for. This does NOT govern inner markers. A bracketed line naming a member always starts a sub-section, in every mode, so the voices ALTERNATE β€” that is an explicit instruction in the lyrics and the one form of duet the model genuinely does well. It used to be switched off by this setting, which meant a song could have alternation or a unison chorus but never both, and the loss was silent. β€’ lead + split on inner markers (recommended) β€” an unsplittable header duet becomes the first-named singer plus a short backing note carrying the other's own timbre. β€’ both names in one section β€” both names go into the cell and 'Siren Cast' decides what to do with them, which is where the unison mode lives. Use this when a section is meant to be sung TOGETHER.
arrangement_notesBOOLEANfalsePut the non-vocal part of a marker into the plan's 4th column, which appends it to that section's caption β€” '[Chorus - massive explosion of sound, wall of distorted guitars]' is a real instruction about the chorus and this is the only place it fits. Everything here lands inside Siren Cast's cfg delta, so it is guided as hard as the voice is: at most 4 clauses per section are kept and the rest is reported. If a take comes back muddy or sung by one indistinct voice, turning this OFF is the cheapest thing to try.
verboseBOOLEANtruePrint the report to the console. The same text is always on the 'report' output.
voice_1optKINBURG_VOICEA band member β€” Character Card's 'voice' output. Wire the SAME cards you wire into Siren Cast: this node matches marker names against them and reads their gender when a marker only says MALE / FEMALE. With nothing wired the markers' own wording is used as the vocal description, which still works β€” it just can't say 'Nina', and inner markers can't split a section.
voice_2optKINBURG_VOICEβ€”

Outputs (2)

NameTypeDescription
planSTRINGβ€”
reportSTRINGβ€”