Nhân bản giọng nói
Nhân bản bất kỳ giọng nói nào từ một mẫu âm thanh tham chiếu ngắn. Cả Qwen3-TTS và CosyVoice3 đều hỗ trợ nhân bản giọng nói với các speaker encoder khác nhau — lần lượt là ECAPA-TDNN (1024 chiều) và CAM++ (192 chiều).
Cơ chế hoạt động
- Ghi âm hoặc cung cấp một mẫu âm thanh tham chiếu của giọng nói mục tiêu
- Trích xuất embedding người nói — một speaker encoder xử lý âm thanh tham chiếu thành một vector embedding có chiều cố định
- Tiêm embedding — embedding người nói điều kiện hóa mô hình TTS trong quá trình tổng hợp
- Tổng hợp giọng nói — mô hình TTS sinh ra giọng nói khớp với các đặc tính giọng của mẫu tham chiếu
Engine
Nhân bản giọng nói khả dụng với cả hai engine TTS. Mỗi engine dùng một speaker encoder khác nhau:
| Engine | Speaker Encoder | Embedding | Backend |
|---|---|---|---|
| Qwen3-TTS | ECAPA-TDNN | x-vector 1024 chiều | MLX (GPU) |
| CosyVoice3 | CAM++ | 192 chiều | CoreML (Neural Engine) |
CosyVoice3 + CAM++
CosyVoice3 sử dụng speaker encoder CAM++ (Context-Aware Masking++) từ dự án 3D-Speaker của Alibaba. Embedding 192 chiều điều kiện hóa mô hình DiT flow thông qua một lớp affine projection (192 → 80) được huấn luyện đồng thời với CosyVoice3.
Kiến trúc CAM++
| Giai đoạn | Mô tả |
|---|---|
| FCM | Module convolution đầu cuối (Conv2d + 2 ResBlock, 32 kênh) |
| TDNN | Time Delay Neural Network (320 đến 128 kênh, kernel size 5) |
| D-TDNN blocks | 3 block kết nối dày đặc (12/24/16 lớp) với context-aware masking |
| Stats Pool | Pooling trung bình + độ lệch chuẩn (thống kê toàn cục) |
| Dense | Linear projection xuống embedding 192 chiều |
Mô hình CoreML (~14 MB, FP16) chạy trên Neural Engine. Nó được tải tự động từ aufklarer/CamPlusPlus-Speaker-CoreML ở lần dùng đầu tiên.
Nhân bản giọng nói với Qwen3-TTS
Qwen3-TTS hỗ trợ hai chế độ nhân bản giọng nói:
Chế độ ICL (khuyến nghị)
Chế độ In-Context Learning mã hóa âm thanh tham chiếu thành các token codec qua bộ encoder của Mimi speech tokenizer và đặt chúng trước bản chép lại của tham chiếu. Điều này cung cấp cho mô hình ngữ cảnh âm học đầy đủ — chất lượng cao hơn và EOS đáng tin cậy (sửa các vấn đề với văn bản ngắn và các ngôn ngữ phi tiếng Anh).
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
)
Chế độ X-Vector
Dùng một encoder ECAPA-TDNN sinh ra x-vector 1024 chiều. Không cần bản chép lại nhưng chất lượng thấp hơn. Có thể không phát ra EOS trên văn bản ngắn hoặc một số ngôn ngữ nhất định.
Kiến trúc ECAPA-TDNN
| Giai đoạn | Mô tả |
|---|---|
| TDNN | Time Delay Neural Network (128 đến 512 kênh, kernel size 5) |
| SE-Res2Net blocks | 3 block với Squeeze-and-Excitation (512 kênh, dilation 2/3/4) |
| MFA | Multi-layer Feature Aggregation (1536 kênh + ReLU) |
| ASP | Attentive Statistics Pooling (1536 kênh, softmax theo thời gian) |
| FC | Lớp fully connected (3072 đến 1024 chiều) |
Các trọng số (76 tham số) được bao gồm trong safetensors của Qwen3-TTS — không cần tải xuống riêng.
Sử dụng CLI
# Nhân bản giọng CosyVoice3 (CAM++, CoreML Neural Engine)
.build/release/speech speak "Text in the cloned voice" \
--engine cosyvoice --voice-sample reference.wav -o output.wav
# Nhân bản giọng Qwen3-TTS (ECAPA-TDNN, MLX GPU)
.build/release/speech speak "Text in the cloned voice" \
--voice-sample reference.wav -o output.wav
Ví dụ
# CosyVoice3: nhân bản giọng đa ngôn ngữ (9 ngôn ngữ)
.build/release/speech speak "Hello, this is my cloned voice." \
--engine cosyvoice --voice-sample my_voice.wav -o cloned_hello.wav
# CosyVoice3: nhân bản giọng ở một ngôn ngữ khác
.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: nhân bản giọng tiếng Anh
.build/release/speech speak "The quick brown fox jumps over the lazy dog." \
--voice-sample recording_15s.wav -o cloned_fox.wav
Hội thoại nhiều người nói
CosyVoice3 hỗ trợ hội thoại nhiều người nói với nhân bản giọng nói theo từng người. Dùng cờ --speakers để ánh xạ các thẻ người nói tới các tệp âm thanh tham chiếu:
# Hội thoại hai người với nhân bản giọng
.build/release/speech speak "[S1] Hello there! [S2] Hey, how are you?" \
--engine cosyvoice --speakers s1=alice.wav,s2=bob.wav -o dialogue.wav
# Hội thoại với thẻ cảm xúc + nhân bản giọng
.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
# Điều chỉnh khoảng lặng giữa các lượt nói
.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
Âm thanh tham chiếu của mỗi người nói được xử lý qua encoder CAM++ để trích xuất một embedding 192 chiều. Mô hình được load một lần và tái sử dụng cho tất cả người nói. Xem hướng dẫn CosyVoice3 để biết chi tiết đầy đủ về cú pháp hội thoại và thẻ cảm xúc.
Mẹo về âm thanh tham chiếu
- Thời lượng: 5 đến 15 giây giọng nói hoạt động tốt nhất. Đoạn ngắn hơn có thể không nắm bắt đủ đặc tính giọng nói; đoạn dài hơn mang lại lợi ích giảm dần.
- Một người nói duy nhất: Tham chiếu chỉ nên chứa một người nói. Âm thanh nhiều người nói sẽ tạo ra kết quả không thể đoán trước.
- Âm thanh sạch: Giảm thiểu tiếng ồn nền, nhạc, và tiếng vọng. Dùng module nâng cao chất lượng giọng nói để làm sạch các tham chiếu nhiễu trước khi nhân bản.
- Giọng nói tự nhiên: Dùng giọng nói tự nhiên, mang tính hội thoại thay vì thì thầm, hét lên, hoặc hát.
Với Qwen3-TTS, nhân bản giọng nói chỉ hoạt động với mô hình base — không phải customVoice. Nhân bản giọng nói CosyVoice3 hoạt động với mô hình mặc định.
API Swift
import CosyVoiceTTS
// Nhân bản giọng CosyVoice3
let model = try await CosyVoiceTTSModel.fromPretrained()
let speaker = try await CamPlusPlusSpeaker.fromPretrained()
// Trích xuất embedding người nói 192 chiều từ âm thanh tham chiếu
let embedding = try speaker.embed(audio: refSamples, sampleRate: 16000)
// Tổng hợp với giọng đã nhân bản
let audio = model.synthesize(
text: "Hello in a cloned voice!",
speakerEmbedding: embedding
)
// Với chỉ dẫn tùy chỉnh + embedding người nói
let styledAudio = model.synthesize(
text: "Hello!",
instruction: "Speak happily and with excitement.",
speakerEmbedding: embedding
)
// Hội thoại nhiều người nói
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
// Nhân bản giọng Qwen3-TTS
let model = try await Qwen3TTSModel.fromPretrained()
let audio = model.synthesizeWithVoiceClone(
text: "Hello in a cloned voice!",
referenceAudio: refSamples,
referenceSampleRate: 24000
)
Cache âm thanh tham chiếu
Cả synthesizeWithVoiceClone (x-vector) và synthesizeWithVoiceCloneICL (ICL) đều cache phần tiền xử lý theo tham chiếu giữa các lời gọi trên cùng một instance mô hình. Đường x-vector cache embedding người nói ECAPA-TDNN; đường ICL còn cache thêm đầu ra của bộ encoder codec Mimi. Cache được định địa chỉ theo nội dung (hash của các mẫu thô + sample rate) và bị giới hạn bởi một LRU nhỏ (mặc định 4 mục), vì vậy các lần tạo lặp lại với cùng dạng sóng tham chiếu sẽ bỏ qua các bước mel + encoder mà không tăng bộ nhớ vô hạn.
let tts = try await Qwen3TTSModel.fromPretrained()
// Lần gọi đầu tiên: chạy ECAPA-TDNN, cache embedding
_ = tts.synthesizeWithVoiceClone(text: "Hello", referenceAudio: ref, ...)
// Các lần gọi sau với cùng tham chiếu: cache hit
_ = tts.synthesizeWithVoiceClone(text: "How are you?", referenceAudio: ref, ...)
// Giải phóng tường minh (hiếm khi cần — LRU xử lý dung lượng)
tts.clearReferenceAudioCache()