Création du module pmoaudio

This commit is contained in:
2025-10-11 00:33:13 +02:00
parent fa0206a3f9
commit e70537ed1b
16 changed files with 2318 additions and 0 deletions

165
pmoaudio/README.md Normal file
View File

@@ -0,0 +1,165 @@
# 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 :
```rust
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
```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
MIT