kazeia/RAPPORT_TTS.md

9.2 KiB

Rapport TTS - Qwen3-TTS sur OnePlus Pad 3

Date : 26 mars 2026 Objectif : Synthèse vocale avec clonage de voix en français pour le chatbot Kazeia Tablette : OnePlus Pad 3 (Snapdragon 8 Elite, NPU Hexagon HTP v79)


1. Modèles TTS évalués

1.1 Chatterbox Multilingual (ONNX)

Caractéristique Détail
Repo onnx-community/chatterbox-multilingual-ONNX
Architecture LLM 0.5B (Llama) + speech encoder + conditional decoder
Français Oui (23 langues)
Clonage vocal Oui
Format ONNX (FP32, FP16, Q4F16)
Taille totale ~1.5 Go (Q4F16)

Résultat : Le modèle charge et génère des tokens mais le décodage audio ne fonctionne que sur la première seconde. Les versions quantifiées (FP16, Q4F16) ne produisent pas le stop token, résultant en du silence après le début. Seul le FP32 (2 Go LM) fonctionne correctement mais uniquement pour des phrases très courtes.

Verdict : Non fiable pour la production.

1.2 Qwen3-TTS (PyTorch natif)

Caractéristique 0.6B Base 1.7B CustomVoice 1.7B Base
Clonage vocal Oui (x_vector) Non (voix intégrées) Oui (x_vector)
Français Oui Oui Oui
Taille FP32 ~1.8 Go ~4.5 Go ~4.5 Go
Qualité clonage Bonne N/A Bonne

Résultat : Fonctionne parfaitement sur CPU avec les voix françaises du projet Kazeia.


2. Tests Qwen3-TTS sur CPU (PC)

Phrase : "Bonjour, je comprends que vous soyez triste. Je suis là pour vous écouter."

2.1 Résultats par modèle et voix

Modèle Voix Audio Temps Vitesse Qualité
0.6B Base Damien (clone) 4.0s 18.4s 0.22x RT Bonne
0.6B Base Élodie (clone) 4.3s 18.6s 0.23x RT Bonne
1.7B CustomVoice Vivian (intégrée) 6.1s 37.8s 0.16x RT Très bonne
1.7B CustomVoice Serena (intégrée) 4.3s 26.4s 0.16x RT Très bonne
1.7B Base Damien (clone) 5.0s 30.9s 0.16x RT Très bonne
1.7B Base Élodie (clone) 3.8s 23.7s 0.16x RT Très bonne

2.2 Observations

  • Le 0.6B est ~1.4x plus rapide que le 1.7B (0.22x vs 0.16x RT)
  • Le clonage vocal fonctionne bien avec les voix françaises (accent naturel)
  • Les échantillons de référence doivent être courts (~3-10 secondes)
  • L'audio est continu et complet sur toute la durée (RMS > 0 partout)
  • Le 0.6B offre le meilleur rapport qualité/vitesse pour le clonage

3. Export du talker Qwen3-TTS pour le NPU

3.1 Architecture du modèle

Qwen3-TTS 0.6B est composé de 4 sous-modèles :

Texte + Voix ref → [Speaker Encoder] → x-vector
                        ↓
                   [Talker LM]  ← 28 couches Qwen3, 1024 hidden
                        ↓           (identique à Qwen3-0.6B LLM)
                   [Code Predictor] ← 5 couches, 15 codebooks
                        ↓
                   [Speech Decoder] ← ConvNet + Transformer
                        ↓
                      Audio WAV
Composant Params Rôle
Talker (LM) 754.8M Génère les speech tokens (autorégressif)
Code Predictor 141.6M Prédit les 15 codebooks parallèles
Speaker Encoder 8.9M Extrait l'empreinte vocale
Speech Decoder 114.3M Décode les tokens en audio

3.2 Conversion pour ExecuTorch + QNN

Le talker est architecturalement identique à Qwen3-0.6B :

  • 28 couches Transformer
  • hidden_size = 1024
  • 16 attention heads, 8 KV heads
  • head_dim = 128
  • QK norm + RoPE theta 1M
  • Seule différence : vocab_size = 3072 (codec) au lieu de 151936 (texte)

Étapes de conversion :

  1. Extraction des poids du talker depuis le modèle HuggingFace
  2. Renommage HF → Meta format (wq/wk/wv/wo, feed_forward.w1/w2/w3)
  3. Remplacement de tok_embeddings par codec_embedding (3072x1024)
  4. Suppression de text_projection et text_embedding
  5. Patch du tokenizer pour clamper les IDs de calibration à [0, 3071]
  6. Export via le pipeline ExecuTorch Qwen3-0.6B existant

Résultat : .pte de 286 Mo généré en ~20 minutes.

3.3 Test sur le NPU Hexagon

Métrique Valeur
Débit decode 90.7 tok/s
Prefill 888 tok/s
Tokens générés 503 en 5.5s
Taille .pte 286 Mo
Time to first token 9 ms

Comparaison :

Backend Talker decode (tok/s) vs CPU
NPU Hexagon (ExecuTorch) 90.7 +4.1x
CPU PC (PyTorch) ~22 baseline

