init sync
This commit is contained in:
112
README.md
Normal file
112
README.md
Normal 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 l’ordre
|
||||
|
||||
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 d’entré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.
|
||||
- L’approche 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 d’un 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 d’avis 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é.
|
||||
Reference in New Issue
Block a user