Zura · Voice Guidance
Pitch-shift the voice so the model stops copying it
- original_audio
- isolated_vocals
- AUDIO
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:
- Requires isolated_vocals to be connected.
isolated_vocalsis 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 whenpitch_vocalsis true. If you flip the switch without wiring it, you getConnect isolated vocals before enabling Pitch Vocals. This keeps music out of the pitch shift. - 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. - 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.
Inputs (4)
| Name | Type | Default | Description |
|---|---|---|---|
| original_audio | AUDIO | — | |
| pitch_vocals | BOOLEAN | false | — |
| semitones | FLOAT | 3.0-12–12 | — |
| isolated_vocalsopt | AUDIO | — |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| AUDIO | AUDIO | — |