Nodes/ComfyUI_zura_nodes/Zura · Voice Guidance
ComfyUI Node

Zura · Voice Guidance

Pitch-shift the voice so the model stops copying it

By ZURAVFX·Created about a month ago·Updated a day ago· 2
Zura · Voice Guidance
  • original_audio
  • isolated_vocals
  • AUDIO
◄pitch_vocalsfalse►
◄semitones3.0►

GenjVoiceGuidance ("Zura · Voice Guidance") is a three-wire audio node with a very specific job: take the performance audio you're using as a guidance track and shift its pitch, so the model is guided by speech timing and delivery rather than by a clean copy of one particular person's voice.

It's the least glamorous node in the pack and one of the more thoughtful. In the shipped prepare/review graph it sits under the title "Optional: isolate vocals and shift +3 semitones", between the vocal separators and the guidance audio input of GenjSaveReview.

How it works

If pitch_vocals is off, the node is a pass-through: whatever comes in on original_audio comes out untouched. That's the default, and for most clips it's fine.

Turn it on and it does the following:

  1. Requires isolated_vocals to be connected. isolated_vocals is a lazy optional input, so ComfyUI won't evaluate the upstream vocal-separation branch unless you ask for it - the node tells the engine it needs that input only when pitch_vocals is true. If you flip the switch without wiring it, you get Connect isolated vocals before enabling Pitch Vocals. This keeps music out of the pitch shift.
  2. Writes the vocals to a scratch f32 file and calls ffmpeg with rubberband=pitch=<2^(semitones/12)> - a genuine time-preserving pitch shift, not a resample, so the timing that the mouth is supposed to follow doesn't change.
  3. Reads the result back, pads or truncates it to the original track's length, and returns it at the vocals' sample rate.

The output is a single AUDIO. In the pack's prepare stage it becomes the guidance_audio that goes into GenjSaveReview, which writes it into guide.mp4 as the soundtrack of the guidance clip - the thing the video model watches and listens to when it's producing mouth motion.

Inputs and outputs

Required: original_audio (AUDIO), pitch_vocals (BOOLEAN, default off), semitones (FLOAT, -12 to 12, step 0.5, default 3).

Optional: isolated_vocals (AUDIO, lazy).

Output: AUDIO. Wire it into guidance_audio on GenjSaveReview, or into any node that wants a guide track.

Why the "+3 semitones" default

It's enough to make the track audibly not the source speaker without making it sound like a cartoon. Small shifts are also gentler on the pitch shifter, which matters for sibilants. Go to ±7 or more and you'll hear artifacts that the video model will happily try to lip-sync to, which is the opposite of what you want. Two or three semitones and a female/male-ish presentation shift is the practical range.

The other half of the trick is doing this on isolated vocals rather than the full mix. Shifting a whole mix transposes the music too, and now your guidance track is a slightly wrong song rather than a guide. Hence the hard requirement.

Install

ComfyUI Manager → search Zura Nodes → install → restart ComfyUI.

cd ComfyUI/custom_nodes
git clone https://github.com/ZURAVFX/ComfyUI_zura_nodes
python -m pip install -r ComfyUI/custom_nodes/ComfyUI_zura_nodes/requirements.txt

For vocal isolation the pack's docs point at MelBand RoFormer (diffusion_models/MelBandRoformer_fp16.safetensors) and the ComfyUI-MelBandRoFormer node pack - that's a separate install and a separate download.

Troubleshooting

rubberband is the thing that breaks. The node shells out to ffmpeg with that filter, and ffmpeg builds are not born equal: plenty of portable builds ship without librubberband. If yours lacks it, ffmpeg exits non-zero and the node surfaces the tail of its stderr, which will mention the filter by name. The fix is an ffmpeg build compiled with --enable-librubberband (most gyan.dev and BtbN full builds have it), not a Python package.

Nothing changed. Almost always the switch: pitch_vocals defaults to false, and a pass-through looks exactly like a broken pitch shift until you notice the output is bit-identical.

Timing drifts after the shift. The node pads the shifted result to the length of original_audio at the vocals' sample rate, so if the two tracks were prepared independently and one of them is short, you'll get silence tacked on the end. Give it a vocal stem that covers the same span as the original audio and it stays locked.

CategoryZura/Artist Studio

Inputs (4)

NameTypeDefaultDescription
original_audioAUDIO—
pitch_vocalsBOOLEANfalse—
semitonesFLOAT3.0-12–12—
isolated_vocalsoptAUDIO—

Outputs (1)

NameTypeDescription
AUDIOAUDIO—