Files
pmomusic/pmoaudio/IMPLEMENTATION_SUMMARY.md

427 lines
13 KiB
Markdown
Raw Normal View History

2025-10-11 15:36:06 +02:00
# Résumé de l'implémentation - Extensions PMOAudio
## Objectif
Étendre le système de pipeline audio PMOAudio existant pour supporter :
- Contrôle de volume dynamique avec synchronisation master/secondaire
- Nouveaux types de sinks (Chromecast, MPD, Disk)
- Système d'événements générique pour communication inter-nodes
- Architecture multiroom avec flux dupliqués et volumes indépendants
---
## Modifications apportées
### 1. AudioChunk - Extension avec gain (src/audio_chunk.rs)
**Ajouts :**
- Champ `gain: f32` (valeur par défaut : 1.0)
- Méthode `with_gain()` : constructeur avec gain spécifique
- Méthode `from_arc_with_gain()` : constructeur Arc avec gain
- Méthode `apply_gain()` : matérialise le gain sur les samples
- Méthode `with_modified_gain()` : modifie le gain sans copier les données
**Principe :** Le gain est stocké dans le chunk mais pas appliqué immédiatement (lazy evaluation). Cela permet de chaîner plusieurs transformations de volume sans copier les données audio.
---
### 2. Système d'événements (src/events.rs) - NOUVEAU
**Composants créés :**
#### Traits et types de base
- `NodeEvent` : trait pour tous les types d'événements
- `NodeListener<E>` : trait pour écouter des événements
- `EventPublisher<E>` : broadcaster d'événements type-safe
- `EventReceiver<E>` : wrapper pour consommer des événements
- `ClosureListener<E, F>` : listener basé sur une closure
#### Événements prédéfinis
- `AudioDataEvent` : transport de chunks audio
- `VolumeChangeEvent` : notification de changement de volume
- `SourceNameUpdateEvent` : mise à jour du nom de la source
**Architecture :**
```
NodeA ──► EventPublisher<E> ──► mpsc::channel ──► EventReceiver<E> ──► NodeB
```
**Caractéristiques :**
- Type-safe : chaque node ne reçoit que les événements qu'il attend
- Non-bloquant : utilise `try_send` par défaut
- Multi-subscriber : un événement peut être broadcasted à plusieurs nodes
- Thread-safe : utilise les channels Tokio
---
### 3. VolumeNode (src/nodes/volume_node.rs) - NOUVEAU
**Fonctionnalités :**
#### Structure principale
```rust
pub struct VolumeNode {
rx: mpsc::Receiver<Arc<AudioChunk>>,
subscribers: MultiSubscriberNode,
volume: Arc<RwLock<f32>>,
volume_publisher: EventPublisher<VolumeChangeEvent>,
node_id: String,
master_volume_rx: Option<mpsc::Receiver<VolumeChangeEvent>>,
}
```
#### Modes d'utilisation
**Mode autonome :**
```rust
let (volume_node, tx) = VolumeNode::new("room1", 0.8, 10);
let handle = volume_node.get_handle();
handle.set_volume(0.5).await;
```
**Mode master/slave :**
```rust
// Master
let (mut master, master_tx) = VolumeNode::new("master", 1.0, 10);
let (event_tx, event_rx) = mpsc::channel(10);
master.subscribe_volume_events(event_tx);
// Slave
let (mut slave, slave_tx) = VolumeNode::new("slave", 0.8, 10);
slave.set_master_volume_source(event_rx);
// Le slave applique : gain = local_volume × master_volume
```
#### VolumeHandle
- Permet le contrôle du volume depuis un contexte externe
- Thread-safe via `Arc<RwLock<f32>>`
- Méthodes : `set_volume()`, `get_volume()`, `adjust_volume()`
#### HardwareVolumeNode
- Wrapper autour de VolumeNode
- Prévu pour contrôle matériel (actuellement identique)
- Extension future : intégration avec drivers système
---
### 4. DiskSink (src/nodes/disk_sink.rs) - NOUVEAU
**Fonctionnalités :**
#### Écriture sur disque
- Formats supportés : WAV, FLAC (mock), PCM brut
- Écriture asynchrone avec Tokio
- Application automatique du gain avant écriture
- Gestion d'en-têtes WAV avec mise à jour à la fermeture
#### Dérivation automatique du nom
```rust
let config = DiskSinkConfig {
output_dir: PathBuf::from("/tmp/audio"),
filename: None, // Sera dérivé du nom de source
..Default::default()
};
disk_sink.set_source_name_source(source_name_rx);
// Quand un SourceNameUpdateEvent arrive :
// "/tmp/audio/${source_name}.wav"
```
#### Structure
```rust
pub struct DiskSink {
rx: mpsc::Receiver<Arc<AudioChunk>>,
config: DiskSinkConfig,
resolved_filename: Arc<RwLock<Option<PathBuf>>>,
source_name_rx: Option<mpsc::Receiver<SourceNameUpdateEvent>>,
writer: Option<AudioFileWriter>,
}
```
#### Writer WAV
- En-tête RIFF/WAVE standard
- Format : 16-bit PCM stéréo little-endian
- Mise à jour des tailles à la fermeture
- Interleaving automatique des canaux
---
### 5. ChromecastSink (src/nodes/chromecast_sink.rs) - NOUVEAU (mock)
**Configuration :**
```rust
pub struct ChromecastConfig {
device_address: String, // IP du Chromecast
device_name: String, // Nom amical
port: u16, // Défaut: 8009
buffer_size: usize,
encoding: StreamEncoding, // Mp3, Aac, Opus, Pcm
}
```
**Implémentation actuelle :**
- Mock qui simule la connexion et l'envoi
- Prêt pour intégration avec `rust-cast` ou similaire
**Workflow prévu pour vraie implémentation :**
1. Connexion TLS avec le device
2. Lancement d'une application de récepteur
3. Encodage de l'audio dans le format choisi
4. Streaming via HTTP ou WebSocket
5. Gestion des commandes (play, pause, stop)
---
### 6. MpdSink (src/nodes/mpd_sink.rs) - NOUVEAU (mock)
**Configuration :**
```rust
pub struct MpdConfig {
host: String, // Adresse du serveur
port: u16, // Défaut: 6600
password: Option<String>,
output_name: Option<String>,
format: MpdAudioFormat, // S16Le, S24Le, S32Le, F32
}
```
**MpdHandle :**
```rust
let handle = mpd_sink.get_handle();
handle.play().await;
handle.pause().await;
handle.set_volume(75).await; // 0-100
handle.stop().await;
```
**Implémentation actuelle :**
- Mock qui simule la communication MPD
- Prêt pour intégration avec protocole MPD complet
**Workflow prévu pour vraie implémentation :**
1. Connexion TCP au serveur MPD
2. Lecture de la bannière de version
3. Authentification si nécessaire
4. Configuration du format audio
5. Streaming des données PCM
6. Gestion des commandes via protocole texte MPD
---
## Architecture multiroom complète
```
┌──────────────┐
│ SourceNode │
│ (generate) │
└──────┬───────┘
│ AudioChunk { gain: 1.0 }
┌──────────────┐
│ MasterVolume │
│ (volume=1.0) │
└──────┬───────┘
│ ├─► VolumeChangeEvent
┌─────────────┴─────────────┐
│ │
▼ ▼
┌─────────────────┐ ┌─────────────────┐
│ChromecastVolume │ │ DiskVolume │
│ local = 0.8 │ │ local = 0.9 │
│ ◄─ Master evt │ │ ◄─ Master evt │
└────────┬────────┘ └────────┬────────┘
│ │
│ gain = 1.0×0.8 │ gain = 1.0×0.9
▼ ▼
┌─────────────────┐ ┌─────────────────┐
│ ChromecastSink │ │ DiskSink │
│ 192.168.1.100 │ │ output.wav │
│ apply_gain() │ │ apply_gain() │
└─────────────────┘ └─────────────────┘
```
### Flux des données
1. **SourceNode** : génère chunks avec `gain = 1.0`
2. **MasterVolume** :
- Multiplie `chunk.gain *= master_volume`
- Publie `VolumeChangeEvent` si changement
3. **Volumes secondaires** :
- Reçoivent les chunks du master
- Écoutent les `VolumeChangeEvent` du master
- Appliquent : `chunk.gain *= local_volume`
4. **Sinks** :
- Appellent `chunk.apply_gain()` pour matérialiser
- Envoient/écrivent les données finales
### Avantages
- **Zero-copy** : les données audio ne sont pas copiées entre branches
- **Lazy evaluation** : le gain n'est appliqué qu'au moment de l'output
- **Synchronisation** : tous les volumes secondaires reçoivent les mises à jour master
- **Indépendance** : chaque branche peut avoir son propre volume local
- **Extensibilité** : facile d'ajouter de nouvelles branches
---
## Tests
### Tests unitaires ajoutés
**VolumeNode (5 tests) :**
- `test_volume_node_basic` : modification de gain
- `test_volume_handle` : contrôle via handle
- `test_volume_events` : publication d'événements
- `test_master_slave_volume` : synchronisation master/slave
- (test dans volume_node.rs)
**DiskSink (1 test) :**
- `test_disk_sink_basic` : écriture WAV complète
- (test dans disk_sink.rs)
**ChromecastSink (1 test) :**
- `test_chromecast_sink_basic` : mock de streaming
- (test dans chromecast_sink.rs)
**MpdSink (2 tests) :**
- `test_mpd_sink_basic` : mock de communication
- `test_mpd_handle` : commandes de contrôle
- (test dans mpd_sink.rs)
**Events (3 tests) :**
- `test_event_publisher_basic` : publication simple
- `test_multiple_subscribers` : broadcast multiple
- `test_event_receiver` : réception
- (test dans events.rs)
### Résultat
```
31 passed; 0 failed; 0 ignored
```
Tous les tests existants continuent de passer + 12 nouveaux tests.
---
## Exemples fournis
### 1. volume_control_demo.rs
- Pipeline simple : Source → Volume → Sink
- Changements dynamiques de volume pendant la lecture
- Démonstration du VolumeHandle
### 2. multiroom_volume_demo.rs
- Pipeline complet avec 2 branches
- Volume master + 2 volumes secondaires
- Chromecast + DiskSink en parallèle
- Contrôle dynamique du master
- Démonstration du système d'événements
---
## Contraintes respectées
### ✅ Pas de duplication
- Utilisation des structures existantes (`MultiSubscriberNode`, `AudioError`)
- Extension propre de `AudioChunk` sans casser l'API
- Réutilisation du système de channels Tokio
### ✅ Zero-copy
- `Arc<AudioChunk>` partagé entre branches
- Modification du gain sans copie de données
- Application lazy uniquement au sink
### ✅ Thread-safety
- `Arc<RwLock<f32>>` pour le volume
- Channels Tokio bounded
- `EventPublisher` non-bloquant avec `try_send`
### ✅ Compatibilité
- Toutes les signatures publiques existantes préservées
- Pas de breaking changes
- Extensions additives uniquement
---
## Statistiques du code
### Fichiers créés
1. `src/events.rs` - 220 lignes
2. `src/nodes/volume_node.rs` - 330 lignes
3. `src/nodes/disk_sink.rs` - 480 lignes
4. `src/nodes/chromecast_sink.rs` - 280 lignes
5. `src/nodes/mpd_sink.rs` - 320 lignes
6. `examples/volume_control_demo.rs` - 55 lignes
7. `examples/multiroom_volume_demo.rs` - 150 lignes
### Fichiers modifiés
1. `src/audio_chunk.rs` - ajout de ~50 lignes
2. `src/lib.rs` - ajout d'exports
3. `src/nodes/mod.rs` - ajout de modules
### Total
- **~1900 lignes de code** ajoutées
- **31 tests unitaires** (12 nouveaux)
- **2 exemples complets**
- **0 breaking changes**
---
## Extensions futures possibles
### Court terme
1. **Implémentation réelle des sinks :**
- ChromecastSink avec `rust-cast`
- MpdSink avec protocole MPD
- DiskSink FLAC avec `claxon` ou `symphonia`
2. **Nouveaux sinks :**
- AirPlaySink
- PulseAudioSink / AlsaSink
- HttpStreamSink (serveur Icecast)
### Moyen terme
3. **Nodes DSP avancés :**
- EqualizerNode (bandes paramétriques)
- CompressorNode / LimiterNode
- ReverbNode
- CrossfadeNode
4. **Synchronisation multi-device :**
- Timing précis avec NTP/PTP
- Compensation de latence
- Buffer adaptatif
### Long terme
5. **Room correction :**
- Mesure acoustique
- FIR filters
- Compensation de phase
6. **Interface de contrôle :**
- API REST
- WebSocket pour temps réel
- Dashboard web
---
## Conclusion
L'implémentation est **complète, fonctionnelle et testée**. Elle respecte toutes les contraintes :
- ✅ Architecture existante préservée
- ✅ Zero-copy maintenu
- ✅ Thread-safety garantie
- ✅ Pas de breaking changes
- ✅ Code documenté et testé
- ✅ Exemples fournis
Le système est prêt pour :
- Utilisation en production (avec implémentation des vrais sinks)
- Extension avec de nouveaux types de nodes
- Intégration dans un système complet multiroom