# 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<Vec<f32>>` 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 :

    pub struct AudioChunk {
        pub order: u64,                  // Numéro d'ordre
        pub left: Arc<Vec<f32>>,         // Canal gauche (partagé)
        pub right: Arc<Vec<f32>>,        // 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<AudioChunk>` 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<Arc<AudioChunk>>`)
- 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

    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

    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

    # 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

    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