음성 복제

짧은 참조 오디오 샘플에서 원하는 목소리를 복제합니다. IndexTTS2는 감정, 템포, 일시정지 제어가 있는 네이티브 MLX zero-shot 복제를 추가하며, Qwen3-TTS 및 CosyVoice3의 화자 인코더 복제와 함께 사용할 수 있습니다.

동작 방식

  1. 대상 음성의 레퍼런스 오디오 샘플을 녹음하거나 제공합니다
  2. 화자 임베딩 추출 — 화자 인코더가 레퍼런스 오디오를 고정 차원 임베딩 벡터로 처리합니다
  3. 임베딩 주입 — 화자 임베딩이 합성 중 TTS 모델을 조건화합니다
  4. 음성 합성 — TTS 모델이 레퍼런스 샘플의 음성 특성과 일치하는 음성을 생성합니다

엔진

음성 복제는 여러 TTS 엔진에서 사용할 수 있습니다. IndexTTS2는 프롬프트 오디오와 스타일 조건을 사용하고, Qwen3-TTS와 CosyVoice3는 화자 인코더를 사용합니다.

F5-TTS와 Higgs TTS 3는 화자 인코더를 아예 쓰지 않습니다. F5는 참조 음성의 멜 스펙트로그램과 전사 텍스트를 직접 조건으로 삼고, Higgs는 코덱으로 인코딩한 참조 프레임을 이어서 생성합니다.

엔진화자 인코더임베딩백엔드
Qwen3-TTSECAPA-TDNN1024차원 x-vectorMLX (GPU)
CosyVoice3CAM++192차원CoreML (Neural Engine)
IndexTTS2w2v-BERT + MaskGCT + CAMPPlussemantic codes + prompt mel + 192-dim styleMLX (GPU)
F5-TTS없음 — 참조 멜 + 전사 텍스트100밴드 참조 멜MLX (GPU)
Higgs TTS 3Higgs 코덱 인코더8 코드북 참조 코드MLX (GPU)

IndexTTS2 음성 복제

IndexTTS2는 네이티브 MLX zero-shot 음성 복제 엔진입니다. --voice-sample이 필요하며 기본적으로 aufklarer/IndexTTS2-MLX-fp16을 다운로드합니다. 선택적 감정/스타일 참조, 프리셋 또는 벡터 감정 제어, 말하기 속도 조정, 내부 일시정지 제한을 지원합니다. 현재는 배치 합성만 지원합니다. 감정은 프리셋 또는 8값 벡터(--indextts2-emotion, 강도는 --indextts2-emotion-weight)나 별도의 스타일 참조(--indextts2-emotion-audio)로 지정할 수 있습니다. --indextts2-s2mel-steps는 플로 스텝 수와 속도의 트레이드오프입니다(기본 15, 청감 검증). float32 어텐션 디코드 개편 후 M5 Pro에서 RTF ≈ 1.0으로 합성합니다.

F5-TTS 음성 클로닝

F5-TTS는 약 10초의 참조 음성과 그 전사 텍스트(--f5-reference-text)로 DiT 플로 매칭을 통해 클로닝합니다 — 화자 인코더가 없습니다. 패키지에서 가장 빠른 클로닝 경로(기본 16 스텝에서 RTF 0.57, 낮을수록 빠름)이자 가장 작은 엔진(피크 RSS 약 0.8 GB)으로 영어와 중국어를 지원합니다. F5-TTS 가이드를 참고하세요.

Higgs TTS 3 음성 클로닝

Higgs TTS 3(4B)는 참조 클립을 코덱 프레임으로 인코딩해 이어서 생성합니다. 인라인 감정·스타일·효과음 태그를 갖춘 표현력 있는 대화형 합성, 100개 이상 언어, RTF 0.78. 참조 전사(--higgs-ref-text)를 주면 클로닝 품질이 좋아집니다. Higgs TTS 3 가이드를 참고하세요.

CosyVoice3 + CAM++

CosyVoice3는 Alibaba의 3D-Speaker 프로젝트의 CAM++ (Context-Aware Masking++) 화자 인코더를 사용합니다. 192차원 임베딩은 CosyVoice3와 함께 공동 학습된 affine projection 레이어(192 → 80)를 통해 DiT flow 모델을 조건화합니다.

CAM++ 아키텍처

