استنساخ الصوت
استنسخ أي صوت انطلاقًا من عيّنة صوتية مرجعية قصيرة. يدعم كلٌّ من Qwen3-TTS وCosyVoice3 استنساخ الصوت باستخدام encoders مختلفة للمتحدّث — ECAPA-TDNN (1024 بُعدًا) وCAM++ (192 بُعدًا) على التوالي.
كيف يعمل
- سجّل أو وفّر عيّنة صوتية مرجعية للصوت المستهدف
- استخراج embedding المتحدّث — يقوم encoder المتحدّث بمعالجة الصوت المرجعي للحصول على متجه embedding ذي بُعد ثابت
- حقن الـembedding — يُكيّف embedding المتحدّث نموذج TTS أثناء التوليد
- توليد الكلام — يولّد نموذج TTS كلامًا يطابق الخصائص الصوتية للعيّنة المرجعية
المحرّكات
يتوفّر استنساخ الصوت مع كلا محرّكَي TTS. يستخدم كلٌّ منهما encoder متحدّث مختلف:
| المحرّك | encoder المتحدّث | Embedding | Backend |
|---|---|---|---|
| Qwen3-TTS | ECAPA-TDNN | x-vector بـ1024 بُعدًا | MLX (GPU) |
| CosyVoice3 | CAM++ | 192 بُعدًا | CoreML (Neural Engine) |
CosyVoice3 + CAM++
يستخدم CosyVoice3 encoder المتحدّث CAM++ (Context-Aware Masking++) من مشروع 3D-Speaker التابع لـAlibaba. يُكيّف embedding ذو الـ192 بُعدًا نموذج DiT flow عبر طبقة إسقاط أفينية (192 → 80) دُرِّبت بالتزامن مع CosyVoice3.
بنية CAM++
| المرحلة | الوصف |
|---|---|
| FCM | وحدة التفاف أمامية (Conv2d + 2 ResBlocks، 32 قناة) |
| TDNN | Time Delay Neural Network (من 320 إلى 128 قناة، حجم النواة 5) |
| كتل D-TDNN | 3 كتل مترابطة بكثافة (12/24/16 طبقة) مع context-aware masking |
| Stats Pool | تجميع المتوسّط + الانحراف المعياري (إحصاءات شاملة) |
| Dense | إسقاط خطّي إلى embedding بـ192 بُعدًا |
يعمل نموذج CoreML (نحو 14 MB، FP16) على Neural Engine. يُنزَّل تلقائيًا من aufklarer/CamPlusPlus-Speaker-CoreML عند أوّل استخدام.
استنساخ الصوت في Qwen3-TTS
يدعم Qwen3-TTS وضعَين لاستنساخ الصوت:
وضع ICL (موصى به)
يُرمِّز وضع In-Context Learning الصوتَ المرجعي إلى رموز codec عبر encoder مُجزّئ الكلام Mimi ويُسبِقه بالنص المرجعي. وهذا يمنح النموذج سياقًا صوتيًا كاملًا — جودة أعلى و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
يستخدم encoder من نوع ECAPA-TDNN يُنتج x-vector بـ1024 بُعدًا. لا يتطلّب نصًا مرجعيًا لكن جودته أقل. قد يفشل في إصدار EOS مع النصوص القصيرة أو بعض اللغات.
بنية ECAPA-TDNN
| المرحلة | الوصف |
|---|---|
| TDNN | Time Delay Neural Network (من 128 إلى 512 قناة، حجم النواة 5) |
| كتل SE-Res2Net | 3 كتل مع Squeeze-and-Excitation (512 قناة، توسّع 2/3/4) |
| MFA | Multi-layer Feature Aggregation (1536 قناة + ReLU) |
| ASP | Attentive Statistics Pooling (1536 قناة، softmax عبر الزمن) |
| FC | طبقة متّصلة بالكامل (من 3072 إلى 1024 بُعدًا) |
الأوزان (76 معاملًا) مضمّنة في ملفات safetensors الخاصّة بـQwen3-TTS — لا حاجة إلى تنزيل منفصل.
استخدام 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
# 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
حوار متعدّد المتحدّثين
يدعم CosyVoice3 الحوارات متعدّدة المتحدّثين مع استنساخ صوت لكلّ متحدّث. استخدم الـflag --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
يُعالَج الصوت المرجعي لكلّ متحدّث عبر encoder CAM++ لاستخراج embedding بـ192 بُعدًا. يُحمَّل النموذج مرّة واحدة ويُعاد استخدامه لجميع المتحدّثين. راجع دليل CosyVoice3 لمزيد من التفاصيل حول صياغة الحوار ووسوم الانفعال.
نصائح للصوت المرجعي
- المدّة: من 5 إلى 15 ثانية من الكلام تُعطي أفضل نتيجة. المقاطع الأقصر قد لا تلتقط ما يكفي من الخصائص الصوتية؛ والمقاطع الأطول تُعطي عوائد متناقصة.
- متحدّث وحيد: يجب أن يحتوي المرجع على متحدّث واحد فقط. الصوت متعدّد المتحدّثين يُنتج نتائج غير متوقّعة.
- صوت نظيف: قلِّل ضجيج الخلفية والموسيقى والصدى. استخدم وحدة تحسين الكلام لتنظيف المراجع الضوضائية قبل الاستنساخ.
- كلام طبيعي: استخدم كلامًا حواريًا طبيعيًا بدلًا من الهمس أو الصراخ أو الغناء.
بالنسبة إلى Qwen3-TTS، يعمل استنساخ الصوت مع نموذج base فقط — وليس مع customVoice. أمّا استنساخ الصوت في CosyVoice3 فيعمل مع النموذج الافتراضي.
واجهة 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
)
تخزين الصوت المرجعي مؤقتًا
تُخزِّن كلّ من synthesizeWithVoiceClone (x-vector) وsynthesizeWithVoiceCloneICL (ICL) المعالجة المسبقة لكلّ مرجع عبر الاستدعاءات على نفس نسخة النموذج. يُخزِّن مسار x-vector embedding المتحدّث الناتج عن ECAPA-TDNN؛ ويُخزِّن مسار ICL إضافةً إلى ذلك مخرجات encoder codec Mimi. الذاكرة المؤقّتة مفهرسة بالمحتوى (تجزئة العيّنات الخام + معدّل العيّنات) ومحدودة بسعة LRU صغيرة (4 مدخلات افتراضيًا)، بحيث تتخطّى التوليدات المتكرّرة على نفس الموجة المرجعية تمريرات mel وencoder دون نموّ غير محدود للذاكرة.
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()