Nodes/Music Production Toolkit/Instrumental check · keep least vocal take
ComfyUI Node

Instrumental check · keep least vocal take

That 'instrumental' still sang — keep the take with the fewest words

By jplenio·Created 19 days ago·Updated a day ago· 55
Instrumental check · keep least vocal take
  • candidate_0
  • candidate_1
  • candidate_2
  • candidate_3
  • candidate_4
  • candidate_5
  • candidate_6
  • candidate_7
  • candidate_8
  • candidate_9
  • candidate_10
  • audio
  • check_report_json
max_retries2
word_tolerance0
report_0
report_1
report_2
report_3
report_4
report_5
report_6
report_7
report_8
report_9
report_10

Here's the thing nobody tells you about instrumental covers: the score isn't the problem. In the toolkit's instrumental mode the vocal part is muted and its melody moves into the instrumental part, and that's verified on every run. If you still hear a voice, the audio model invented it. There is no setting that fixes that, which means the only honest move is to listen - and if you're queuing this overnight, something has to listen for you.

That's this node. It's the referee: it takes a pile of freshly generated takes, they each get transcribed, and it keeps the least vocal one.

How the choosing works

It doesn't render eleven songs and pick one. Every candidate input is declared lazy, which is ComfyUI's mechanism for letting a node demand its inputs one at a time rather than all up front. When the graph reaches this node it asks for candidate_0 and report_0, looks at the report, and if that take passed the tolerance it stops there. A clean first take costs exactly one generation.

If the first take had words, it asks for the next pair. max_retries (0–10, default 2) is how many extra takes it's allowed to request - so the default budget is three takes total, not eleven.

When nothing reaches the tolerance, the node does the thing I'd want a human to do: it keeps the take with the fewest recognised words, not the last one rendered. The word counts are already measured; throwing that measurement away because a loop ended would be silly. Ties go to the earliest take, which matters if you're using a fixed seed - the same seed reproduces the same pick. The losing takes' temporary WAVs get deleted, and the audio that continues downstream is the winner's original float data, not the 16-bit scratch copy.

Inputs and outputs

Only two inputs are yours to set:

  • max_retries - the extra-take budget. Each retry is a full re-render, so this is a straight time-for-luck trade. 2 is a sane default; 0 means "take the first one and live with it."
  • word_tolerance - how many recognised words still count as an instrumental. Words, not letters. 0 is strict: not one recognisable word. If you're getting a stray syllable at the tail and the rest is clean, 1 or 2 saves you a re-render.

The rest are the candidate_N / report_N pairs, 0 through 10. Note the report says "words heard", "transcript", pass/fail and the take's WAV path - read the transcript before you trust the count. Four words of actual lyric is a very different problem from four hallucinated words over a cymbal wash.

Outputs are audio - feed it wherever your decoded song went - and check_report_json, which lands in the generation record as instrumental_check_result.

You don't place this node

That's the part beginners trip on. This node is created inside the pack's generation expansion, along with its check nodes, when the instrumental_check toggle in the Music settings node is on. It only ever applies to YuE2 instrumental covers; every other mode and model ignores it byte for byte. Your job is to switch it on and set the retry budget and tolerance there - not to drag this node onto the canvas.

Takes live in <system temp>/mmt-instrumental-check/<take-N>-<random>/candidate.wav while the run is going, so you can audition a rejected take later. The kept file stays; the others go. If you cancel a run before this node executes, leftovers get pruned the next time the check runs, on a 24-hour rule.

Install

Nothing extra for this node beyond the pack:

cd ComfyUI/custom_nodes
git clone https://github.com/jplenio/ComfyUI-MiniMax-Music-Production-Toolkit.git
cd ComfyUI-MiniMax-Music-Production-Toolkit
python -m pip install -r requirements.txt

Restart ComfyUI afterwards and refresh the browser. If you installed the toolkit before 3.1 and deliberately skipped the Whisper engine, add it:

python -m pip install -r requirements-whisper.txt

Where it goes wrong