단계설명
FCM프론트엔드 컨볼루션 모듈 (Conv2d + 2개 ResBlock, 32 채널)
TDNNTime Delay Neural Network (320 → 128 채널, 커널 크기 5)
D-TDNN 블록context-aware masking이 적용된 3개의 밀집 연결 블록 (12/24/16 레이어)
Stats PoolMean + standard deviation pooling (전역 통계)
Dense192차원 임베딩으로의 선형 투영

CoreML 모델(약 14 MB, FP16)은 Neural Engine에서 실행됩니다. 첫 사용 시 aufklarer/CamPlusPlus-Speaker-CoreML에서 자동으로 다운로드됩니다.

Qwen3-TTS 음성 복제

Qwen3-TTS는 두 가지 음성 복제 모드를 지원합니다:

ICL 모드 (권장)

In-Context Learning 모드는 Mimi speech tokenizer 인코더를 통해 레퍼런스 오디오를 코덱 토큰으로 인코딩하고 레퍼런스 전사 앞에 추가합니다. 이를 통해 모델이 전체 음향 컨텍스트를 얻게 되며 — 더 높은 품질과 안정적인 EOS를 제공합니다 (짧은 텍스트와 비영어 언어의 문제를 해결합니다).

let (model, encoder) = try await Qwen3TTSModel.fromPretrainedWithEncoder()
let audio = model.synthesizeWithVoiceCloneICL(
    text: "Target text to synthesize.",
    referenceAudio: refSamples,
    referenceSampleRate: 24000,
    referenceText: "Exact transcript of reference audio.",
    language: "english",
    codecEncoder: encoder
)

X-Vector 모드

1024차원 x-vector를 생성하는 ECAPA-TDNN 인코더를 사용합니다. 전사가 필요하지 않지만 품질이 낮습니다. 짧은 텍스트나 특정 언어에서 EOS를 방출하지 못할 수 있습니다.

ECAPA-TDNN 아키텍처

단계설명
TDNNTime Delay Neural Network (128 → 512 채널, 커널 크기 5)
SE-Res2Net 블록Squeeze-and-Excitation이 있는 3개 블록 (512 채널, dilation 2/3/4)
MFAMulti-layer Feature Aggregation (1536 채널 + ReLU)
ASPAttentive Statistics Pooling (1536 채널, 시간에 대한 softmax)
FCFully connected 레이어 (3072 → 1024 차원)

가중치(76 파라미터)는 Qwen3-TTS safetensors에 포함되어 있습니다 — 별도 다운로드가 필요 없습니다.

CLI 사용법

# CosyVoice3 voice cloning (CAM++, CoreML Neural Engine)
.build/release/speech speak "Text in the cloned voice" \
    --engine cosyvoice --voice-sample reference.wav -o output.wav

# IndexTTS2 voice cloning (expanded MLX bundle)
.build/release/speech speak "Text in the cloned voice" \
    --engine indextts2 --voice-sample reference.wav \
    --indextts2-speaking-rate 1.35 \
    --indextts2-max-pause 0.05 \
    -o output.wav

# F5-TTS voice cloning (reference + transcript, MLX GPU)
.build/release/speech speak "Text in the cloned voice" \
    --engine f5 --voice-sample reference.wav \
    --f5-reference-text "Transcript of the reference clip." -o output.wav

# Higgs TTS 3 voice cloning (4B, expressive, MLX GPU)
.build/release/speech speak "Text in the cloned voice" \
    --engine higgs --voice-sample reference.wav \
    --higgs-ref-text "Transcript of the reference clip." -o output.wav

# Qwen3-TTS voice cloning (ECAPA-TDNN, MLX GPU)
.build/release/speech speak "Text in the cloned voice" \
    --voice-sample reference.wav -o output.wav

예시

# CosyVoice3: multilingual voice cloning (9 languages)
.build/release/speech speak "Hello, this is my cloned voice." \
    --engine cosyvoice --voice-sample my_voice.wav -o cloned_hello.wav

# CosyVoice3: clone voice in a different language
.build/release/speech speak "Guten Tag, das ist meine geklonte Stimme." \
    --engine cosyvoice --voice-sample my_voice.wav --language german -o german.wav

# Qwen3-TTS: English voice cloning
.build/release/speech speak "The quick brown fox jumps over the lazy dog." \
    --voice-sample recording_15s.wav -o cloned_fox.wav

# IndexTTS2: local exported bundle
.build/release/speech speak "This voice was cloned locally." \
    --engine indextts2 --voice-sample my_voice.wav \
    --indextts2-speaking-rate 1.35 \
    --indextts2-max-pause 0.05 \
    --indextts2-bundle-dir /path/to/IndexTTS2-MLX-fp16 \
    -o indextts2.wav

