Clonación de voz

Clona cualquier voz a partir de una muestra breve de audio de referencia. Tanto Qwen3-TTS como CosyVoice3 soportan clonación de voz con distintos encoders de hablante — ECAPA-TDNN (1024 dim) y CAM++ (192 dim) respectivamente.

Cómo funciona

  1. Graba o proporciona una muestra de audio de referencia de la voz objetivo
  2. Extracción del embedding de hablante — un encoder de hablante procesa el audio de referencia para obtener un vector de embedding de dimensión fija
  3. Inyección del embedding — el embedding del hablante condiciona al modelo TTS durante la síntesis
  4. Síntesis de voz — el modelo TTS genera voz que coincide con las características vocales de la muestra de referencia

Motores

La clonación de voz está disponible con ambos motores TTS. Cada uno utiliza un encoder de hablante distinto:

MotorEncoder de hablanteEmbeddingBackend
Qwen3-TTSECAPA-TDNNx-vector de 1024 dimMLX (GPU)
CosyVoice3CAM++192 dimCoreML (Neural Engine)

CosyVoice3 + CAM++

CosyVoice3 usa el encoder de hablante CAM++ (Context-Aware Masking++) del proyecto 3D-Speaker de Alibaba. El embedding de 192 dim condiciona el modelo DiT flow mediante una capa de proyección afín (192 → 80) entrenada conjuntamente con CosyVoice3.

Arquitectura de CAM++

EtapaDescripción
FCMMódulo convolucional frontal (Conv2d + 2 ResBlocks, 32 canales)
TDNNTime Delay Neural Network (de 320 a 128 canales, kernel de tamaño 5)
Bloques D-TDNN3 bloques densamente conectados (12/24/16 capas) con context-aware masking
Stats PoolPooling de media + desviación estándar (estadísticas globales)
DenseProyección lineal a un embedding de 192 dim

El modelo CoreML (~14 MB, FP16) se ejecuta en el Neural Engine. Se descarga automáticamente desde aufklarer/CamPlusPlus-Speaker-CoreML en el primer uso.

Clonación de voz con Qwen3-TTS

Qwen3-TTS soporta dos modos de clonación de voz:

Modo ICL (recomendado)

El modo In-Context Learning codifica el audio de referencia en tokens de codec mediante el encoder del tokenizador de voz Mimi y los antepone a la transcripción de referencia. Esto proporciona al modelo un contexto acústico completo — mayor calidad y EOS fiable (soluciona los problemas con textos cortos e idiomas distintos del inglés).

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
)

Modo X-Vector

Usa un encoder ECAPA-TDNN que produce un x-vector de 1024 dim. No requiere transcripción pero ofrece menor calidad. Puede fallar al emitir EOS en textos cortos o en ciertos idiomas.

Arquitectura de ECAPA-TDNN

EtapaDescripción
TDNNTime Delay Neural Network (de 128 a 512 canales, kernel de tamaño 5)
Bloques SE-Res2Net3 bloques con Squeeze-and-Excitation (512 canales, dilatación 2/3/4)
MFAMulti-layer Feature Aggregation (1536 canales + ReLU)
ASPAttentive Statistics Pooling (1536 canales, softmax sobre el tiempo)
FCCapa totalmente conectada (de 3072 a 1024 dimensiones)

Los pesos (76 parámetros) están incluidos en los safetensors de Qwen3-TTS — no requiere descarga adicional.

Uso de la CLI

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

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

Ejemplos

# CosyVoice3: multilingual voice cloning (9 languages)
.build/release/audio 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/audio 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/audio speak "The quick brown fox jumps over the lazy dog." \
    --voice-sample recording_15s.wav -o cloned_fox.wav

Diálogo multi-hablante

CosyVoice3 soporta diálogos multi-hablante con clonación de voz por hablante. Usa el flag --speakers para mapear las etiquetas de hablante a archivos de audio de referencia:

# Two-speaker dialogue with voice cloning
.build/release/audio 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/audio 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/audio speak "[S1] First line. [S2] Second line." \
    --engine cosyvoice --speakers s1=a.wav,s2=b.wav --turn-gap 0.5 -o gapped.wav

El audio de referencia de cada hablante se procesa a través del encoder CAM++ para extraer un embedding de 192 dim. El modelo se carga una sola vez y se reutiliza para todos los hablantes. Consulta la guía de CosyVoice3 para obtener más detalles sobre la sintaxis de los diálogos y las etiquetas de emoción.

Consejos para el audio de referencia

Importante

Para Qwen3-TTS, la clonación de voz solo funciona con el modelo base — no con customVoice. La clonación de voz de CosyVoice3 funciona con el modelo por defecto.

API de Swift

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
)