Instrumental vocal check: no candidate audio reached the selection node means the check node isn't wired to the candidate socket it's looking for - usually after you've lowered max_retries and the stored workflow didn't keep up. Rewire, or set the retries back.

Whisper hallucinating lyrics over music is real and it will make a harmless take look guilty. Look at the transcript, and if the same nonsense shows up on every take, raise word_tolerance rather than grinding through retries. And remember the whole feature is opt-in because transcription costs time - a clean render pays for one Whisper pass, a stubborn one pays for three.

CategoryMiniMax Music Production Toolkit/generation

Inputs (24)

NameTypeDefaultDescription
max_retriesINT20–10How many extra takes may be generated when a take still contains words (0-10). A clean first take costs one generation; every retry is a full re-render.
word_toleranceINT00–50How many recognised words still count as an instrumental. Words, not letters: 0 means the render must not contain a recognisable word at all.
candidate_0optAUDIOOne generated take (AUDIO), numbered like its report_N. Lazy: the take is only rendered when this node actually asks for it.
report_0optSTRINGWord-check report for candidate_N: words heard, transcript, pass/fail and the path of that take's temporary WAV. Lazy, like its candidate.
candidate_1optAUDIOOne generated take (AUDIO), numbered like its report_N. Lazy: the take is only rendered when this node actually asks for it.
report_1optSTRINGWord-check report for candidate_N: words heard, transcript, pass/fail and the path of that take's temporary WAV. Lazy, like its candidate.
candidate_2optAUDIOOne generated take (AUDIO), numbered like its report_N. Lazy: the take is only rendered when this node actually asks for it.
report_2optSTRINGWord-check report for candidate_N: words heard, transcript, pass/fail and the path of that take's temporary WAV. Lazy, like its candidate.
candidate_3optAUDIOOne generated take (AUDIO), numbered like its report_N. Lazy: the take is only rendered when this node actually asks for it.
report_3optSTRINGWord-check report for candidate_N: words heard, transcript, pass/fail and the path of that take's temporary WAV. Lazy, like its candidate.
candidate_4optAUDIOOne generated take (AUDIO), numbered like its report_N. Lazy: the take is only rendered when this node actually asks for it.
report_4optSTRINGWord-check report for candidate_N: words heard, transcript, pass/fail and the path of that take's temporary WAV. Lazy, like its candidate.
candidate_5optAUDIOOne generated take (AUDIO), numbered like its report_N. Lazy: the take is only rendered when this node actually asks for it.
report_5optSTRINGWord-check report for candidate_N: words heard, transcript, pass/fail and the path of that take's temporary WAV. Lazy, like its candidate.
candidate_6optAUDIOOne generated take (AUDIO), numbered like its report_N. Lazy: the take is only rendered when this node actually asks for it.
report_6optSTRINGWord-check report for candidate_N: words heard, transcript, pass/fail and the path of that take's temporary WAV. Lazy, like its candidate.
candidate_7optAUDIOOne generated take (AUDIO), numbered like its report_N. Lazy: the take is only rendered when this node actually asks for it.
report_7optSTRINGWord-check report for candidate_N: words heard, transcript, pass/fail and the path of that take's temporary WAV. Lazy, like its candidate.
candidate_8optAUDIOOne generated take (AUDIO), numbered like its report_N. Lazy: the take is only rendered when this node actually asks for it.
report_8optSTRINGWord-check report for candidate_N: words heard, transcript, pass/fail and the path of that take's temporary WAV. Lazy, like its candidate.
candidate_9optAUDIOOne generated take (AUDIO), numbered like its report_N. Lazy: the take is only rendered when this node actually asks for it.
report_9optSTRINGWord-check report for candidate_N: words heard, transcript, pass/fail and the path of that take's temporary WAV. Lazy, like its candidate.
candidate_10optAUDIOOne generated take (AUDIO), numbered like its report_N. Lazy: the take is only rendered when this node actually asks for it.
report_10optSTRINGWord-check report for candidate_N: words heard, transcript, pass/fail and the path of that take's temporary WAV. Lazy, like its candidate.

Outputs (2)

NameTypeDescription
audioAUDIO
check_report_jsonSTRING