ComfyUI Node

Muse Voice Swap V1

Re-voice a generated video without murdering the ambience

By muse-collective-26·Created 2 months ago·Updated 2 months ago· 1
Muse Voice Swap V1
  • source_audio
  • reference_audio
  • reference_audio_2
  • reference_audio_3
  • final_audio
  • new_vocals_only
  • bg_audio_only
  • speaker_2_vocals_only
  • speaker_3_vocals_only
  • timeline_data
timeline_data{}
use_timeline_overridefalse
multi_speaker_modefalse
auto_assign_speakersfalse
sep_chunk_length10.0
sep_chunk_overlap0.10
sep_chunk_fade_shapelinear
vad_top_db35
min_segment_seconds0.30
min_gap_seconds0.20
whisper_model_sizesmall
whisper_languageauto
use_llm_emotion_tagsfalse
gemma_clip_name(no text_encoders found)
emotion_tag_temperature0.70
fish_model_path
fish_languageauto
fish_deviceauto
fish_precisionauto
fish_attentionauto
fish_max_new_tokens0
fish_chunk_length200
fish_temperature0.80
fish_top_p0.80
fish_repetition_penalty1.10
fish_seed0
fish_keep_model_loadedtrue
time_match_moderipple
stretch_fft_size2048
bg_extend_modehold_last
bg_volume1.00
vad_methodsilero
silero_threshold0.50
silero_speech_pad_ms30
timeline_ui
min_confidence_margin0.10
speaker_aware_mergefalse
merge_similarity_threshold0.30
reference_text
reference_text_2
reference_text_3

Video models like LTX don't make sound, and the audio you bolt on afterward usually has one weak link: the voice. The ambience is fine, but the dialogue is whatever generic synthetic voice the audio model hallucinated - and it's the first thing a viewer notices.

Muse Voice Swap V1 exists to fix exactly that. Feed it your mixed audio and a 5–30 second reference of the voice you actually want, and it re-clones every spoken line in that voice, then lays the new vocals back over the untouched ambience. Same clip, same room tone, different person talking. Fully local, no API key, no quota.

How it works

Under the hood it's an eight-step pipeline, and your settings map onto the steps:

  1. Separate - Demucs splits vocals from the bass/drums/other bed.
  2. Detect - a VAD finds speech segments in the vocal track.
  3. Transcribe - Whisper writes each segment's text.
  4. Tag (optional) - a local Gemma model listens to each segment's actual audio and inserts Fish S2-style emotion tags like [excited] or [whisper].
  5. Assign (optional) - ECAPA-TDNN compares each segment against your reference voices to guess who's speaking.
  6. Clone - Fish Audio S2 re-speaks each line in the target voice.
  7. Time-match - the clone gets placed back at the right moment.
  8. Remix - the new vocals go over the untouched background, plus isolated stems.

The notable thing is step 0: this node doesn't reimplement any of it - it bridges into Christian Byrne's audio-separation-nodes-comfyui and Saganaki22's ComfyUI-FishAudioS2 at runtime and calls their classes. That means those two packs must be installed first - otherwise you get a "is ... installed and loaded?" error instead of a swap.

The timeline widget is the real differentiator

Most voice-swap tools are fire-and-forget. This one embeds a Muse-Director-style timeline widget right in the node: drag boundaries, fix transcript text, assign a speaker, tweak or clear the emotion tag, lock a segment, even drop a per-segment voice override (a filename in ComfyUI/input). Then flip use_timeline_override on and re-run - it skips re-detection and re-transcription and only re-clones the parts you changed. That edit-then-regen loop is what makes it usable on real clips instead of demos.

Inputs that matter

  • source_audio and reference_audio are the only two you must wire. The reference should be clean, just the person talking.
  • multi_speaker_mode (with reference_audio_2/_3) turns it into a 3-speaker dubbing rig and adds the speaker_2_vocals_only/speaker_3_vocals_only outputs. Add auto_assign_speakers and it guesses who says what - but treat the guess as a draft; short segments score low confidence regardless.
  • use_llm_emotion_tags is the toggle behind the emotion-tag step - the fix for flat, monotone clones - and needs a Gemma checkpoint in models/text_encoders/ (the README points at gemma4_e4b_it_fp8_scaled.safetensors).
  • fish_model_path - set it to s2-pro-fp8 (auto download) and the model fetches itself on first run.
  • time_match_mode (ripple/stretch_to_fit/off) and bg_extend_mode (hold_last/loop/trim) decide how the clone lands back in time and what happens when the background runs short. Defaults are fine to start.
  • vad_method defaults to Silero, the good one; energy_threshold is the older librosa path, kept as a fallback. The README's "energy-based VAD" line is stale - the shipped default is Silero.