3.4 Exportabilité des autres composants

Composant ExecuTorch ONNX Raison échec
Talker OK (286 Mo .pte) Échoue torch.export incompatible
Code Predictor Non Non Multi-tête (15 codebooks), non standard
Speaker Encoder Non tenté Non tenté Petit (8.9M), rapide sur CPU
Speech Decoder Non tenté Échoue ConvNet dynamique + boucles

4. Pipeline TTS recommandé pour la tablette

4.1 Architecture hybride NPU + CPU

┌─────────────────────────────────────────────┐
│                TABLETTE                      │
│                                             │
│  [Speaker Encoder]  ──→  x-vector           │
│      (CPU, 8.9M)                            │
│                                             │
│  [Talker .pte]  ──→  speech tokens          │
│      (NPU, 90 tok/s, 286 Mo)               │
│                                             │
│  [Code Predictor]  ──→  16 codebooks        │
│      (CPU, 141M, ~5 couches)               │
│                                             │
│  [Speech Decoder]  ──→  audio WAV           │
│      (CPU, 114M, ConvNet)                   │
└─────────────────────────────────────────────┘

4.2 Mesure réelle du pipeline (CPU PC) et estimation NPU

Phrase : "Bonjour, je suis là pour vous accompagner." → 2.3s d'audio

Composant % temps CPU PC NPU tablette (estimé)
Talker (LM autorégressif) 87% 9.9s 0.3s (90.7 tok/s)
Speaker Encoder + Code Predictor + Decoder 13% 1.5s ~1.5s
Total 100% 11.5s ~1.9s
Ratio temps réel 0.20x (5x trop lent) 1.24x RT

Le NPU transforme un pipeline 5x trop lent en un pipeline temps réel en accélérant le talker (le goulot d'étranglement) de 3 tok/s à 90.7 tok/s.

4.3 Prérequis pour le déploiement complet

  1. Termux sur la tablette avec Python 3.10 + PyTorch CPU
  2. Le .pte du talker déjà déployé
  3. Les poids du code predictor + speech decoder en PyTorch
  4. Un script d'orchestration qui :
    • Encode la voix de référence (CPU)
    • Génère les speech tokens via le talker .pte (NPU)
    • Prédit les codebooks (CPU)
    • Décode en audio (CPU)

5. Fichiers produits

Sur le PC

Chemin Description
models_qnn/qwen3-tts-executorch/hybrid_llama_qnn.pte Talker exporté pour NPU (286 Mo)
models_qnn/qwen3-tts-export/qwen3_tts_talker.pth Poids talker format Meta (3.1 Go)
models_qnn/qwen3-tts-export/config.json Config ExecuTorch du talker
models_qnn/chatterbox-tts/ Chatterbox ONNX (abandonné)
tts_qwen3/ Échantillons audio générés
voix_clips/ Voix françaises tronquées à 5s

Échantillons audio générés

Fichier Modèle Voix
tts_qwen3/qwen3tts_06b_damien.wav 0.6B Base Damien (clone)
tts_qwen3/qwen3tts_06b_elodie.wav 0.6B Base Élodie (clone)
tts_qwen3/qwen3tts_17b_Vivian.wav 1.7B CustomVoice Vivian
tts_qwen3/qwen3tts_17b_Serena.wav 1.7B CustomVoice Serena
tts_qwen3/qwen3tts_17b_base_damien.wav 1.7B Base Damien (clone)
tts_qwen3/qwen3tts_17b_base_elodie.wav 1.7B Base Élodie (clone)

Voix de référence françaises

8 voix disponibles dans /opt/Kazeia/voix/ (clips 5s dans voix_clips/) : Amir, Damien, Didier, Élodie, Jérôme, Richard, Sid, Zelda


6. Commandes de référence

Générer de la parole avec Qwen3-TTS (CPU PC)

from qwen_tts import Qwen3TTSModel
model = Qwen3TTSModel.from_pretrained("Qwen/Qwen3-TTS-12Hz-0.6B-Base", device_map="cpu", dtype=torch.float32)
wavs, sr = model.generate_voice_clone(
    text="Votre texte ici",
    language="French",
    ref_audio="voix_clips/damien.wav",
    ref_text="Bonjour",
    x_vector_only_mode=True,
)
soundfile.write("output.wav", wavs[0], sr)

Exporter le talker TTS pour le NPU

# 1. Convertir les poids (voir script de conversion)
# 2. Exporter via ExecuTorch (avec patch tokenizer)
python3.10 examples/qualcomm/oss_scripts/llama/llama.py \
  -m SM8750 -b build-android --decoder_model qwen3-0_6b \
  --checkpoint qwen3_tts_talker.pth --params config.json \
  -s DEVICE_ID --backend htp -c -a OUTPUT_DIR

Tester le talker sur le NPU tablette

python3.10 examples/qualcomm/oss_scripts/llama/llama.py \
  -m SM8750 -b build-android --decoder_model qwen3-0_6b \
  -s DEVICE_ID --backend htp \
  --pre_gen_pte OUTPUT_DIR -a OUTPUT_DIR --prompt "test"

Rapport généré par Claude Code (Opus 4.6)