IrodoriTTS-v2 Dialogue TTS
IrodoriTTS-v2 Dialogue TTS
- model
- ref_audio_config_a
- ref_audio_config_b
- cfg_config
- rescale_config
- combined_audio
- dialogue_timing_json
- log_text
This is the reason the pack exists. The other nodes are scaffolding; IrodoriTTS-v2 Dialogue TTS is the thing that turns a speaker-tagged script into one finished conversation audio file, with two different cloned voices trading lines in order. If you've ever hand-assembled multi-voice TTS by rendering each line separately and stitching them in an editor, this node exists to make you stop doing that.
The contract: dialogue_segments_json
The whole node runs off one JSON string, and the format is the thing to learn first, because everything else follows from it. Every input on the node is a knob on top of this:
{
"schema": "kantan.dialogue.v1",
"utterances": [
{ "index": 1, "speaker": "A", "text": "こんにちは。" },
{ "index": 2, "speaker": "B", "text": "こんにちは、調子はどう?" }
]
}
Required per utterance: speaker and text. index is optional (fallback is just 1, 2, 3…). The node parses this, walks each utterance in order, swaps reference voices based on the speaker, synthesizes, and concatenates. Two speakers max - the README is explicit that three or more isn't supported. If a line has an unknown speaker, unknown_speaker_policy decides: error (default), skip, or fold it into speaker A or B.
You can write this JSON by hand, but the intended flow is to let an LLM generate it. The author's reference pipeline is ComfyUI-LLM-Session producing a transcript, then ComfyUI-ScriptFlow's MultiOutputScript extracting just the 「...」 quoted lines into this JSON - so an LLM can write narration and stage direction, and only the dialogue gets spoken. A bundled sample workflow (examples/ModelChatSample.json) shows the whole chain.
What you actually set
ref_audio_config_a/ref_audio_config_b- from two Reference Audio nodes. Your two voices. If you leave one out, the node still runs; it's optional.speaker_a_key/speaker_b_key- the string values that mean "speaker A" and "speaker B" in your JSON (defaultsAandB). If your LLM emits"speaker": "Alice"and"Bob", set the keys to those.seed+seed_mode- the widget seed is the per-run base seed;seed_modedecides how it's distributed across utterances. The README's recommendation is solid: keep ComfyUI's "control before generate" on fixed and useincrement_per_utterance(the default), so utterance 1 gets seed 100, utterance 2 gets 101, and so on - every line is reproducible and distinct.fixedgives every line the same seed (fine, but more likely to sound samey),randomizeignores the seed entirely.silence_ms(default 300) andfade_ms(default 10) - dead air between lines and a short fade at each utterance's edges. The fade exists to kill click noise when clips are stitched; if you still hear pops, the README suggests bumpingfade_msto 15–20 (or down to ~5 if the starts sound rounded).num_steps,cfg_guidance_mode,cfg_scale_text,cfg_scale_speaker,context_kv_cache- the same sampler knobs the plain Sampler node exposes.cfg_scale_text(3) pushes the words,cfg_scale_speaker(5) holds the voice.max_utterances(default 100) - a safety cap on how many lines it'll chew through;skip_empty_text(default on) drops blank lines.
Outputs
combined_audio- the full conversation as oneAUDIOtrack at 24kHz. Feed it to a SaveAudio node.dialogue_timing_json- per-utterance start/end seconds. This is the hidden gem: it's your subtitle/lyric-sync data for free, so if you're driving lip-sync or burned-in subs off this audio, you already have the timestamps.log_text- a plain-text log of what was synthesized, in what order, with which seed. Nice for reproducibility post-mortems.
Installing and the usual pain
cd ComfyUI/custom_nodes
git clone https://github.com/kantan-kanto/ComfyUI-DialogueTTS
pip install -r ComfyUI-DialogueTTS/requirements.txt
then restart (or search "ComfyUI-DialogueTTS" in ComfyUI Manager). The main checkpoint won't download itself - grab Irodori-TTS-500M-v2.safetensors into models/checkpoints/ - but the codec and tokenizer auto-fetch on first load.
Issues people actually hit: malformed JSON (the node errors cleanly on invalid JSON, so check the JSON before blaming the model); "Model not found" because the checkpoint was never placed; and long scenes on low VRAM, where keeping context_kv_cache on is what stops generation from slowing to a crawl. Also worth knowing this repo started as an unofficial v2 fork of jupo-ai's ComfyUI_IrodoriTTS_Wrapper before that project caught up with v2 - so if you have both packs installed, mind which one you're actually wiring up. And yes: this is a Japanese TTS at heart, so the pipeline sings in Japanese.
Inputs (20)
| Name | Type | Default | Description |
|---|---|---|---|
| model | IRODORI_V2_MODEL | — | |
| dialogue_segments_json | STRING | — | |
| speaker_a_key | STRING | A | — |
| speaker_b_key | STRING | B | — |
| seed | INT | 00–9223372036854776000 | — |
| seed_mode | COMBO | increment_per_utterance | 3 options: increment_per_utterance, fixed, randomize |
| num_steps | INT | 401–120 | — |
| cfg_guidance_mode | COMBO | independent | 3 options: independent, joint, alternating |
| cfg_scale_text | FLOAT | 3.00–10 | — |
| cfg_scale_speaker | FLOAT | 5.00–10 | — |
| context_kv_cache | BOOLEAN | true | — |
| silence_ms | INT | 3000–10000 | — |
| fade_ms | INT | 100–200 | — |
| max_utterances | INT | 1000–1000 | — |
| skip_empty_text | BOOLEAN | true | — |
| unknown_speaker_policy | COMBO | error | 4 options: error, skip, use_a, use_b |
| ref_audio_config_aopt | IRODORI_V2_REF_CONFIG | — | |
| ref_audio_config_bopt | IRODORI_V2_REF_CONFIG | — | |
| cfg_configopt | IRODORI_V2_CFG_CONFIG | — | |
| rescale_configopt | IRODORI_V2_RESCALE_CONFIG | — |
Outputs (3)
| Name | Type | Description |
|---|---|---|
| combined_audio | AUDIO | — |
| dialogue_timing_json | STRING | — |
| log_text | STRING | — |