moa/emulator/frontends/common/src/cpal.rs

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();
}
}
}