2025-07-04 17:02:28 +02:00
2025-07-04 17:02:28 +02:00
2025-07-04 17:02:28 +02:00
2025-07-04 17:02:28 +02:00
2025-07-04 17:02:28 +02:00
2025-07-04 17:02:28 +02:00
2025-07-04 17:02:28 +02:00
2025-07-04 17:02:28 +02:00

ox_speak Architecture Audio Temps Réel en Rust

📦 Structure actuelle du projet

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)

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é.

Description
No description provided
Readme 50 KiB
Languages
Rust 100%