Seed-VC is a zero-shot voice conversion framework using a diffusion transformer architecture. Key features:

  • External timbre shifter during training perturbs source speech timbre, preventing leakage
  • Diffusion transformer uses full reference speech context for fine-grained timbre capture
  • Supports real-time voice conversion (~300ms algorithm delay)
  • Supports singing voice conversion (V1) and accent/emotion conversion (V2)

Paper: Zero-shot Voice Conversion with Diffusion Transformers Codebase: Github HuggingFace: Model Checkpoints

Available Models

VersionNamePurposeSRParams
v1.0seed-uvit-tat-xlsr-tinyVoice Conversion2205025M
v1.0seed-uvit-whisper-small-wavenetVoice Conversion2205098M
v1.0seed-uvit-whisper-baseSinging Voice Conversion44100200M
v2.0hubert-bsqvae-smallVoice & Accent Conversion2205067M+90M

Installation

git clone https://github.com/Plachtaa/seed-vc.git
cd seed-vc

conda create -n seedvc python=3.10
conda activate seedvc

pip install -r requirements.txt

# Optional: ~6x speedup on V2 models (Windows)
pip install triton-windows==3.2.0.post13

Command Line Inference (V1)

python inference.py \
    --source <source-wav> \
    --target <reference-wav> \
    --output <output-dir> \
    --diffusion-steps 25 \
    --length-adjust 1.0 \
    --inference-cfg-rate 0.7 \
    --f0-condition False \
    --auto-f0-adjust False \
    --semi-tone-shift 0 \
    --fp16 True

Parameter guide:

  • diffusion-steps: 25 default; 30–50 for best quality; 4–10 for fastest
  • length-adjust: <1.0 speeds up, >1.0 slows down
  • f0-condition: set True for singing voice conversion
  • semi-tone-shift: pitch shift in semitones (SVC only)

Command Line Inference (V2 — Accent/Emotion)

python inference_v2.py \
    --source <source-wav> \
    --target <reference-wav> \
    --output <output-dir> \
    --diffusion-steps 25 \
    --intelligibility-cfg-rate 0.7 \
    --similarity-cfg-rate 0.7 \
    --convert-style true \
    --anonymization-only false \
    --top-p 0.9 \
    --temperature 1.0

Web UI Options

python app_vc.py --fp16 True      # V1 Voice Conversion
python app_svc.py --fp16 True     # Singing Voice Conversion
python app_vc_v2.py --compile     # V2 Model
python app.py --enable-v1 --enable-v2  # Integrated

Real-Time Voice Conversion

python real-time-gui.py

Recommended settings (RTX 3060 Laptop GPU):

ParameterValue
Diffusion Steps10
Inference CFG Rate0.7
Max Prompt Length3.0s
Block Time0.18s
Crossfade Length0.04s
Extra Context (left)2.5s
Extra Context (right)0.02s
Latency~430ms

Use VB-CABLE to route GUI output to a virtual microphone.

Batch Voice Conversion Script

Converts utterances from an input directory using random reference voices — useful for corpus augmentation and speaker anonymization.

"""
Batch Seed-VC augmentation script.
Usage:
    python batch_seedvc_augment.py \
        --seedvc-root ../seed-vc \
        --ref-dir /path/to/reference/voices \
        --input-dir /path/to/input/utterances \
        --output-dir /path/to/output \
        --diffusion-steps 30 \
        --recursive --skip-existing
"""

from __future__ import annotations
import argparse, json, os, random, sys, time
from pathlib import Path
from typing import Iterable, Sequence

def _iter_audio_files(root: Path, recursive: bool, exts: Sequence[str]) -> Iterable[Path]:
    exts_lc = {e.lower() if e.startswith(".") else f".{e.lower()}" for e in exts}
    if root.is_file():
        if root.suffix.lower() in exts_lc:
            yield root
        return
    if not root.exists():
        return
    if recursive:
        for p in root.rglob("*"):
            if p.is_file() and p.suffix.lower() in exts_lc:
                yield p
    else:
        for p in root.iterdir():
            if p.is_file() and p.suffix.lower() in exts_lc:
                yield p

def main() -> int:
    parser = argparse.ArgumentParser(description="Batch Seed-VC corpus augmentation")
    parser.add_argument("--seedvc-root", type=str, default="../seed-vc")
    parser.add_argument("--ref-dir", type=str, required=True)
    parser.add_argument("--input-dir", type=str, required=True)
    parser.add_argument("--output-dir", type=str, required=True)
    parser.add_argument("--device", type=str, default="")
    parser.add_argument("--recursive", action="store_true")
    parser.add_argument("--ext", type=str, default="wav")
    parser.add_argument("--seed", type=int, default=0)
    parser.add_argument("--limit", type=int, default=0)
    parser.add_argument("--skip-existing", action="store_true")
    parser.add_argument("--diffusion-steps", type=int, default=30)
    parser.add_argument("--length-adjust", type=float, default=1.0)
    parser.add_argument("--inference-cfg-rate", type=float, default=0.7)
    parser.add_argument("--f0-condition", action="store_true")
    parser.add_argument("--suffix", type=str, default="_seedvc")
    parser.add_argument("--manifest", type=str, default="manifest_seedvc.jsonl")
    args = parser.parse_args()

    seedvc_root = Path(args.seedvc_root).resolve()
    sys.path.insert(0, str(seedvc_root))
    os.environ.setdefault("HF_HUB_CACHE", str(seedvc_root / "checkpoints" / "hf_cache"))

    from seed_vc_wrapper import SeedVCWrapper
    import soundfile as sf

    ref_files = sorted(_iter_audio_files(Path(args.ref_dir), args.recursive, args.ext.split(",")))
    input_files = sorted(_iter_audio_files(Path(args.input_dir), args.recursive, args.ext.split(",")))

    if args.limit > 0:
        input_files = input_files[:args.limit]

    random.seed(args.seed)
    output_dir = Path(args.output_dir)
    output_dir.mkdir(parents=True, exist_ok=True)

    device = None
    if args.device:
        import torch
        device = torch.device(args.device)

    wrapper = SeedVCWrapper(device=device, load_f0_model=bool(args.f0_condition))

    with (output_dir / args.manifest).open("a", encoding="utf-8") as mf:
        for idx, inp in enumerate(input_files, start=1):
            out_path = output_dir / f"{inp.stem}{args.suffix}.wav"
            if args.skip_existing and out_path.exists():
                continue
            ref = random.choice(ref_files)
            try:
                sr, audio = wrapper.convert_voice_npy(
                    source=str(inp), target=str(ref),
                    diffusion_steps=args.diffusion_steps,
                    length_adjust=args.length_adjust,
                    inference_cfg_rate=args.inference_cfg_rate,
                    f0_condition=bool(args.f0_condition),
                )
                sf.write(str(out_path), audio, sr)
                rec = {"source": str(inp), "reference": str(ref), "output": str(out_path)}
                mf.write(json.dumps(rec) + "\n")
                mf.flush()
                if idx % 10 == 0:
                    print(f"[{idx}/{len(input_files)}] Processed")
            except Exception as e:
                print(f"FAILED {inp.name}: {e}")
    return 0

if __name__ == "__main__":
    raise SystemExit(main())