Clonage vocal
Clonez n'importe quelle voix à partir d'un court échantillon audio de référence. Qwen3-TTS et CosyVoice3 prennent tous deux en charge le clonage vocal avec différents encodeurs de locuteur — ECAPA-TDNN (1024 dim) et CAM++ (192 dim) respectivement.
Fonctionnement
- Enregistrez ou fournissez un échantillon audio de référence de la voix cible
- Extraction de l'empreinte de locuteur — un encodeur de locuteur traite l'audio de référence en un vecteur d'embedding à dimension fixe
- Injection de l'embedding — l'embedding de locuteur conditionne le modèle TTS pendant la synthèse
- Synthèse vocale — le modèle TTS génère une parole qui correspond aux caractéristiques vocales de l'échantillon de référence
Moteurs
Le clonage vocal est disponible avec les deux moteurs TTS. Chacun utilise un encodeur de locuteur différent :
| Moteur | Encodeur de locuteur | Embedding | Backend |
|---|---|---|---|
| Qwen3-TTS | ECAPA-TDNN | x-vector 1024 dim | MLX (GPU) |
| CosyVoice3 | CAM++ | 192 dim | CoreML (Neural Engine) |
CosyVoice3 + CAM++
CosyVoice3 utilise l'encodeur de locuteur CAM++ (Context-Aware Masking++) du projet 3D-Speaker d'Alibaba. L'embedding 192 dim conditionne le modèle flow DiT via une couche de projection affine (192 → 80) qui a été entraînée conjointement avec CosyVoice3.
Architecture CAM++
| Étape | Description |
|---|---|
| FCM | Module convolutionnel d'entrée (Conv2d + 2 ResBlocks, 32 canaux) |
| TDNN | Time Delay Neural Network (320 vers 128 canaux, kernel size 5) |
| Blocs D-TDNN | 3 blocs densément connectés (12/24/16 couches) avec masquage sensible au contexte |
| Stats Pool | Pooling moyenne + écart-type (statistiques globales) |
| Dense | Projection linéaire vers un embedding 192 dim |
Le modèle CoreML (~14 Mo, FP16) s'exécute sur le Neural Engine. Il est téléchargé automatiquement depuis aufklarer/CamPlusPlus-Speaker-CoreML à la première utilisation.
Clonage vocal Qwen3-TTS
Qwen3-TTS prend en charge deux modes de clonage vocal :
Mode ICL (recommandé)
Le mode In-Context Learning encode l'audio de référence en tokens de codec via l'encodeur du tokenizer de parole Mimi et les préfixe avec la transcription de référence. Cela donne au modèle un contexte acoustique complet — qualité supérieure et EOS fiable (corrige les problèmes sur les textes courts et les langues non anglaises).
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
)
Mode x-vector
Utilise un encodeur ECAPA-TDNN qui produit un x-vector 1024 dim. Pas besoin de transcription mais qualité inférieure. Peut échouer à émettre EOS sur des textes courts ou certaines langues.
Architecture ECAPA-TDNN
| Étape | Description |
|---|---|
| TDNN | Time Delay Neural Network (128 vers 512 canaux, kernel size 5) |
| Blocs SE-Res2Net | 3 blocs avec Squeeze-and-Excitation (512 canaux, dilation 2/3/4) |
| MFA | Agrégation multi-couches de caractéristiques (1536 canaux + ReLU) |
| ASP | Attentive Statistics Pooling (1536 canaux, softmax dans le temps) |
| FC | Couche entièrement connectée (3072 vers 1024 dimensions) |
Les poids (76 paramètres) sont inclus dans les safetensors de Qwen3-TTS — aucun téléchargement séparé requis.
Utilisation en CLI
# Clonage vocal CosyVoice3 (CAM++, Neural Engine CoreML)
.build/release/speech speak "Text in the cloned voice" \
--engine cosyvoice --voice-sample reference.wav -o output.wav
# Clonage vocal Qwen3-TTS (ECAPA-TDNN, GPU MLX)
.build/release/speech speak "Text in the cloned voice" \
--voice-sample reference.wav -o output.wav
Exemples
# CosyVoice3 : clonage vocal multilingue (9 langues)
.build/release/speech speak "Hello, this is my cloned voice." \
--engine cosyvoice --voice-sample my_voice.wav -o cloned_hello.wav
# CosyVoice3 : cloner une voix dans une autre langue
.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 : clonage vocal anglais
.build/release/speech speak "The quick brown fox jumps over the lazy dog." \
--voice-sample recording_15s.wav -o cloned_fox.wav
Dialogue multi-locuteurs
CosyVoice3 prend en charge les dialogues multi-locuteurs avec clonage vocal par locuteur. Utilisez l'option --speakers pour associer les tags de locuteur aux fichiers audio de référence :
# Dialogue à deux locuteurs avec clonage vocal
.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 avec tags d'émotion + clonage vocal
.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
# Ajuster le silence entre les tours
.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
L'audio de référence de chaque locuteur est traité par l'encodeur CAM++ pour extraire un embedding 192 dim. Le modèle est chargé une fois et réutilisé pour tous les locuteurs. Voir le guide CosyVoice3 pour tous les détails sur la syntaxe de dialogue et les tags d'émotion.
Conseils sur l'audio de référence
- Durée : 5 à 15 secondes de parole fonctionnent le mieux. Des clips plus courts peuvent ne pas capturer assez de caractéristiques vocales ; des clips plus longs donnent des rendements décroissants.
- Locuteur unique : La référence ne doit contenir qu'un seul locuteur. Un audio multi-locuteurs produira des résultats imprévisibles.
- Audio propre : Minimisez le bruit de fond, la musique et la réverbération. Utilisez le module d'amélioration de la parole pour nettoyer les références bruitées avant le clonage.
- Parole naturelle : Utilisez une parole conversationnelle et naturelle plutôt que des chuchotements, cris ou chants.
Pour Qwen3-TTS, le clonage vocal fonctionne uniquement avec le modèle base — pas avec customVoice. Le clonage vocal de CosyVoice3 fonctionne avec le modèle par défaut.
API Swift
import CosyVoiceTTS
// Clonage vocal CosyVoice3
let model = try await CosyVoiceTTSModel.fromPretrained()
let speaker = try await CamPlusPlusSpeaker.fromPretrained()
// Extraire l'embedding de locuteur 192 dim de l'audio de référence
let embedding = try speaker.embed(audio: refSamples, sampleRate: 16000)
// Synthétiser avec la voix clonée
let audio = model.synthesize(
text: "Hello in a cloned voice!",
speakerEmbedding: embedding
)
// Avec instruction personnalisée + embedding de locuteur
let styledAudio = model.synthesize(
text: "Hello!",
instruction: "Speak happily and with excitement.",
speakerEmbedding: embedding
)
// Dialogue multi-locuteurs
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
// Clonage vocal Qwen3-TTS
let model = try await Qwen3TTSModel.fromPretrained()
let audio = model.synthesizeWithVoiceClone(
text: "Hello in a cloned voice!",
referenceAudio: refSamples,
referenceSampleRate: 24000
)
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()