Outputs

final_audio is your mix. new_vocals_only and bg_audio_only are the stems - handy if you want to rebalance or re-process them. And timeline_data should be wired back into the node's own timeline_data input so your manual edits persist across runs.

Install

Via ComfyUI Manager, search "Muse Collective Voice Swap" (or install by Git URL), or manually:

cd ComfyUI/custom_nodes
git clone https://github.com/muse-collective-26/MuseVoiceSwap.git

Then restart. The pack's own requirements (librosa, openai-whisper, soundfile, speechbrain, hyperpyyaml) install through Manager. Before you can run anything you also need, from Manager:

  • ComfyUI-FishAudioS2 - the voice cloner
  • audio-separation-nodes-comfyui - Demucs separation and the mixing
  • ComfyUI-VideoHelperSuite - to load the audio/video in the first place
  • A Gemma checkpoint in models/text_encoders/ if you want emotion tags

First run is slow and that's not a bug: Demucs (hdemucs_high_trained.pt), the Whisper model, the ECAPA-TDNN speaker encoder, and the Fish S2 pro model all auto-download on first use.

Gotchas

  • Missing pack = immediate error. The bridge resolves classes at runtime, so install order matters. "AudioSeparation not found" or "FishS2VoiceCloneTTS not found" is the dependency, not the node.
  • Duration drift. Fish S2 has no native duration control, so in ripple mode (the default) the final length can drift from the source over many segments. It's logged to the console, not hidden. stretch_to_fit trades that for possible time-stretch artifacts.
  • Expect to hand-edit segments. No VAD nails boundaries on every clip - that's what the timeline is for. Plan a first pass that's just detection, correct, then re-run with the override on.
  • Demucs on foley. It's trained on music stems; its separation of non-musical ambience is "unproven beyond ear-testing" per the author. On weird sources, check bg_audio_only before you trust the mix.

It's a lot of moving parts for one node and the prerequisite stack is real. But the payoff - a flat generated voice turned into a re-voiced mix with the room intact - is something nothing else in the ComfyUI audio corner does in a single box.

CategoryMuse Collective

Inputs (45)

