Nodes/Anomalous TTS (GPT-SoVITS)/Character Speech (GPT-SoVITS)
ComfyUI Node

Character Speech (GPT-SoVITS)

One Node, a Whole Script, and the Voice You Actually Trained

By DemonGatanjieu·Created about a month ago·Updated 5 days ago· 10
Character Speech (GPT-SoVITS)
    • audio
    • info
    ◄character▾►
    ◄text►
    ◄seed0►
    ◄speed1.00►
    ◄languageauto►
    ◄reference_audio►
    ◄reference_text►
    ◄gpt_weights▾►
    ◄sovits_weights▾►
    ◄pause_seconds0.30►
    ◄top_k15►
    ◄top_p1.00►
    ◄temperature1.00►
    ◄repetition_penalty1.35►
    ◄batch_size8►
    ◄cross_lingualadjust►
    ◄volumenormalize►
    ◄preset►

    The corner of TTS nobody wired into ComfyUI

    Local TTS settled on three names: Kokoro for cheap and fast, F5-TTS for latency, Chatterbox for the best zero-shot clone - all three borrow a voice from a few seconds of audio. GPT-SoVITS is different: you fine-tune it on a few minutes of a character, and afterwards the model isn't imitating the voice, it is that voice. "I've found Sovits to be the best overall quality of local TTS models so far" is the community verdict, and the usual workflow chains a finetune into RVC for polish.

    So why isn't it everywhere? The official repo is a self-contained WebUI with its own Python environment; running it beside ComfyUI means two installs fighting over one GPU, and "I might try GPT-SoVITS next but it looks a bit complex." This pack removes both: it vendors a trimmed copy of the inference code and runs it on ComfyUI's own PyTorch. The README is Chinese; the node is English.

    How it works

    A GPT-SoVITS character is two weight files plus a reference clip: the GPT .ckpt turns phonemes into semantic tokens, the SoVITS .pth turns those into a waveform, and the reference supplies the timbre and prosody the model steers toward. The node parses your script into lines - each with its own voice, language and seed - decodes lines sharing a voice in batches, then stitches one stream with pauses inserted.

    Two things matter before you hit Queue. Sentences are cached by content, so editing one line and re-running regenerates only that line. And the reference's tone bleeds into everything - a question as the main reference makes every line end on a rise - so the pack defaults to calm 4–8 s statements.

    The inputs that matter

    character lists folders under your gpt_sovits models directory; a folder needs at least one .ckpt and one .pth to show up, and subfolders like 日配 / 中配 become separate entries. (no characters found) means folder layout, not a bug.

    text is the script, with its own small markup: {happy} swaps to that emotion's reference audio, {main} goes back, [name] switches speaker, [pause:1s] inserts a gap, [take:2] re-rolls just the section after it. A typo'd emotion or character name won't crash the run - it warns and carries on.

    seed and speed are what you'll touch on the node face. Behind the advanced section: language (leave it on auto - it decides per sentence), reference_audio / reference_text to override the clip, gpt_weights / sovits_weights when a character has several checkpoints, pause_seconds, the sampling knobs, and batch_size. Two earn their keep: cross_lingual on adjust tightens sampling when a sentence's language differs from its reference - a Japanese model reading Chinese - and volume on normalize levels each sentence to about −20 dBFS so a new seed doesn't suddenly shout.

    Outputs are audio (into Save Audio or Preview Audio) and info - the sentence report plus warnings, also readable in the node's /history.

    Installing it

    ComfyUI Manager, search Anomalous TTS, install, restart. Or by hand:

    cd ComfyUI/custom_nodes
    git clone https://github.com/DemonGatanjieu/Anomalous_TTS
    python_embeded/python.exe -m pip install -r requirements.txt   # portable; else your ComfyUI python
    

    Lighter than you'd expect for TTS: einops, soundfile, soxr, transformers, huggingface_hub, tqdm, plus per-language front-ends - pyopenjtalk-plus; pypinyin/jieba/opencc; g2p_en/wordsegment/nltk. No torch, no ffmpeg. onnxruntime is deliberately absent: install it beside onnxruntime-gpu and other nodes break, so Chinese polyphones fall back to pypinyin.

    Base models download on first use into models/gpt_sovits/pretrained/: HuBERT (190 MB, always needed), plus Chinese RoBERTa (650 MB) and G2PW (600 MB) for Chinese and an ERes2NetV2 encoder (100 MB) for v2Pro - 1.6 GB worst case. Already have a GPT-SoVITS install? Point pretrained at it in ComfyUI/user/anomalous_tts.json and download nothing.

    Where people get burned

    An empty or stale character list. Add extra model roots under an anomalous_tts: section in extra_model_paths.yaml, or in ComfyUI/user/anomalous_tts.json. The node's buttons refresh the list, so no restart needed.

    A missing language package. The node shows a ⚠ line when a run needs a front-end you haven't installed; click it for the pip command for your Python. It's the same class of failure that made older GPT-SoVITS wrappers miserable (No module named 'jieba_fast'), and the general hazard of audio in ComfyUI, where nltk and pyopenjtalk get shared with other TTS packs.

    VRAM. batch_size defaults to 8; lower it when a long script OOMs. The pack hooks ComfyUI's memory freeing, so an image model needing room evicts these weights first - or POST /free with {"unload_models": true} from the API.

    Wrong model version. v1, v2, v2Pro and v2ProPlus load; v3 and v4 don't. Trained on the newest GPT-SoVITS? That checkpoint won't work here.

    Accents on cross-lingual lines. A Japanese-trained model will mangle Chinese ü sounds and tones, and no setting fixes that. The pack's answer is the replace map in the character settings - respell the hard word - plus cross_lingual left on.

    Last thing: the pack ships no voices, and while its code is MIT, the licence on the fine-tunes themselves is murky. Use voices you have the rights to.

    CategoryAnomalous/TTS

    Inputs (18)

    NameTypeDefaultDescription
    characterCOMBOA character in the gpt_sovits models folder
    textSTRINGThe script. {happy} switches the emotion, {main} switches back; [name] switches the speaker; [pause:1s] inserts a pause.
    seedoptINT00–18446744073709550000—
    speedoptFLOAT1.000.5–2Speaking speed. Left at its default, the presets decide (Anomalous: GPT-SoVITS Settings, and each character's generation settings)
    languageoptCOMBOautoauto: decided for each sentence from its text
    reference_audiooptSTRINGMain reference clip: a path inside the character's folder, e.g. refs/xxx.wav. Empty: the one in the character's settings, or a 3–10 s clip with a transcript, picked automatically
    reference_textoptSTRINGWhat the main reference clip says. Empty: looked up automatically
    gpt_weightsoptCOMBOauto: the one in the character's settings, else the most trained
    sovits_weightsoptCOMBOauto: the one in the character's settings, else the most trained
    pause_secondsoptFLOAT0.300–5Pause between sentences
    top_koptINT151–100Left at its default, the presets decide (Anomalous: GPT-SoVITS Settings, and each character's generation settings)
    top_poptFLOAT1.000.05–1Left at its default, the presets decide (Anomalous: GPT-SoVITS Settings, and each character's generation settings)
    temperatureoptFLOAT1.000.05–2Left at its default, the presets decide (Anomalous: GPT-SoVITS Settings, and each character's generation settings)
    repetition_penaltyoptFLOAT1.351–2Left at its default, the presets decide (Anomalous: GPT-SoVITS Settings, and each character's generation settings)
    batch_sizeoptINT81–64Sentences generated at once. Lower it when VRAM runs out
    cross_lingualoptCOMBOadjustadjust: a sentence in another language than the reference clip (a Japanese character speaking Chinese, say) uses the preset set for that pair of languages, "stable" unless changed in Anomalous. off: every sentence as if in the reference's language
    volumeoptCOMBOnormalizenormalize: each sentence's voice is brought to about -20 dBFS (peaks under -1 dBFS), so a new seed or sentence does not jump in loudness. off: the loudness the model gives
    presetoptSTRINGEmpty: the presets decide (Anomalous: GPT-SoVITS Settings, and each character's generation settings). A preset's name (stable / standard / lively, or custom_… made in Anomalous): every sentence uses its numbers; speed is not part of a preset

    Outputs (2)

    NameTypeDescription
    audioAUDIO—
    infoSTRING—