다화자 대화

CosyVoice3는 화자별 음성 복제가 적용된 다화자 대화를 지원합니다. --speakers 플래그를 사용해 화자 태그를 레퍼런스 오디오 파일에 매핑하세요:

# Two-speaker dialogue with voice cloning
.build/release/speech speak "[S1] Hello there! [S2] Hey, how are you?" \
    --engine cosyvoice --speakers s1=alice.wav,s2=bob.wav -o dialogue.wav

# Dialogue with emotion tags + voice cloning
.build/release/speech speak "[S1] (happy) Great news! [S2] (surprised) Really? Tell me more." \
    --engine cosyvoice --speakers s1=alice.wav,s2=bob.wav -o emotional_dialogue.wav

# Adjust silence between turns
.build/release/speech speak "[S1] First line. [S2] Second line." \
    --engine cosyvoice --speakers s1=a.wav,s2=b.wav --turn-gap 0.5 -o gapped.wav

각 화자의 레퍼런스 오디오는 CAM++ 인코더를 통해 처리되어 192차원 임베딩이 추출됩니다. 모델은 한 번 로드되고 모든 화자에 재사용됩니다. 대화 문법과 감정 태그의 자세한 내용은 CosyVoice3 가이드를 참조하세요.

레퍼런스 오디오 팁

중요

Qwen3-TTS의 음성 복제는 base 모델에서만 동작하며 customVoice에서는 동작하지 않습니다. CosyVoice3 음성 복제는 기본 모델에서 동작합니다. IndexTTS2는 --voice-sample이 필요하고 현재 배치 합성만 지원하며 선택적 감정/스타일 제어를 받습니다.

Swift API

import CosyVoiceTTS

// CosyVoice3 voice cloning
let model = try await CosyVoiceTTSModel.fromPretrained()
let speaker = try await CamPlusPlusSpeaker.fromPretrained()

// Extract 192-dim speaker embedding from reference audio
let embedding = try speaker.embed(audio: refSamples, sampleRate: 16000)

// Synthesize with cloned voice
let audio = model.synthesize(
    text: "Hello in a cloned voice!",
    speakerEmbedding: embedding
)

// With custom instruction + speaker embedding
let styledAudio = model.synthesize(
    text: "Hello!",
    instruction: "Speak happily and with excitement.",
    speakerEmbedding: embedding
)

// Multi-speaker dialogue
let segments = DialogueParser.parse("[S1] (happy) Hi! [S2] Hey there.")
let embeddings = ["S1": aliceEmbedding, "S2": bobEmbedding]
let dialogueAudio = DialogueSynthesizer.synthesize(
    segments: segments,
    speakerEmbeddings: embeddings,
    model: model,
    language: "english"
)
import Qwen3TTS

// Qwen3-TTS voice cloning
let model = try await Qwen3TTSModel.fromPretrained()
let audio = model.synthesizeWithVoiceClone(
    text: "Hello in a cloned voice!",
    referenceAudio: refSamples,
    referenceSampleRate: 24000
)
import IndexTTS2TTS

// IndexTTS2 zero-shot voice cloning
let model = try await IndexTTS2TTSModel.fromPretrained()
let audio = try await model.generate(
    text: "Hello in a cloned voice!",
    referenceAudio: URL(fileURLWithPath: "reference.wav"),
    synthesisOptions: try IndexTTS2SynthesisOptions(
        speakingRate: 1.35,
        maxInternalPauseDuration: 0.05)
)

Reference Audio Caching

Both synthesizeWithVoiceClone (x-vector) and synthesizeWithVoiceCloneICL (ICL) cache their per-reference preprocessing across calls on the same model instance. The x-vector path caches the ECAPA-TDNN speaker embedding; the ICL path additionally caches the Mimi codec encoder output. The cache is content-addressed (hash of raw samples + sample rate) and bounded to a small LRU (default 4 entries), so repeated generations against the same reference waveform skip the mel + encoder passes without unbounded memory growth.

let tts = try await Qwen3TTSModel.fromPretrained()

// First call: runs ECAPA-TDNN, caches the embedding
_ = tts.synthesizeWithVoiceClone(text: "Hello", referenceAudio: ref, ...)

// Subsequent calls with the same reference: cache hit
_ = tts.synthesizeWithVoiceClone(text: "How are you?", referenceAudio: ref, ...)

// Explicit eviction (rarely needed — LRU handles capacity)
tts.clearReferenceAudioCache()