# PMOAudio Pipeline audio stéréo async optimisé pour Rust, utilisant Tokio. ## Caractéristiques - **Pipeline push-based async** : Tous les nodes utilisent Tokio pour un traitement non-bloquant - **Zero-copy optimisé** : Les données audio sont partagées via `Arc>` pour éviter les clonages inutiles - **Support multiroom** : BufferNode avec buffer circulaire et offsets indépendants par abonné - **TimerNode** : Calcul de position temporelle en temps réel - **Backpressure** : Channels bounded avec `try_send` pour éviter les blocages ## Architecture ### AudioChunk Structure de données pour un chunk audio stéréo : ```rust pub struct AudioChunk { pub order: u64, // Numéro d'ordre pub left: Arc>, // Canal gauche (partagé) pub right: Arc>, // Canal droit (partagé) pub sample_rate: u32, // Taux d'échantillonnage } ``` Les données sont wrappées dans `Arc` pour permettre le partage sans copie entre plusieurs abonnés. ### Nodes #### SingleSubscriberNode - Un seul abonné - Pas de clone inutile du Arc #### MultiSubscriberNode - Plusieurs abonnés - Partage le même `Arc` avec tous #### SourceNode - Génère ou lit des chunks audio - Version mock avec génération de sinusoïdes pour tests #### DecoderNode - Décode les chunks audio - Supporte le passthrough et le resampling (mock) #### DspNode - Applique des transformations DSP - Clone les données uniquement si modification nécessaire - Exemple : gain, filtrage #### BufferNode - Buffer circulaire (`VecDeque>`) - Support multiroom avec offsets indépendants - `try_send` non-bloquant pour éviter de bloquer la source #### TimerNode - Node passthrough qui ne modifie pas les données - Incrémente un compteur de samples - Calcule la position : `position_sec = elapsed_samples / sample_rate` - Fournit un `TimerHandle` pour monitoring #### SinkNode - Node terminal qui consomme les chunks - Versions : silent, logging, stats, mock file writer ## Pipeline type ``` SourceNode → DecoderNode → DSPNode → BufferNode → TimerNode → SinkNode(s) ↓ Multiroom Sinks (avec offsets) ``` ## Exemples ### Pipeline simple ```rust use pmoaudio::{SinkNode, SourceNode, TimerNode}; #[tokio::main] async fn main() { let (mut timer, timer_tx) = TimerNode::new(10); let (sink, sink_tx) = SinkNode::new("Output".to_string(), 10); timer.add_subscriber(sink_tx); let timer_handle = timer.get_position_handle(); tokio::spawn(async move { timer.run().await.unwrap() }); let sink_handle = tokio::spawn(async move { sink.run_with_stats().await.unwrap() }); tokio::spawn(async move { let mut source = SourceNode::new(); source.add_subscriber(timer_tx); source.generate_chunks(30, 4800, 48000, 440.0).await.unwrap(); }); sink_handle.await.unwrap(); } ``` ### Multiroom ```rust let (buffer, buffer_tx) = BufferNode::new(50, 10); let (sink1, sink1_tx) = SinkNode::new("Room 1".to_string(), 10); let (sink2, sink2_tx) = SinkNode::new("Room 2".to_string(), 10); buffer.add_subscriber_with_offset(sink1_tx, 0).await; // Pas de délai buffer.add_subscriber_with_offset(sink2_tx, 5).await; // 5 chunks de retard ``` ## Lancer les exemples ```bash # Pipeline simple cargo run --example simple_pipeline # Pipeline complet avec tous les nodes cargo run --example pipeline_demo # Configuration multiroom cargo run --example multiroom_demo # Streaming avec timing réel cargo run --example streaming_demo ``` ## Tests ```bash cargo test ``` 20 tests unitaires couvrant : - Propagation des chunks - Calcul de position par TimerNode - BufferNode multi-abonné avec offsets - Arc sharing et zero-copy - DSP avec gain et filtrage - Resampling ## Optimisations 1. **Arc sharing** : Les `AudioChunk` sont clonés via `Arc::clone()` qui ne clone que le pointeur 2. **Copy-on-Write** : Les DSP nodes clonent les données uniquement si modification nécessaire 3. **Bounded channels** : Backpressure automatique 4. **try_send** : Non-bloquant pour BufferNode, permet de sauter des chunks si un abonné est saturé 5. **RwLock** : Pour partage concurrent du compteur TimerNode ## Dépendances - `tokio` : Runtime async et channels - `async-trait` : Traits async ## License CeCill-2.0