76 lines
2.3 KiB
Rust
76 lines
2.3 KiB
Rust
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<T> takes a reference to a slice of sample-friendly
|
|
// primitives, calculates the max value and atomically updates the AudioState.
|
|
fn process_audio<T>(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);
|
|
}
|