200 lines
6.9 KiB
Rust
200 lines
6.9 KiB
Rust
use std::sync::{Arc, Mutex};
|
|
use std::sync::atomic::{AtomicBool, Ordering};
|
|
use std::thread::JoinHandle;
|
|
use std::time::Instant;
|
|
use cpal::{default_host, BufferSize, Device, SampleRate, Stream, StreamConfig, SupportedStreamConfig};
|
|
use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
|
|
use crate::core::mixer::AudioMixer;
|
|
use crate::domain::event::{Event, EventBus};
|
|
use crate::utils::real_time_event::RealTimeEvent;
|
|
|
|
#[derive(Clone)]
|
|
pub struct Speaker {
|
|
device: Device
|
|
}
|
|
|
|
pub struct AudioPlayback {
|
|
event_bus: EventBus,
|
|
speaker: Speaker,
|
|
running: Arc<AtomicBool>,
|
|
stream: Option<Stream>,
|
|
worker: Option<JoinHandle<()>>,
|
|
mixer: AudioMixer
|
|
}
|
|
|
|
impl Speaker {
|
|
pub fn new(device: Device) -> Self {
|
|
Speaker {
|
|
device
|
|
}
|
|
}
|
|
|
|
pub fn default() -> Self {
|
|
let host = default_host();
|
|
let device = host.default_output_device().unwrap();
|
|
Speaker::new(device)
|
|
}
|
|
|
|
pub fn get_output_config(&self) -> SupportedStreamConfig {
|
|
self.device.default_output_config().unwrap()
|
|
}
|
|
|
|
pub fn get_stream_config(&self) -> StreamConfig {
|
|
// Lister toutes les configurations supportées
|
|
self.print_supported_configs();
|
|
|
|
self.get_output_config().into()
|
|
}
|
|
|
|
pub fn print_supported_configs(&self) {
|
|
println!("\n=== CONFIGURATIONS AUDIO DISPONIBLES ===");
|
|
|
|
// Configuration par défaut
|
|
match self.device.default_output_config() {
|
|
Ok(config) => {
|
|
println!("📌 Configuration par défaut:");
|
|
println!(" Canaux: {}", config.channels());
|
|
println!(" Sample Rate: {} Hz", config.sample_rate().0);
|
|
println!(" Format: {:?}", config.sample_format());
|
|
println!(" Buffer Size: {:?}", config.buffer_size());
|
|
},
|
|
Err(e) => println!("❌ Impossible d'obtenir la config par défaut: {}", e)
|
|
}
|
|
|
|
// Toutes les configurations supportées
|
|
println!("\n📋 Toutes les configurations supportées:");
|
|
match self.device.supported_output_configs() {
|
|
Ok(configs) => {
|
|
for (i, config_range) in configs.enumerate() {
|
|
println!("\n Config #{}", i + 1);
|
|
println!(" Canaux: {}", config_range.channels());
|
|
println!(" Sample Rate: {} - {} Hz",
|
|
config_range.min_sample_rate().0,
|
|
config_range.max_sample_rate().0);
|
|
println!(" Format: {:?}", config_range.sample_format());
|
|
println!(" Buffer Size: {:?}", config_range.buffer_size());
|
|
|
|
// Suggestions de sample rates courants
|
|
let common_rates = [8000, 11025, 16000, 22050, 44100, 48000, 88200, 96000];
|
|
let mut supported_common = Vec::new();
|
|
for rate in common_rates {
|
|
if rate >= config_range.min_sample_rate().0 && rate <= config_range.max_sample_rate().0 {
|
|
supported_common.push(rate);
|
|
}
|
|
}
|
|
if !supported_common.is_empty() {
|
|
println!(" Sample rates courants supportés: {:?}", supported_common);
|
|
}
|
|
}
|
|
},
|
|
Err(e) => println!("❌ Impossible de lister les configs: {}", e)
|
|
}
|
|
|
|
// Informations sur le device
|
|
println!("\n🎧 Informations du device:");
|
|
if let Ok(name) = self.device.name() {
|
|
println!(" Nom: {}", name);
|
|
}
|
|
|
|
// Test de configurations spécifiques
|
|
println!("\n🧪 Test de configurations spécifiques:");
|
|
let test_configs = [
|
|
(44100, 1, "Mono 44.1kHz"),
|
|
(44100, 2, "Stéréo 44.1kHz"),
|
|
(48000, 1, "Mono 48kHz"),
|
|
(48000, 2, "Stéréo 48kHz"),
|
|
(22050, 2, "Stéréo 22.05kHz"),
|
|
];
|
|
|
|
for (sample_rate, channels, description) in test_configs {
|
|
let test_config = StreamConfig {
|
|
channels,
|
|
sample_rate: SampleRate(sample_rate),
|
|
buffer_size: BufferSize::Default
|
|
};
|
|
|
|
// Test si cette config est supportée (tentative de création d'un stream fictif)
|
|
let dummy_callback = |_: &mut [f32], _: &cpal::OutputCallbackInfo| {};
|
|
match self.device.build_output_stream(&test_config, dummy_callback, |_| {}, None) {
|
|
Ok(_) => println!(" ✅ {} - SUPPORTÉ", description),
|
|
Err(_) => println!(" ❌ {} - NON SUPPORTÉ", description),
|
|
}
|
|
}
|
|
|
|
println!("\n===========================================\n");
|
|
}
|
|
|
|
|
|
pub fn build_stream<F>(&self, callback: F) -> Stream
|
|
where
|
|
F: FnMut(&mut [f32], &cpal::OutputCallbackInfo) + Send + 'static,
|
|
{
|
|
let config = self.get_stream_config();
|
|
|
|
self.device.build_output_stream(
|
|
&config,
|
|
callback,
|
|
|err| println!("Error output stream: {err}"),
|
|
None
|
|
).unwrap()
|
|
}
|
|
}
|
|
|
|
impl AudioPlayback {
|
|
pub fn new(event_bus: EventBus, speaker: Speaker, mixer: AudioMixer) -> Self {
|
|
Self {
|
|
event_bus,
|
|
speaker,
|
|
running: Arc::new(AtomicBool::new(false)),
|
|
stream: None,
|
|
worker: None,
|
|
mixer
|
|
}
|
|
}
|
|
|
|
pub fn default(event_bus: EventBus, mixer: AudioMixer) -> Self {
|
|
let speaker = Speaker::default();
|
|
AudioPlayback::new(event_bus, speaker, mixer)
|
|
}
|
|
|
|
pub async fn start(&mut self) {
|
|
self.running.store(true, Ordering::SeqCst);
|
|
let stream_running = self.running.clone();
|
|
let event_bus = self.event_bus.clone();
|
|
let mixer = self.mixer.clone();
|
|
// stream cpal
|
|
println!("Setting up audio playback stream...");
|
|
let last_time = Mutex::new(Instant::now());
|
|
let stream = self.speaker.build_stream(move |data, info| {
|
|
println!(
|
|
"CALLBACK : reçu {} samples, type info = {:?}",
|
|
data.len(),
|
|
info
|
|
);
|
|
|
|
let now = Instant::now();
|
|
let mut last = last_time.lock().unwrap();
|
|
let dt = now.duration_since(*last);
|
|
println!("Callback audio appelée chaque {:?} ms (≈ {:.1} Hz)", dt.as_millis(), 1000.0 / dt.as_millis().max(1) as f32);
|
|
*last = now;
|
|
|
|
if !stream_running.load(Ordering::Relaxed){
|
|
return;
|
|
}
|
|
|
|
let audio_mixer = mixer.read(data.len());
|
|
data.copy_from_slice(&audio_mixer);
|
|
// println!("data content : {:?}", data);
|
|
|
|
let _ = event_bus.emit_sync(Event::PlaybackTick(data.len()));
|
|
});
|
|
stream.play().unwrap();
|
|
self.stream = Some(stream);
|
|
println!("Audio playback stream started");
|
|
}
|
|
|
|
pub async fn stop(&mut self) {
|
|
self.running.store(false, Ordering::SeqCst);
|
|
|
|
}
|
|
} |