การโคลนเสียง
โคลนเสียงใดก็ได้จากตัวอย่างเสียงอ้างอิงสั้น ๆ ทั้ง Qwen3-TTS และ CosyVoice3 รองรับการโคลนเสียงด้วย speaker encoder ที่ต่างกัน — ECAPA-TDNN (1024 มิติ) และ CAM++ (192 มิติ) ตามลำดับ
วิธีการทำงาน
- บันทึกหรือจัดเตรียม ตัวอย่างเสียงอ้างอิงของเสียงเป้าหมาย
- การสกัด embedding ผู้พูด — speaker encoder ประมวลผลเสียงอ้างอิงเป็น vector embedding ที่มีจำนวนมิติคงที่
- การฉีด embedding — embedding ผู้พูดกำหนดเงื่อนไขโมเดล TTS ระหว่างการสังเคราะห์
- การสังเคราะห์เสียงพูด — โมเดล TTS สร้างเสียงพูดที่ตรงกับลักษณะเสียงของตัวอย่างอ้างอิง
Engine
การโคลนเสียงพร้อมใช้กับทั้งสอง engine TTS แต่ละ engine ใช้ speaker encoder ที่ต่างกัน:
| Engine | Speaker Encoder | Embedding | Backend |
|---|---|---|---|
| Qwen3-TTS | ECAPA-TDNN | x-vector 1024 มิติ | MLX (GPU) |
| CosyVoice3 | CAM++ | 192 มิติ | CoreML (Neural Engine) |
CosyVoice3 + CAM++
CosyVoice3 ใช้ speaker encoder CAM++ (Context-Aware Masking++) จากโครงการ 3D-Speaker ของ Alibaba Embedding 192 มิติกำหนดเงื่อนไขโมเดล DiT flow ผ่านชั้น affine projection (192 → 80) ที่ได้รับการฝึกพร้อมกับ CosyVoice3
สถาปัตยกรรม CAM++
| ขั้นตอน | คำอธิบาย |
|---|---|
| FCM | โมดูล convolution ส่วนหน้า (Conv2d + 2 ResBlock, 32 ช่อง) |
| TDNN | Time Delay Neural Network (320 ถึง 128 ช่อง, kernel size 5) |
| D-TDNN blocks | 3 บล็อกที่เชื่อมต่อหนาแน่น (12/24/16 ชั้น) ด้วย context-aware masking |
| Stats Pool | การ pooling ค่าเฉลี่ย + ส่วนเบี่ยงเบนมาตรฐาน (สถิติทั่วโลก) |
| Dense | Linear projection ลงเป็น embedding 192 มิติ |
โมเดล CoreML (~14 MB, FP16) ทำงานบน Neural Engine ระบบจะดาวน์โหลดอัตโนมัติจาก aufklarer/CamPlusPlus-Speaker-CoreML เมื่อใช้งานครั้งแรก
การโคลนเสียงด้วย Qwen3-TTS
Qwen3-TTS รองรับโหมดการโคลนเสียง 2 โหมด:
โหมด ICL (แนะนำ)
โหมด In-Context Learning เข้ารหัสเสียงอ้างอิงเป็น codec token ผ่าน encoder ของ 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
ใช้ encoder ECAPA-TDNN ที่สร้าง x-vector 1024 มิติ ไม่จำเป็นต้องมีบทถอดความแต่คุณภาพต่ำกว่า อาจไม่สามารถปล่อย EOS ในข้อความสั้นหรือบางภาษา
สถาปัตยกรรม ECAPA-TDNN
| ขั้นตอน | คำอธิบาย |
|---|---|
| TDNN | Time Delay Neural Network (128 ถึง 512 ช่อง, kernel size 5) |
| SE-Res2Net blocks | 3 บล็อกที่มี Squeeze-and-Excitation (512 ช่อง, dilation 2/3/4) |
| MFA | Multi-layer Feature Aggregation (1536 ช่อง + ReLU) |
| ASP | Attentive Statistics Pooling (1536 ช่อง, softmax ตามเวลา) |
| FC | ชั้น fully connected (3072 ถึง 1024 มิติ) |
น้ำหนัก (76 พารามิเตอร์) รวมอยู่ใน safetensors ของ Qwen3-TTS — ไม่จำเป็นต้องดาวน์โหลดแยก
การใช้งาน CLI
# การโคลนเสียง CosyVoice3 (CAM++, CoreML Neural Engine)
.build/release/speech speak "Text in the cloned voice" \
--engine cosyvoice --voice-sample reference.wav -o output.wav
# การโคลนเสียง Qwen3-TTS (ECAPA-TDNN, MLX GPU)
.build/release/speech speak "Text in the cloned voice" \
--voice-sample reference.wav -o output.wav
ตัวอย่าง
# CosyVoice3: การโคลนเสียงหลายภาษา (9 ภาษา)
.build/release/speech speak "Hello, this is my cloned voice." \
--engine cosyvoice --voice-sample my_voice.wav -o cloned_hello.wav
# CosyVoice3: โคลนเสียงในภาษาอื่น
.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: การโคลนเสียงภาษาอังกฤษ
.build/release/speech speak "The quick brown fox jumps over the lazy dog." \
--voice-sample recording_15s.wav -o cloned_fox.wav
บทสนทนาหลายผู้พูด
CosyVoice3 รองรับบทสนทนาหลายผู้พูดพร้อมการโคลนเสียงเฉพาะของแต่ละผู้พูด ใช้แฟล็ก --speakers เพื่อ map แท็กผู้พูดไปยังไฟล์เสียงอ้างอิง:
# บทสนทนาสองผู้พูดพร้อมการโคลนเสียง
.build/release/speech speak "[S1] Hello there! [S2] Hey, how are you?" \
--engine cosyvoice --speakers s1=alice.wav,s2=bob.wav -o dialogue.wav
# บทสนทนาพร้อมแท็กอารมณ์ + การโคลนเสียง
.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
# ปรับช่องเงียบระหว่างผลัด
.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 ทำงานกับโมเดลค่าเริ่มต้น
API Swift
import CosyVoiceTTS
// การโคลนเสียง CosyVoice3
let model = try await CosyVoiceTTSModel.fromPretrained()
let speaker = try await CamPlusPlusSpeaker.fromPretrained()
// สกัด embedding ผู้พูด 192 มิติจากเสียงอ้างอิง
let embedding = try speaker.embed(audio: refSamples, sampleRate: 16000)
// สังเคราะห์ด้วยเสียงที่โคลนแล้ว
let audio = model.synthesize(
text: "Hello in a cloned voice!",
speakerEmbedding: embedding
)
// พร้อมคำสั่งแบบกำหนดเอง + embedding ผู้พูด
let styledAudio = model.synthesize(
text: "Hello!",
instruction: "Speak happily and with excitement.",
speakerEmbedding: embedding
)
// บทสนทนาหลายผู้พูด
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
let model = try await Qwen3TTSModel.fromPretrained()
let audio = model.synthesizeWithVoiceClone(
text: "Hello in a cloned voice!",
referenceAudio: refSamples,
referenceSampleRate: 24000
)
Cache ของเสียงอ้างอิง
ทั้ง synthesizeWithVoiceClone (x-vector) และ synthesizeWithVoiceCloneICL (ICL) มีการ cache ขั้นตอน preprocessing สำหรับแต่ละอ้างอิงข้ามการเรียกบน instance ของโมเดลเดียวกัน เส้นทาง x-vector cache embedding ผู้พูดของ ECAPA-TDNN; เส้นทาง ICL เพิ่มเติม cache เอาต์พุตของ encoder codec Mimi Cache ระบุที่อยู่ตามเนื้อหา (hash ของตัวอย่างดิบ + sample rate) และจำกัดด้วย LRU ขนาดเล็ก (ค่าเริ่มต้น 4 รายการ) ดังนั้นการสร้างซ้ำสำหรับ waveform อ้างอิงเดียวกันจะข้ามขั้นตอน mel + encoder โดยไม่เพิ่มหน่วยความจำอย่างไม่จำกัด
let tts = try await Qwen3TTSModel.fromPretrained()
// การเรียกครั้งแรก: รัน ECAPA-TDNN, cache embedding
_ = tts.synthesizeWithVoiceClone(text: "Hello", referenceAudio: ref, ...)
// การเรียกถัด ๆ ไปด้วยอ้างอิงเดียวกัน: cache hit
_ = tts.synthesizeWithVoiceClone(text: "How are you?", referenceAudio: ref, ...)
// การไล่ออกอย่างชัดเจน (ไม่ค่อยจำเป็น — LRU จัดการความจุ)
tts.clearReferenceAudioCache()