NameTypeDefaultDescription
source_audioAUDIOThe LTX-generated mixed audio (speech + ambient).
reference_audioAUDIOVoice-clone target reference, 5-30s.
timeline_dataSTRING{}
use_timeline_overrideBOOLEANfalseON = keep existing segment boundaries/text from timeline_data; skip auto-detect and auto-transcribe, only re-clone + remix.
multi_speaker_modeBOOLEANfalseON = assign each segment to speaker 1/2/3 (set per-segment in the timeline panel) and clone it using that speaker's reference voice (reference_audio / reference_audio_2 / reference_audio_3). Also produces isolated audio tracks for speakers 2 and 3.
auto_assign_speakersBOOLEANfalseON (with multi_speaker_mode) = auto-guess each segment's speaker by comparing its voice against reference_audio/_2/_3 using a speaker-embedding similarity match. This is a best-guess classifier, not a guarantee — review/correct assignments in the timeline afterward, same as you would Whisper's transcript. Only runs on a fresh (non-override) pass — once use_timeline_override is on, your manual corrections are trusted as-is and this is skipped entirely, so you don't need to lock a segment just to protect a manual speaker fix.
sep_chunk_lengthFLOAT10.02–60
sep_chunk_overlapFLOAT0.100–2
sep_chunk_fade_shapeCOMBOlinear4 options: linear, half_sine, logarithmic, exponential
vad_top_dbFLOAT355–60
min_segment_secondsFLOAT0.300.05–5
min_gap_secondsFLOAT0.200.02–5
whisper_model_sizeCOMBOsmall5 options: tiny, base, small, medium, large-v3
whisper_languageCOMBOauto12 options: auto, en, zh, ja, ko, es, +6
use_llm_emotion_tagsBOOLEANfalseON = feed each segment's actual audio to a local Gemma model and ask it to insert Fish S2-style emotion/prosody tags ([excited], [whisper], [pause], etc.) based on the real delivery, before cloning. Fixes flat/ monotone clones. Fully local — no API key, no quota/cost.
gemma_clip_nameCOMBO(no text_encoders found)Gemma checkpoint from ComfyUI/models/text_encoders/, loaded via the same CLIPLoader + TextGenerate nodes ComfyUI uses natively. e.g. gemma4_e4b_it_fp8_scaled.safetensors
emotion_tag_temperatureFLOAT0.700–2
fish_model_pathCOMBO1 options: (FishAudioS2 not loaded)
fish_languageCOMBOauto44 options: auto, en, zh, ja, ko, es, +38
fish_deviceCOMBOauto4 options: auto, cuda, cpu, mps
fish_precisionCOMBOauto4 options: auto, bfloat16, float16, float32
fish_attentionCOMBOauto4 options: auto, sdpa, sage_attention, flash_attention
fish_max_new_tokensINT00–4096
fish_chunk_lengthINT200100–400
fish_temperatureFLOAT0.800.1–1
fish_top_pFLOAT0.800.1–1
fish_repetition_penaltyFLOAT1.100.9–2
fish_seedINT00–2147483647
fish_keep_model_loadedBOOLEANtrue
time_match_modeCOMBOripple3 options: ripple, stretch_to_fit, off
stretch_fft_sizeINT2048512–8192
bg_extend_modeCOMBOhold_last3 options: hold_last, loop, trim
bg_volumeFLOAT1.000–2
vad_methodCOMBOsilerosilero (recommended) = neural voice-activity model, correctly tells quiet-but-real speech apart from actual silence. energy_threshold = the original librosa.effects.split method (relative dB threshold) — kept as an instant revert path if silero doesn't suit some source material. min_segment_seconds/min_gap_seconds above apply to either; vad_top_db only applies to energy_threshold.
silero_thresholdFLOAT0.500.1–0.9Only applies when vad_method is silero. Speech-probability cutoff Silero requires before calling a chunk of audio speech at all -- LOWER this if it's missing quiet/soft-spoken talking (more sensitive, but risks catching more non-speech noise too). Separate from min_segment_seconds/min_gap_seconds, which only filter/merge what Silero already decided was speech.
silero_speech_pad_msINT300–500Only applies when vad_method is silero. Padding (ms) added to both ends of each detected speech chunk -- raise this if word onsets/tails right at a segment boundary are getting clipped/missed.
timeline_uiSTRING
min_confidence_marginFLOAT0.100–0.9Only applies with multi_speaker_mode + auto_assign_speakers. How much the top speaker match must beat the runner-up (cosine similarity) before a segment's speaker gets auto-assigned. LOWER = more segments get auto-assigned (more risk of a wrong guess); HIGHER = fewer, more confident assignments (more segments left unchanged for manual review). 0.1 was previously hardcoded; short (<1s) segments tend to score low confidence regardless of this value — that's an ECAPA-TDNN limitation on short clips, not something this setting fixes.
speaker_aware_mergeBOOLEANfalseON = before merging two speech intervals separated by a gap shorter than min_gap_seconds, check whether they sound like the same voice first. Only merges when they do — a brief interjection from someone else (an 'ouch!', 'help!', a quick reaction) wedged into a small gap stays as its own segment instead of being silently absorbed into the surrounding speaker's segment, which is what happens with this OFF (the original behavior). Compares the two intervals directly against each other, not against any reference voice — works independently of multi_speaker_mode/auto_assign_speakers, and is useful even with a single known speaker (keeps an unrelated voice from merging into their segment). Falls back to always-merge whenever the check itself can't be computed.
merge_similarity_thresholdFLOAT0.300–0.9Only applies with speaker_aware_merge ON. How similar two adjacent intervals' voices must be (cosine similarity) to still merge across a short gap. LOWER = merges more readily (risks absorbing a real interjection); HIGHER = splits more readily (risks splitting one person's own natural pause into two segments).
reference_audio_2optAUDIOReference voice for segments assigned speaker 2 (multi_speaker_mode).
reference_audio_3optAUDIOReference voice for segments assigned speaker 3 (multi_speaker_mode).
reference_textoptSTRINGTranscript of reference_audio. Leave disconnected to auto-transcribe it internally via Whisper — no need to wire anything in for the normal case.
reference_text_2optSTRING
reference_text_3optSTRING

Outputs (6)

NameTypeDescription
final_audioAUDIO
new_vocals_onlyAUDIO
bg_audio_onlyAUDIO
speaker_2_vocals_onlyAUDIO
speaker_3_vocals_onlyAUDIO
timeline_dataSTRING