Zura · Final Audio
Change the selection, keep the voice take
- generated_audio
- edit
- finished_audio
- receipt
The point of this node
Zura · Final Audio is stage four of the pack's Audio - Replace Speech (LongCat) template, and its whole reason for existing is a cost argument.
Cloning a voice with LongCat-AudioDiT 3.5B is the slow part of a dialogue edit. Browsing selections across two waveforms is the fast part - you'll do it fifteen times. If those two things live in the same node, every nudge of an IN handle re-runs a 3.5B diffusion model. If they're separate, the take is generated once and everything after it is millisecond-scale arithmetic on a numpy array.
So this node has deliberately no selection widgets. It takes the selection you made in ZuraAudioDialogueSelect - carried on the ZURA_AUDIO_EDIT wire - and applies it.
If you're wondering why the older ZuraAudioDialogueFinish exists, that's the combined version: selection and blend in one node, kept so saved graphs still load.
What it does on Run
generated_audio comes from the voice stage, edit from the select stage. Internally the node calls the same splice routine as its parent: decode the source with ffmpeg, convert the take to the source's rate and channel count, cut the slice described by edit["generated_selection"], level-match it to the source slice with a bounded gain, fade both joins with the squared-sine ramp set by fade_ms, and concatenate - samples outside the selection are bit-identical to the original.
Two things it does that a lot of audio nodes skip:
- It reads back what it wrote. After writing
dialogue_edit.wavit re-opens the file and checks sample rate and every sample against the in-memory result. If they differ you get a hard error instead of a mystery. - It refuses to guess. If the upstream select stage recorded a problem (
edit["selection_error"]), or theeditwire has nogenerated_selectionat all, it throws before writing anything: "Select replacement words on the waveform, then press Run."
timing behaves exactly as it does elsewhere in the pack. Natural duration (no stretch) drops the take in at its own length and slides the following audio later. Keep timing (up to 15% fit) holds the timeline using atempo, and refuses rather than stretching past 15%.
Inputs and outputs that matter
You set three things: timing, fade_ms (default 12 ms), and project - a folder label, not a path. The node rejects anything with slashes, colons or angle brackets, because it's used to build the output directory name.
finished_audio is a plain AUDIO you can wire to Preview Audio, a save node, or straight into a video node. receipt is JSON: source and replacement sample ranges, the output length, duration_ratio, level_gain, fade_ms, time_stretched, and outside_selection_unchanged: true. Signed off, in writing, that it didn't touch the rest of your dialogue.
It's an output node, so it writes as it runs: output/zura_voice/<project>_<date>_<id>/dialogue_edit.wav (float WAV, original sample rate and channel count), replacement_take.wav, and edit_receipt.json. Every run gets a fresh folder, so nothing gets overwritten - which also means a dozen experiments means a dozen folders. Clean up occasionally.
Install
Manager → search Zura Nodes → install the ZURAVFX entry, restart ComfyUI. Manual equivalent:
cd ComfyUI/custom_nodes
git clone https://github.com/ZURAVFX/ComfyUI_zura_nodes
cd ComfyUI_zura_nodes && pip install -r requirements.txt
ffmpeg and ffprobe on PATH are mandatory - the source decode calls them directly. The voice model is a separate one-time install via Setup_LongCat_Windows.cmd / python setup_longcat.py --install, into its own Python 3.11 environment; weights do not ship with the pack. Parakeet is optional and only needed for the auto word detection upstream.
Where people get burned
The preview goes stale and stays stale. Move either selection on the waveform, or change fade_ms, and the previously rendered final is no longer what the graph would produce - the UI keeps showing the old one until you press Run again. The pack's docs are explicit about this ("Update final audio"), and it's the number one way to ship the wrong word.
"Connect Select Replacement Words before Final Audio." Your edit wire skipped the select stage. Common cause: wiring edit straight from ZuraAudioDialogueSource into this node. Source carries the timings, not the selection.
Auto-selection errors surface here, far from their cause. If the select stage couldn't identify the changed words unambiguously, the message reaches you at this node. Go back to the waveform and choose Manual IN/OUT - it always works, and it's usually faster than debugging Parakeet.
It can't add detail that isn't there. LongCat synthesises at 24 kHz; exporting at 48 kHz preserves your source's rate and channel count, but the replaced words still sound like 24 kHz speech sitting in a 48 kHz file. That's a voice-model problem, not an export setting.
Inputs (5)
| Name | Type | Default | Description |
|---|---|---|---|
| generated_audio | AUDIO | — | |
| edit | ZURA_AUDIO_EDIT | — | |
| timing | COMBO | 2 options: Natural duration (no stretch), Keep timing (up to 15% fit) | |
| fade_ms | FLOAT | 120–100 | — |
| project | STRING | Dialogue_Edit | — |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| finished_audio | AUDIO | — |
| receipt | STRING | — |