extern crate cpal; use cpal::{EventLoop, Sample, StreamData, UnknownTypeInputBuffer}; use std::{ i16, sync::atomic::{AtomicBool, AtomicI16, Ordering}, }; pub struct AudioState { pub max_value: AtomicI16, pub gui_up_to_date: AtomicBool, } impl AudioState { pub fn new() -> Self { AudioState { max_value: AtomicI16::new(0), gui_up_to_date: AtomicBool::new(false), } } } // Open the default audio device, and start the audio routine: pub fn start(state: &AudioState) -> Result<(), String> { let event_loop = EventLoop::new(); let device = cpal::default_input_device().ok_or("Could not find a default input device")?; let mut supported_formats_range = device .supported_input_formats() .map_err(|_| "Could not get supported input formats")?; let format = supported_formats_range .next() .ok_or("Could not get supported formats range")? .with_max_sample_rate(); let stream_id = event_loop .build_input_stream(&device, &format) .map_err(|_| "Could not build input stream")?; event_loop.play_stream(stream_id); // Define the actual audio callback. // All we do is extract the enum variant, deference the buffer // and then lend it to process_audio: event_loop.run(|_stream_id, stream_data| match stream_data { StreamData::Input { buffer: UnknownTypeInputBuffer::I16(buffer), } => process_audio(&*buffer, state), StreamData::Input { buffer: UnknownTypeInputBuffer::U16(buffer), } => process_audio(&*buffer, state), StreamData::Input { buffer: UnknownTypeInputBuffer::F32(buffer), } => process_audio(&*buffer, state), _ => unreachable!(), }); } // process_audio takes a reference to a slice of sample-friendly // primitives, calculates the max value and atomically updates the AudioState. fn process_audio(buffer: &[T], state: &AudioState) where T: cpal::Sample, { let max = buffer .iter() .map(Sample::to_i16) .max() .map(|s| s.max(i16::min_value() + 1)) .unwrap_or(0) .abs(); state.max_value.store(max, Ordering::SeqCst); state.gui_up_to_date.store(false, Ordering::SeqCst); }