Satyr Score (Plan β Plan) π
Which verse your YuE2 plan isn't going to sing
- voice_1
- voice_2
- abc
- lyrics
- report
Your plan and your lyrics are two separate chunks of text that YuE2 has to reconcile itself, and it does that by merging neighbours, inserting sections that carry no words, and renaming what's left by its own reading - a first [Chorus] can come back labelled % pre-chorus. Satyr Score checks one against the other and tells you what the render will actually do with your words. It reads far more than it writes, which turned out to be a finding rather than a design choice: an earlier version rewrote every phrase, and the songs came back sparse and sung by one voice.
The matching is the clever part
The obvious approach - pair lyric block one with section one - puts somebody else's lyrics under nearly every section, because the model's sections don't line up with anything you wrote: it merged a 63-syllable verse with the 47-syllable pre-chorus that followed into one 112-note section.
So the node matches by counts instead. The plan's note count is the syllable budget, and the model writes roughly one note per syllable - 1.03 across a measured song, exactly 47/47 on one section.
What the report says
- Which block landed where, so a merge or a dropped section is visible rather than surprising.
- A block the plan can't sing in full - a bridge given 17 notes for 39 syllables loses well over half its words.
- A block the plan won't sing at all - a measured plan gave an 8-syllable outro no notes whatsoever.
- Which register each section uses, which tracks who sings it without deciding it.
- A section whose register contradicts its marker - the one case any edit can act on.
Read that report before spending a render. Most of what goes wrong with a plan can't be edited out later; it can only be caught early.
Inputs and outputs
abc- the plan, fromYuE2 Generate ABCor Satyr Import.lyrics- with[Verse 1 - Keen Burg]style markers: section name first, then the member's name. A bracketed line that names a member inside a section starts an exchange from that point; a line wholly in round brackets is a backing vocal, and gets a phrase of its own in the other singer's register.recast- off by default. Moves a whole section whose register contradicts its marker, by whole octaves only, and takes the move back if it narrows the gap between the registers.refit- off by default, and the default is the recommendation.keep_markers- on by default; passes the lyrics through byte for byte, markers and all.verbose- print the report to the console.
The two optional inputs, voice_1 and voice_2, take a Character Card's voice output. The cards are ranked on one ladder of voice types (soprano β¦ bass) and the upper half takes the upper band - relative, because the same tenor belongs above a bass and below a soprano. With fewer than two wired, no phrase is recast.
Out comes abc (the plan, with unchanged lines byte-identical so a diff shows only what moved), lyrics - wire this one into YuE2 Generate Music, not your original text - and report.
Why both switches are off
Both defaults are the recommendation.
recast exists because of a real, clean result: one chorus moved down an octave came back sung by the other singer at a fixed seed, with the untouched chorus staying female. Every attempt to use it as a lever since has failed, because a style naming no voices is sung by one singer throughout however the plan is written. On one measured plan every section already sat where its marker asked, and the right number of edits was zero.
refit cuts each phrase to as many notes as its words have syllables, which sounds right and behaves badly. Removing a note conserves the bar it lives in, so it doesn't free time, it stretches the notes that remain: a real run lost 96 notes, pushed the vocal line from 50% silence to 57%, and came back sounding like the singer was labouring through it. What the model does on its own is sing at a natural rate and rest the remainder of the bar. To shorten a song, cut bars - that's Satyr Trim's job.
keep_markers is on because listening beat reasoning. The markers were stripped at first, on the grounds that YuE2 has no field for a singer and its own guidance says to keep instructions out of the lyrics. In practice the model reads them and takes the performance from them: "powerful belts", "deep growl" come back as strain and intensity. Turn it off only if your lyrics carry stage directions in round brackets, which YuE2 reads as a backing vocal and sings aloud.
Installing it
Part of Kinburg-Nodes: ComfyUI Manager β Kinburg-Nodes, or:
cd ComfyUI/custom_nodes
git clone https://github.com/Kinburg/Kinburg-Nodes
Restart, and that's it - nothing extra to download. The voice inputs come from the pack's own Character Card nodes, so a band member is described once and drives the lyrics, the cover art prompt and the song.
Where people get burned
Turning on recast or refit hoping to fix a one-voice take. Neither can, for reasons the pack documents at unusual length - the singer is chosen in the style string. And the round-bracket trap: a stage direction like (distorted bass) isn't ignored by YuE2, it's sung, so strip the markers for those lyrics or move them into square brackets.
Inputs (8)
| Name | Type | Default | Description |
|---|---|---|---|
| abc | STRING | The plan, from 'YuE2 Generate ABC' or pasted in by hand. It is read, not trusted: the header gives the meter, unit and tempo, and everything below is indexed by section and phrase. Lines this node does not decide to change come back byte for byte, so a diff of a run shows the phrases that moved and nothing else. | |
| lyrics | STRING | The lyrics with '[Verse 1 - Keen Burg]' style markers β the same format Siren Score reads. The section name comes FIRST, then the member's name. A bracketed line that NAMES a member inside a section starts an exchange from that point, and a line wholly in ROUND brackets is a backing vocal: it gets a phrase of its own in the OTHER singer's register, which is how the model writes one when it bothers to. Wire the 'lyrics' OUTPUT of this node into YuE2. By default it is this text unchanged: the markers are worth keeping, because the model takes the performance from them. | |
| recast | BOOLEAN | true | Move each section whose register contradicts its marker by whole octaves, so the plan agrees with the lyrics. ON by default. With a LoRA on the text encoder this is what decides who sings. At one seed, a plan that contradicted its markers in five sections came back in ONE voice; the same plan with those five moved was sung by exactly the singers marked, its melody, rhythm and arrangement unchanged. On the base model the same correction turned one section of four β register alone is not what it listens to. It moves whole sections only, with the pickup each one opens on, and takes the move back if it narrows the gap between the registers. |
| refit | BOOLEAN | false | Cut each phrase to as many notes as its words have syllables. OFF by default, and the default is the recommendation. The idea is sound β the plan's note count really is the syllable budget, a measured chorus of 47 syllables had been written 47 notes β but the edit is not. Removing a note conserves the bar it lives in, so it does not free time, it STRETCHES the notes that remain: a real run lost 96 notes, pushed the vocal line from 50% silence to 57%, and came back sounding like the singer was labouring through it. What the model itself does is sing a line at a natural rate and rest the remainder of the bar. Until this works the same way, leave it off and shorten the song by cutting bars instead. It is kept for the case where a phrase is far too short for its line and you would rather have the words than the rhythm. |
| keep_markers | BOOLEAN | true | Pass the lyrics to the 'lyrics' output exactly as written, markers and all. ON by default, because that is what sounded better. They were being stripped on the reasoning that YuE2 has no field for a singer and its own guidance says to keep instructions out of the lyrics. Listening says otherwise: the model reads them and takes the PERFORMANCE from them β 'powerful belts', 'deep growl', 'vocal duel, intense emotional peak' come back as strain and intensity, and stripping them flattens the take. With a LoRA on the text encoder they also decide who sings where, once 'recast' has made the plan agree with them. Turn it off if your lyrics carry stage directions in ROUND brackets: YuE2 reads those as a backing vocal and sings them aloud. Square brackets are safe either way. |
| verbose | BOOLEAN | true | Print the report to the console. The same text is always on the 'report' output. |
| voice_1opt | KINBURG_VOICE | A band member β a Character Card's 'voice' output. Wire the same cards the markers name. The cards decide which band each singer gets: they are ranked on one ladder of voice types (soprano β¦ bass, with a bare 'female'/'male' sitting between the ranges each usually covers) and the upper half takes the upper band. That has to be relative β the same tenor belongs above a bass and below a soprano. With fewer than two wired there is nothing to alternate, and no phrase is recast. | |
| voice_2opt | KINBURG_VOICE | β |
Outputs (3)
| Name | Type | Description |
|---|---|---|
| abc | STRING | β |
| lyrics | STRING | β |
| report | STRING | β |