init sync

This commit is contained in:
2025-07-04 17:02:28 +02:00
commit 9d86f322f2
30 changed files with 2523 additions and 0 deletions

112
README.md Normal file
View File

@@ -0,0 +1,112 @@
# ox_speak Architecture Audio Temps Réel en Rust
## 📦 Structure actuelle du projet
```text
src/
├── main.rs # Point d'entrée, instancie App et démarre l'orchestration
├── lib.rs # Déclarations des modules principaux
├── app/ # Initialisation, gestion de haut niveau de l'application
│ ├── mod.rs
│ └── app.rs # Struct App : contient les stacks (audio, net) et le EventBus
├── core/ # Traitements audio bas-niveau (CPAL, Opus, stats...)
│ ├── mod.rs
│ ├── capture.rs # Capture micro avec CPAL
│ ├── playback.rs # Playback via CPAL
│ ├── mixer.rs # Mixage audio
│ ├── opus.rs # Encodage/Décodage Opus
│ ├── stats.rs # Statistiques audio
│ └── rms.rs # Détection de silence via RMS
├── domain/ # Types globaux et événements partagés
│ ├── mod.rs
│ └── event.rs # EventBus (struct + enum Event) pour routing interne
├── network/ # Communication réseau (UDP, protocole)
│ ├── mod.rs
│ ├── udp.rs # Client UDP (envoi/réception)
│ └── protocol.rs # Types et parsing de messages réseau
├── runtime/ # Logique d'exécution centrale (orchestration)
│ ├── mod.rs
│ └── dispatcher.rs # Dispatcher central (consomme les Event)
```
---
## 🛠️ Tâches à faire dans lordre
1. **Compléter `domain/event.rs`**
- Définir les `enum Event` utilisés dans ton application (ex : `AudioIn`, `EncodedFrame`, etc.)
- Structurer `EventBus` avec `crossbeam_channel`
2. **Écrire un `dispatcher` simple dans `runtime/dispatcher.rs`**
- Boucle bloquante `while let Ok(event) = rx.recv()`
- Match sur chaque type dévénement (→ faire une "god function" au début)
- Appeler directement les fonctions concernées (ex : `udp.send()`, `playback.play()`…)
3. **Initialiser `App` dans `app/app.rs`**
- Créer une struct `App` contenant :
- le `EventBus`
- les instances des modules nécessaires (`capture`, `udp`, etc.)
- Méthode `run()` qui spawn les threads dentrée (`audio`, `network`), et lance le `dispatcher`
4. **Compléter les modules `core/` et `network/`**
- Rendre chaque module autonome, avec une méthode `start()` ou `run()` prenant un `Sender<Event>`
- Exemple : `capture.run(tx)` capture le micro et envoie des `AudioIn(Vec<f32>)`
5. **Dans `main.rs`**
- Créer `App`, puis appeler `app.run()`.
6. (optionnel) **Plus tard : découpler le dispatcher en handlers**
- Créer un `trait EventHandler` pour déléguer proprement la logique métier
- Injecter les handlers dans le dispatcher pour garder `dispatcher.rs` léger
---
## 🔄 Orchestration globale (comment tout communique)
```text
THREADS :
- capture_thread : génère AudioIn
- opus_encode_thread: encode AudioIn → EncodedFrame
- udp_send_thread : reçoit EncodedFrame → envoie réseau
- udp_recv_thread : reçoit NetIn → decode → AudioDecoded
- playback_thread : lit AudioDecoded
TOUS utilisent :
Sender<Event> (cloné)
EventBus central (domain/event.rs)
RECEIVER :
dispatcher (runtime/dispatcher.rs)
→ lit les events
→ décide quoi faire : encode, envoyer, lire...
```
---
## 📝 Remarques
- Tu peux rester **mono-thread** dans un premier temps pour simplifier le flow.
- Lapproche actuelle (dispatcher = "god function") est très bien pour commencer **et lisible**.
- Tu peux logguer chaque événement dans le dispatcher pour debugger le flux.
- `EventBus` est cloné et injecté dans chaque module. **Aucun module ne s'appelle entre eux.**
---
## 🧠 Notes de conception
- Tu as choisi de garder la génération de la trame encodée **dans le thread de capture**, ce qui est parfaitement logique ici : tu as 20 ms de temps CPU garanti dans le callback CPAL pour encoder et publier un `Event::EncodedFrame`.
- Cette approche évite la surcharge dun thread supplémentaire et permet une architecture **performante et simple** tant que tu restes dans les contraintes temps réel.
---
## ❓Question
> Pour le moment, elle ne se pose pas, car en réalité je vais garder l'ancienne logique du "c'est capture qui va générer la frame encodée", vu que le thread a 20 ms pour bosser entre chaque callback cpal, il a largement le temps de le faire.
Donc aucun doute à ce stade. Si tu changes davis ou que la logique devient trop lourde dans `capture`, tu pourras refactorer vers un modèle où `AudioIn` est un event brut et l'encodage est fait côté dispatcher ou dans un worker dédié.