Nodes/ComfyUI MIDI Edit/MIDI Synthesize Audio
ComfyUI Node

MIDI Synthesize Audio

The payoff node — make your edited MIDI JSON actually sing in a reference voice

By ahkimkoo·Created 3 months ago·Updated about a month ago· 5
MIDI Synthesize Audio
  • prompt_audio
  • audio
midi_json
prompt_metadata
controlmelody
seed12306
auto_shifttrue
pitch_shift0
use_fp16false
cfg3.0
n_steps32

This is where the pack earns its keep. Every other node edits data; this one produces audio. Feed it the edited MIDI JSON (what you want sung) plus a reference audio clip (whose voice you want it sung in), and SoulX-Singer's flow-matching diffusion model renders actual singing. It's the "魔改歌词" - lyrics-remix - payoff, and honestly the whole reason anyone installs this thing.

Inputs that matter

The two required inputs are the whole show:

  • midi_json - the target: lyrics, phonemes, duration, pitch, and f0. Usually the output of MIDI Transcribe AudioMIDI Edit Lyrics or MIDI Lyrics Alignment.
  • prompt_audio - the reference voice that supplies the timbre.

Then the optional ones, roughly in order of how often you'll touch them:

  • prompt_metadata - recommended even though it's optional. It's the midi_json from running MIDI Transcribe Audio on your reference audio. Leave it blank and the node preprocesses the reference internally, which means re-running the whole pipeline on every single render. Pre-transcribe once, cache the string, reuse. The README also ties this to fixing "mumbled diction" - more on that below.
  • control - melody (default) or score. melody uses the actual f0 contour, which is right when your target came from a real vocal (lyrics edited from a transcribed song) - you get timbre closer to the reference. score uses MIDI note pitches: clearer diction, but further from the reference voice. Pick score when the target is a score or instrumental with no reliable f0.
  • cfg - classifier-free guidance, default 3.0. In melody mode, if words get swallowed or the diction turns to mush, raise it toward 4–5 for stronger adherence to the lyric phonemes. Too high and you get over-saturation and artifacts - the author says don't push past 5.
  • n_steps - flow-matching reverse-diffusion steps, default 32, range 8–128. Higher slightly improves quality and linearly slows you down. Leave it alone.
  • use_fp16 - default OFF (FP32), which matches the reference implementation and is safest for quality. Turn ON for autocast speed on GPU if VRAM is tight.
  • seed (default 12306), auto_shift (default ON, auto-aligns target pitch to the reference's range), and pitch_shift (global semitone shift, −36 to +36) are the rest. Set seed for reproducible renders, ignore the others unless you know why you're changing them.

Output is a single audio AUDIO - wire it to Save Audio or a preview node.

What it needs installed

The ~2.6 GB Soul-AILab/SoulX-Singer model has to be in ComfyUI/models/, and OpenAI's whisper-base (used as the audio encoder) downloads itself to the HuggingFace cache on first run. Both come down with:

cd ~/App/ComfyUI/models
huggingface-cli download Soul-AILab/SoulX-Singer --local-dir Soul-AILab/SoulX-Singer

If the whole pack fails to load, it's almost always the git submodule: the SoulX-Singer engine is vendored as a submodule and cloning without --recursive leaves SoulX-Singer/ empty. Fix with git submodule update --init --recursive in the custom_nodes folder.

Troubleshooting grounded in the changelog

  • Mumbled / unclear diction (口齿不清). The author's fix is the prompt_metadata path - transcribe the reference audio once and feed its midi_json in. If you're still getting mush in melody mode, nudge cfg to 4–5.
  • Off-key or "turning into a male voice" in score mode. This was a real, fixed bug: ROSVOT occasionally quantizes pitch wrong (up to ~4 semitones) at phrase-ending long notes, and long sentences could blow up token counts and break timbre transfer. Recent releases corrected pitch from f0 and cap sub-note splits per phrase. If you hit it, update the pack. melody mode is unaffected since it uses f0 directly.
  • rescale_cfg isn't a parameter. Right - upstream SoulXSinger.infer doesn't expose it, so it's hardcoded at 0.75. Nothing to configure.

And manage expectations: this is 32 steps of diffusion over an entire song. It's not instant, and a full song render is a "go make tea" operation, not a "grab a coffee" one.

CategoryMIDI-SoulX

Inputs (10)

NameTypeDefaultDescription
midi_jsonSTRING
prompt_audioAUDIO
prompt_metadataoptSTRING
controloptCOMBOmelody2 options: melody, score
seedoptINT123060–2147483647
auto_shiftoptBOOLEANtrue
pitch_shiftoptINT0-36–36
use_fp16optBOOLEANfalse
cfgoptFLOAT3.01–10
n_stepsoptINT328–128

Outputs (1)

NameTypeDescription
audioAUDIO