73 lines
2.3 KiB
Rust
73 lines
2.3 KiB
Rust
use cpal::{
|
|
Stream, SampleRate, SampleFormat, StreamConfig, OutputCallbackInfo,
|
|
traits::{DeviceTrait, HostTrait, StreamTrait},
|
|
};
|
|
|
|
use crate::audio::{AudioOutput, SAMPLE_RATE};
|
|
|
|
#[allow(dead_code)]
|
|
pub struct CpalAudioOutput {
|
|
stream: Stream,
|
|
}
|
|
|
|
impl CpalAudioOutput {
|
|
pub fn create_audio_output(output: AudioOutput) -> CpalAudioOutput {
|
|
let device = cpal::default_host()
|
|
.default_output_device()
|
|
.expect("No sound output device available");
|
|
|
|
let config: StreamConfig = device
|
|
.supported_output_configs()
|
|
.expect("error while querying configs")
|
|
.find(|config| config.sample_format() == SampleFormat::F32 && config.channels() == 2)
|
|
.expect("no supported config?!")
|
|
.with_sample_rate(SampleRate(SAMPLE_RATE as u32))
|
|
.into();
|
|
|
|
let data_callback = move |data: &mut [f32], _info: &OutputCallbackInfo| {
|
|
let mut index = 0;
|
|
while index < data.len() {
|
|
if let Some((clock, mut frame)) = output.receive() {
|
|
let size = (frame.data.len() * 2).min(data.len() - index);
|
|
frame
|
|
.data
|
|
.iter()
|
|
.zip(data[index..index + size].chunks_mut(2))
|
|
.for_each(|(sample, location)| {
|
|
location[0] = sample.0;
|
|
location[1] = sample.1;
|
|
});
|
|
index += size;
|
|
if size < frame.data.len() * 2 {
|
|
frame.data.drain(0..size / 2);
|
|
output.put_back(clock, frame);
|
|
}
|
|
} else {
|
|
log::debug!("missed an audio frame");
|
|
break;
|
|
}
|
|
}
|
|
};
|
|
|
|
let stream = device
|
|
.build_output_stream(&config, data_callback, move |err| {
|
|
log::error!("ERROR: {:?}", err);
|
|
})
|
|
.unwrap();
|
|
|
|
stream.play().unwrap();
|
|
|
|
CpalAudioOutput {
|
|
stream,
|
|
}
|
|
}
|
|
|
|
pub fn set_mute(&self, mute: bool) {
|
|
if mute {
|
|
self.stream.pause().unwrap();
|
|
} else {
|
|
self.stream.play().unwrap();
|
|
}
|
|
}
|
|
}
|