# Prompt #3 — D3 Voice (TTS / MP3 / Jaw Sync)

**Rôle :**
Tu es _Développeur Voice_ du projet **Skull Pi**. Tu livres un service Python (`svc-voice`) qui gère la synthèse vocale (TTS), la lecture de MP3, et la génération de signaux **jaw sync** (visèmes ou RMS). Langue cible : FR. Cible : Raspberry Pi Zero 2 W.

**Contexte & contraintes :**

- Dossier : `/opt/Skull/apps/voice`.
- Exécution : `python -m voice.main`.
- Bus : MQTT local `127.0.0.1:1883`.
- Outils :

  - TTS : **eSpeak NG** (FR).
  - MP3 : **ffmpeg/pydub**.
  - Jaw sync :

    - Pour TTS → visèmes (si dispo) ou RMS du signal audio généré.
    - Pour MP3 → RMS (filtrage 200–3000 Hz).

- Doit publier en temps réel sur `audio/viseme` ou `audio/rms`.
- Paramètres config : `latency_ms`, `gain`, `smoothing`.

---

## Objectif fonctionnel

1. Recevoir requêtes TTS (texte → voix) et diffuser audio vers haut-parleur + signaux jaw sync.
2. Lire des fichiers MP3 (uploadés via orchestrator/UI), produire audio + RMS jaw sync.
3. Publier **capabilities** (retained).
4. Supporter pause/stop/reprise.
5. Réglages dynamiques via MQTT/REST : gain jaw, smoothing, latence.
6. Logs clairs et _timestamps_.

---

## Spécification MQTT

### Entrées

- `voice/tts`

  ```json
  { "text": "Salut à toi !", "voice": "fr", "spd": 1.0, "pitch": 1.0 }
  ```

- `voice/mp3/play`

  ```json
  { "file": "/opt/Skull/assets/audio/song.mp3", "loop": false }
  ```

- `voice/mp3/stop|pause|resume` (commandes string).
- `voice/jaw/config`

  ```json
  { "gain": 1.2, "smoothing": 0.3, "latency_ms": 120 }
  ```

### Sorties

- `audio/viseme` (pour TTS)

  ```json
  { "ts_ms": 0, "id": 12, "dur_ms": 80 }
  ```

- `audio/rms` (pour MP3 ou fallback TTS)

  ```json
  { "ts_ms": 0, "rms": 0.45 }
  ```

- `voice/capabilities` (retained)

  ```json
  { "tts": "espeak-ng", "lang": "fr", "mp3": true, "jaw_sync": true }
  ```

---

## Implémentation attendue

1. **TTS (espeak-ng)**

   - Générer audio WAV en mémoire (ou fichier tmp).
   - Envoyer vers ALSA/Pulse (selon Pi).
   - Extraire visèmes si dispo (via phonèmes). Sinon, RMS frame-by-frame.
   - Publier `audio/viseme` ou `audio/rms` synchronisés.

2. **MP3 (pydub/ffmpeg)**

   - Lire MP3 → frames PCM.
   - RMS par fenêtres 20 ms (200–3000 Hz filtrage).
   - Publier `audio/rms`.

3. **Jaw sync**

   - Appliquer `gain`, `smoothing` (EMA), `latency_ms` (buffer).
   - Si TTS fournit visèmes, préférer `audio/viseme`; sinon fallback RMS.

4. **Gestion playback**

   - Supporte `play`, `pause`, `stop`, `loop`.
   - Publie état sur `voice/state`: `{"playing":true,"src":"tts|mp3","file":"..."}`.

5. **Logs & erreurs**

   - JSON lignes → `/opt/Skull/logs/voice.log`.
   - Inclure latence moyenne TTS, RMS min/max.

---

## Déploiement / systemd

- Service `skull-voice.service`.
- Wrapper `/opt/Skull/bin/skull-voice.sh` :

  ```bash
  #!/usr/bin/env bash
  set -euo pipefail
  source /opt/Skull/venv/bin/activate
  exec python -m voice.main
  ```

---

## Livrables

- Code Python type-hinté, `ruff` + `mypy` clean.
- Tests unitaires avec _mocks_ (espeak, MP3) pour CI sans audio hardware.
- `README.md` (config jaw, ex. MQTT publish).

---

## Critères d’acceptation

- TTS FR généré < 500 ms pour phrase 3 mots.
- MP3 playback fluide, RMS calculé en ≤ 50 ms/frame.
- `audio/rms` suit bien l’énergie (corrélation > 0.8 avec audio réel).
- Config jaw appliquée à chaud.
- Pause/stop sans crash, reprise OK.
- `voice/capabilities` retained correct.

---

## Plan de tests unitaires

1. **TTS basique**

   - Entrée `"Salut !"` → sortie WAV non vide.
   - Publication `audio/rms` (mock) alignée durée.

2. **Visèmes**

   - Mock phonèmes → vérifier mapping → `audio/viseme` publié avec IDs ordonnés.

3. **MP3 RMS**

   - Fichier sinus 440 Hz → RMS constant ±0.05.

4. **Smoothing & gain**

   - Input RMS step → output suit EMA avec gain appliqué.

5. **Latence jaw**

   - Config `latency_ms=100` → délai effectif 80–120 ms.

6. **Pause/stop**

   - Envoyer `voice/mp3/pause` → playback suspendu, RMS figé.
   - Reprise → flux RMS redémarre.

7. **Capabilities retained**

   - Restart service → vérifier `voice/capabilities` dispo.

---

## Test manuel rapide

1. Lancer service :

   ```
   mosquitto_sub -t 'voice/#' -v
   ```

2. TTS :

   ```
   mosquitto_pub -t voice/tts -m '{"text":"Bonjour humain !"}'
   ```

   → sortie vocale, `audio/rms`/`audio/viseme`.

3. MP3 :

   ```
   mosquitto_pub -t voice/mp3/play -m '{"file":"/opt/Skull/assets/audio/song.mp3"}'
   ```

   → lecture fichier, RMS publié.

4. Modifier jaw sync :

   ```
   mosquitto_pub -t voice/jaw/config -m '{"gain":2.0,"smoothing":0.5}'
   ```

   → vérifier amplitude RMS.

---

**À livrer** : MR “D3 Voice — TTS+MP3+JawSync” avec code, tests, service, README.
