Complete la crate pmoaudio
This commit is contained in:
163
pmoaudio/examples/multiroom_volume_demo.rs
Normal file
163
pmoaudio/examples/multiroom_volume_demo.rs
Normal file
@@ -0,0 +1,163 @@
|
||||
//! Exemple complet de pipeline multiroom avec contrôle de volume
|
||||
//!
|
||||
//! Ce programme démontre :
|
||||
//! - Une source audio unique
|
||||
//! - Deux branches de sortie : Chromecast et DiskSink
|
||||
//! - Un volume master avec deux VolumeNodes secondaires synchronisés
|
||||
//! - Système d'événements pour la communication entre nodes
|
||||
|
||||
use pmoaudio::{
|
||||
ChromecastConfig, ChromecastSink, DiskSink, DiskSinkConfig, SourceNode,
|
||||
VolumeNode,
|
||||
};
|
||||
use tokio::sync::mpsc;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
println!("=== PMOAudio Multiroom Volume Demo ===\n");
|
||||
|
||||
// Configuration
|
||||
let sample_rate = 48000u32;
|
||||
let chunk_size = 4800usize; // 100ms à 48kHz
|
||||
let num_chunks = 50; // 5 secondes de lecture
|
||||
let frequency = 440.0; // La 440 Hz
|
||||
|
||||
// ===== 1. Créer la source audio =====
|
||||
println!("1. Creating audio source...");
|
||||
let mut source = SourceNode::new();
|
||||
|
||||
// ===== 2. Créer le volume master =====
|
||||
println!("2. Creating master volume node...");
|
||||
let (mut master_volume, master_tx) = VolumeNode::new("master".to_string(), 1.0, 50);
|
||||
let master_handle = master_volume.get_handle();
|
||||
|
||||
// Channel pour les événements du volume master
|
||||
let (master_event_tx, master_event_rx_chromecast) = mpsc::channel(10);
|
||||
let (_, master_event_rx_disk) = mpsc::channel(10);
|
||||
|
||||
master_volume.subscribe_volume_events(master_event_tx);
|
||||
|
||||
source.add_subscriber(master_tx);
|
||||
|
||||
// ===== 3. Créer les branches de sortie =====
|
||||
|
||||
// Branche 1: Chromecast avec volume secondaire
|
||||
println!("3a. Creating Chromecast output branch...");
|
||||
let (mut chromecast_volume, chromecast_volume_tx) =
|
||||
VolumeNode::new("chromecast_volume".to_string(), 0.8, 50);
|
||||
|
||||
chromecast_volume.set_master_volume_source(master_event_rx_chromecast);
|
||||
|
||||
let chromecast_config = ChromecastConfig {
|
||||
device_address: "192.168.1.100".to_string(),
|
||||
device_name: "Living Room".to_string(),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let (chromecast_sink, chromecast_sink_tx) =
|
||||
ChromecastSink::new("chromecast1".to_string(), chromecast_config, 50);
|
||||
|
||||
chromecast_volume.add_subscriber(chromecast_sink_tx);
|
||||
master_volume.add_subscriber(chromecast_volume_tx);
|
||||
|
||||
// Branche 2: DiskSink avec volume secondaire
|
||||
println!("3b. Creating DiskSink output branch...");
|
||||
let (mut disk_volume, disk_volume_tx) = VolumeNode::new("disk_volume".to_string(), 0.9, 50);
|
||||
|
||||
disk_volume.set_master_volume_source(master_event_rx_disk);
|
||||
|
||||
let disk_config = DiskSinkConfig {
|
||||
output_dir: std::env::temp_dir().join("pmoaudio_demo"),
|
||||
filename: Some("multiroom_output.wav".to_string()),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let (disk_sink, disk_sink_tx) = DiskSink::new("disk1".to_string(), disk_config, 50);
|
||||
|
||||
disk_volume.add_subscriber(disk_sink_tx);
|
||||
master_volume.add_subscriber(disk_volume_tx);
|
||||
|
||||
// ===== 4. Lancer tous les nodes =====
|
||||
println!("4. Starting pipeline nodes...\n");
|
||||
|
||||
// Spawn master volume
|
||||
let master_volume_handle = tokio::spawn(async move {
|
||||
master_volume.run().await.unwrap();
|
||||
});
|
||||
|
||||
// Spawn chromecast branch
|
||||
let chromecast_volume_handle = tokio::spawn(async move {
|
||||
chromecast_volume.run().await.unwrap();
|
||||
});
|
||||
|
||||
let chromecast_sink_handle = tokio::spawn(async move {
|
||||
let stats = chromecast_sink.run().await.unwrap();
|
||||
stats.display();
|
||||
});
|
||||
|
||||
// Spawn disk branch
|
||||
let disk_volume_handle = tokio::spawn(async move {
|
||||
disk_volume.run().await.unwrap();
|
||||
});
|
||||
|
||||
let disk_sink_handle = tokio::spawn(async move {
|
||||
let stats = disk_sink.run().await.unwrap();
|
||||
stats.display();
|
||||
});
|
||||
|
||||
// ===== 5. Contrôler le volume pendant la lecture =====
|
||||
let master_handle_clone = master_handle.clone();
|
||||
tokio::spawn(async move {
|
||||
// Attendre un peu, puis diminuer le volume
|
||||
tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;
|
||||
println!("\n>>> Decreasing master volume to 0.7");
|
||||
master_handle_clone.set_volume(0.7).await;
|
||||
|
||||
tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;
|
||||
println!(">>> Decreasing master volume to 0.4");
|
||||
master_handle_clone.set_volume(0.4).await;
|
||||
|
||||
tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;
|
||||
println!(">>> Increasing master volume back to 1.0");
|
||||
master_handle_clone.set_volume(1.0).await;
|
||||
});
|
||||
|
||||
// ===== 6. Générer et envoyer les chunks audio =====
|
||||
println!("5. Generating and streaming audio...");
|
||||
tokio::spawn(async move {
|
||||
source
|
||||
.generate_chunks(num_chunks, chunk_size, sample_rate, frequency)
|
||||
.await
|
||||
.unwrap();
|
||||
println!("\n>>> Audio generation complete!");
|
||||
});
|
||||
|
||||
// ===== 7. Attendre la fin de tous les nodes =====
|
||||
println!("6. Waiting for all nodes to complete...\n");
|
||||
|
||||
// Attendre que les sinks terminent
|
||||
chromecast_sink_handle.await?;
|
||||
disk_sink_handle.await?;
|
||||
|
||||
// Nettoyer
|
||||
master_volume_handle.abort();
|
||||
chromecast_volume_handle.abort();
|
||||
disk_volume_handle.abort();
|
||||
|
||||
println!("\n=== Demo completed successfully! ===");
|
||||
println!("\nSummary:");
|
||||
println!("- Generated {} chunks of {} samples each", num_chunks, chunk_size);
|
||||
println!("- Total duration: {:.2} seconds", (num_chunks as usize * chunk_size) as f32 / sample_rate as f32);
|
||||
println!("- Output to Chromecast: Living Room (192.168.1.100)");
|
||||
println!(
|
||||
"- Output to file: {}",
|
||||
std::env::temp_dir()
|
||||
.join("pmoaudio_demo")
|
||||
.join("multiroom_output.wav")
|
||||
.display()
|
||||
);
|
||||
println!("- Master volume control demonstrated with live changes");
|
||||
println!("\nAll streams received synchronized volume updates!");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
96
pmoaudio/examples/quick_start.rs
Normal file
96
pmoaudio/examples/quick_start.rs
Normal file
@@ -0,0 +1,96 @@
|
||||
//! Quick Start - Démonstration rapide des nouvelles fonctionnalités
|
||||
//!
|
||||
//! Cet exemple montre l'utilisation des principales nouvelles fonctionnalités :
|
||||
//! - VolumeNode avec contrôle dynamique
|
||||
//! - DiskSink pour écriture sur disque
|
||||
//! - Pipeline simple et efficace
|
||||
|
||||
use pmoaudio::{AudioFileFormat, DiskSink, DiskSinkConfig, SourceNode, VolumeNode};
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
println!("=== PMOAudio Quick Start ===\n");
|
||||
|
||||
// 1. Créer la source audio (génère un signal de test)
|
||||
let mut source = SourceNode::new();
|
||||
|
||||
// 2. Créer un VolumeNode pour contrôler le volume
|
||||
let (mut volume, volume_tx) = VolumeNode::new("main".to_string(), 0.8, 10);
|
||||
let volume_handle = volume.get_handle();
|
||||
|
||||
// 3. Créer un DiskSink pour écrire sur disque
|
||||
let output_dir = std::env::temp_dir().join("pmoaudio_quickstart");
|
||||
let config = DiskSinkConfig {
|
||||
output_dir: output_dir.clone(),
|
||||
filename: Some("quickstart_output.wav".to_string()),
|
||||
format: AudioFileFormat::Wav,
|
||||
buffer_size: 50,
|
||||
};
|
||||
|
||||
let (disk_sink, disk_tx) = DiskSink::new("disk".to_string(), config, 10);
|
||||
|
||||
// 4. Connecter le pipeline : Source → Volume → DiskSink
|
||||
source.add_subscriber(volume_tx);
|
||||
volume.add_subscriber(disk_tx);
|
||||
|
||||
println!("Pipeline configured:");
|
||||
println!(" SourceNode → VolumeNode (vol=0.8) → DiskSink");
|
||||
println!(" Output: {}/quickstart_output.wav\n", output_dir.display());
|
||||
|
||||
// 5. Lancer les nodes
|
||||
let volume_handle_clone = volume_handle.clone();
|
||||
tokio::spawn(async move {
|
||||
volume.run().await.unwrap();
|
||||
});
|
||||
|
||||
let disk_handle = tokio::spawn(async move {
|
||||
let stats = disk_sink.run().await.unwrap();
|
||||
println!("\nDiskSink Statistics:");
|
||||
stats.display();
|
||||
stats
|
||||
});
|
||||
|
||||
// 6. Démonstration du contrôle de volume pendant la lecture
|
||||
tokio::spawn(async move {
|
||||
println!("Generating audio with volume changes...");
|
||||
|
||||
tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
|
||||
println!(" → Volume: 0.8 (initial)");
|
||||
|
||||
tokio::time::sleep(tokio::time::Duration::from_millis(200)).await;
|
||||
volume_handle_clone.set_volume(0.5).await;
|
||||
println!(" → Volume: 0.5 (decreased)");
|
||||
|
||||
tokio::time::sleep(tokio::time::Duration::from_millis(200)).await;
|
||||
volume_handle_clone.set_volume(1.0).await;
|
||||
println!(" → Volume: 1.0 (maximum)");
|
||||
|
||||
tokio::time::sleep(tokio::time::Duration::from_millis(200)).await;
|
||||
volume_handle_clone.set_volume(0.3).await;
|
||||
println!(" → Volume: 0.3 (low)");
|
||||
});
|
||||
|
||||
// 7. Générer l'audio (10 chunks de 4800 samples à 48kHz = ~1 seconde)
|
||||
source
|
||||
.generate_chunks(
|
||||
10, // nombre de chunks
|
||||
4800, // samples par chunk (100ms @ 48kHz)
|
||||
48000, // sample rate
|
||||
440.0, // fréquence (La 440 Hz)
|
||||
)
|
||||
.await?;
|
||||
|
||||
// 8. Attendre la fin du traitement
|
||||
let stats = disk_handle.await?;
|
||||
|
||||
// 9. Résumé
|
||||
println!("\n=== Summary ===");
|
||||
println!("✓ Audio file generated successfully");
|
||||
println!("✓ {} chunks written", stats.chunks_written);
|
||||
println!("✓ Duration: {:.2} seconds", stats.total_duration_sec);
|
||||
println!("✓ Volume was dynamically adjusted during playback");
|
||||
println!("\nYou can play the file with:");
|
||||
println!(" ffplay {}/quickstart_output.wav", output_dir.display());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
58
pmoaudio/examples/volume_control_demo.rs
Normal file
58
pmoaudio/examples/volume_control_demo.rs
Normal file
@@ -0,0 +1,58 @@
|
||||
//! Exemple simple de contrôle de volume
|
||||
//!
|
||||
//! Démontre l'utilisation du VolumeNode avec changements dynamiques
|
||||
|
||||
use pmoaudio::{SinkNode, SourceNode, VolumeNode};
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
println!("=== Volume Control Demo ===\n");
|
||||
|
||||
// Créer la source
|
||||
let mut source = SourceNode::new();
|
||||
|
||||
// Créer le volume node
|
||||
let (mut volume, volume_tx) = VolumeNode::new("main".to_string(), 1.0, 10);
|
||||
let volume_handle = volume.get_handle();
|
||||
|
||||
// Créer le sink
|
||||
let (sink, sink_tx) = SinkNode::new("Output".to_string(), 10);
|
||||
|
||||
// Connecter le pipeline
|
||||
source.add_subscriber(volume_tx);
|
||||
volume.add_subscriber(sink_tx);
|
||||
|
||||
// Lancer les nodes
|
||||
tokio::spawn(async move { volume.run().await.unwrap() });
|
||||
|
||||
let sink_handle = tokio::spawn(async move { sink.run_with_stats().await.unwrap() });
|
||||
|
||||
// Contrôler le volume pendant la lecture
|
||||
let volume_control = tokio::spawn(async move {
|
||||
tokio::time::sleep(tokio::time::Duration::from_millis(200)).await;
|
||||
println!("Setting volume to 0.5");
|
||||
volume_handle.set_volume(0.5).await;
|
||||
|
||||
tokio::time::sleep(tokio::time::Duration::from_millis(200)).await;
|
||||
println!("Setting volume to 0.2");
|
||||
volume_handle.set_volume(0.2).await;
|
||||
|
||||
tokio::time::sleep(tokio::time::Duration::from_millis(200)).await;
|
||||
println!("Setting volume to 1.0");
|
||||
volume_handle.set_volume(1.0).await;
|
||||
});
|
||||
|
||||
// Générer l'audio
|
||||
source
|
||||
.generate_chunks(20, 4800, 48000, 440.0)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
volume_control.await?;
|
||||
let stats = sink_handle.await?;
|
||||
|
||||
println!("\nFinal statistics:");
|
||||
stats.display();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Reference in New Issue
Block a user