use crate::error::{AppError, AppResult};
use cpal::traits::{DeviceTrait, HostTrait};
use std::sync::{Arc, Mutex};
pub struct AudioConfig {
pub device_name: String,
pub sample_rate: u32,
pub channels: u16,
}
pub fn setup_audio_device(device_name: Option<String>) -> AppResult<(cpal::Device, AudioConfig)> {
let host = cpal::default_host();
let device = if let Some(name) = device_name {
host.input_devices()?
.find(|d| d.name().map(|n| n == name).unwrap_or(false))
.ok_or_else(|| AppError::AudioDevice("Specified device not found".to_string()))?
} else {
host.default_input_device()
.ok_or_else(|| AppError::AudioDevice("No default input device available".to_string()))?
};
let device_name = device.name()?;
let audio_config = AudioConfig {
device_name,
sample_rate: crate::constants::audio::DEFAULT_SAMPLE_RATE,
channels: crate::constants::audio::DEFAULT_CHANNELS,
};
Ok((device, audio_config))
}
pub fn build_audio_stream<F>(
device: &cpal::Device,
config: &cpal::StreamConfig,
data_callback: F,
) -> AppResult<cpal::Stream>
where
F: FnMut(&[f32], &cpal::InputCallbackInfo) + Send + 'static,
{
let stream = device.build_input_stream(
config,
data_callback,
|err| eprintln!("Audio stream error: {}", err),
None,
)?;
Ok(stream)
}
pub fn create_audio_callback(
current_db: Arc<Mutex<f32>>,
smoothed_db: Arc<Mutex<f32>>,
display_db: Arc<Mutex<f32>>,
threshold_reached: Arc<Mutex<bool>>,
linear_threshold: f32,
) -> impl FnMut(&[f32], &cpal::InputCallbackInfo) + Send + 'static {
move |data: &[f32], _: &cpal::InputCallbackInfo| {
let max_sample = data.iter().map(|s| s.abs()).fold(0.0f32, f32::max);
let current_db_value = if max_sample > 0.0 {
20.0 * max_sample.log10()
} else {
-60.0
};
*current_db.lock().unwrap() = current_db_value;
let mut smoothed = smoothed_db.lock().unwrap();
let mut display = display_db.lock().unwrap();
let audio_smoothing = crate::constants::smoothing::AUDIO_SMOOTHING_FACTOR;
*smoothed = *smoothed * (1.0 - audio_smoothing) + current_db_value * audio_smoothing;
let display_smoothing = crate::constants::smoothing::DISPLAY_SMOOTHING_FACTOR;
*display = *display * (1.0 - display_smoothing) + *smoothed * display_smoothing;
let mut threshold_flag = threshold_reached.lock().unwrap();
if max_sample > linear_threshold {
*threshold_flag = true;
}
}
}