SemBridge Score Preview
The free sanity check before you spend GPU time on a score
- notes_json
- token_string
- summary
Singing models don't take lyrics. They take a score - every syllable already pinned to a MIDI pitch and a duration - and if your score is malformed or your note lengths are nonsense, you find out by waiting through a generation that sounds like a cat falling downstairs. Score Preview is the node that tells you in half a second, before any model touches the GPU.
It loads nothing. No safetensors, no torch, no VRAM. It parses your score with the same pure-Python parser the Singing node uses, then formats the exact token string the model would receive, so what you see here is what the model sees.
Why this exists
The audio-generation landscape has plenty of text-to-speech and a decent amount of music, but singing voice synthesis is the corner almost nobody writes about - searching recent discussion for the phrase "singing voice" turns up a couple of dozen threads against hundreds for plain TTS. So there's no folklore to fall back on when a sung render comes out wrong. You get to debug it yourself.
Which is why a validator is worth a whole node. The score format is terse and unforgiving: lyric pitch seconds per line. One missing field and the parser rejects everything; one accidental 15-second note and the model can't encode it at all. Catching that before a two-minute autoregressive run is the entire value proposition.
What goes in
One input, score, multiline, defaulting to a four-line demo:
la 60 0.5
la 62 0.5
la 64 1.0
<SP> 0 0.4
Pitch is a MIDI note number (60 is middle C), seconds is the note length, and <SP> with pitch 0 is a rest. Lines starting with # are ignored, so you can annotate. A JSON list of {text, pitch, duration, note_type} works too, including a whole official request object with a notes key - useful when you're porting an example from the upstream repo.
The parser is stricter than it looks, in helpful ways: pitch must land in 0–128, duration must be positive, a single note can't reach 12.5 seconds, and durations get quantised to 25 ms because that's the model's frame grid. Errors come back with a line number - score line 3: expected 'lyric pitch duration [note_type]' - so you know where to look instead of re-reading five hundred notes.
What comes out
Three string outputs, and they're for different jobs.
notes_json is your score as canonical JSON - the same note list in the same order, ready to paste into the Singing or Speech + Singing node's JSON form when you'd rather assemble it programmatically. token_string is the formatted token sequence the model consumes; hand that to prompt_text when you're using a sung clip as a reference voice, because a sung reference needs its score text, not its lyrics. summary is a small JSON blob with the note count, the total score length in seconds, and the minimum and maximum patch limits that length_control = from_score would derive from it.
That last one is quietly the most useful. One patch is 80 ms, and from_score sets generation bounds to 0.5× and 1.8× the score length in patches - the rule the four official singing examples use. Seeing those numbers tells you whether the Singing node will even have room to finish your phrase: a 6-second score caps out around 135 patches, about 10.8 seconds of audio.
They're STRING outputs, so nothing renders inside the node - wire them into a text-display node or read them off the API response. Most people use this as a check-then-bypass step.
Install
Same pack, same install, nothing extra. If ComfyUI-SemBridge is in custom_nodes and you've restarted, this node is already in audio/sembridge.
cd ComfyUI/custom_nodes
git clone https://github.com/hiroki-abe-58/ComfyUI-SemBridge
python -m pip install -r ComfyUI-SemBridge/requirements.txt
Score Preview needs neither the weights nor soundfile, and it works on a machine with no GPU at all, which makes it a decent first test that the pack installed correctly. There's a ready-made graph at workflows/gui/score_preview.json.
Where people get burned
It won't complain about musical nonsense. It validates structure and arithmetic, not taste. A score of sixteen notes all at MIDI 78 with wide leaps will pass here and still sound bad, because the pack's own testing found wide-leap scores in the high register (MIDI 72–79) missed by 2.4 to 4.2 semitones on every seed tried, while MIDI 48–72 stayed within about half a semitone. Stay in the lower half of the range and you'll have a much better time. No validator can save you from that.
Note type 3 does nothing on its own. It means "this note extends the previous lyric onto another pitch" - melisma. Give it to the first note of a score and there's no previous lyric to extend.
Still not a MIDI importer. Nothing here reads a .mid file, and no setting will make it. The model needs lyrics already aligned to notes, and a MIDI file has the notes without the syllables.
Inputs (1)
| Name | Type | Default | Description |
|---|---|---|---|
| score | STRING | la 60 0.5 la 62 0.5 la 64 1.0 <SP> 0 0.4 | — |
Outputs (3)
| Name | Type | Description |
|---|---|---|
| notes_json | STRING | — |
| token_string | STRING | — |
| summary | STRING | — |