use anyhow::Result;
use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
use ringbuf::{HeapProducer, HeapRb};
use rubato::{FastFixedOut, PolynomialDegree, Resampler};
use std::error::Error;
mod bridge;
pub struct AudioConfiguration {
pub sample_rate: u32,
pub channels: u16,
}
pub struct AudioPlayer {
buffer_producer: HeapProducer<f32>,
resampler: Option<FastFixedOut<f32>>,
pre_resampled_buffer: Vec<f32>,
pre_resampled_split_buffers: [Vec<f32>; 2],
resample_process_buffers: [Vec<f32>; 2],
resampled_buffer: Vec<f32>,
output_stream: cpal::Stream,
}
#[derive(Debug)]
pub enum AudioPlayerError {
NoDefaultOutputDevice,
DualChannelNotSupported,
}
impl Error for AudioPlayerError {}
impl std::fmt::Display for AudioPlayerError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::NoDefaultOutputDevice => write!(f, "No default output device"),
Self::DualChannelNotSupported => write!(f, "Dual channel not supported"),
}
}
}
impl AudioPlayer {
pub fn new(config: AudioConfiguration) -> Result<Self> {
let host = cpal::default_host();
let output_device = host.default_output_device();
let output_device = match output_device {
Some(device) => device,
None => return Err(AudioPlayerError::NoDefaultOutputDevice.into()),
};
let sample_rate = cpal::SampleRate(config.sample_rate);
let conf = output_device
.supported_output_configs()?
.collect::<Vec<_>>();
let mut found_conf = false;
for c in &conf {
if c.channels() == 2
&& c.sample_format() == cpal::SampleFormat::F32
&& c.min_sample_rate() <= sample_rate
&& c.max_sample_rate() >= sample_rate
{
found_conf = true;
break;
}
}
let (output_sample_rate, resampler) = if found_conf {
(sample_rate, None)
} else {
let def_conf = output_device.default_output_config().unwrap();
if def_conf.channels() != 2 || def_conf.sample_format() != cpal::SampleFormat::F32 {
eprintln!("[Audio] No supported configuration found for audio device, please open an issue in github `dcvz/helix`\n\
list of supported configurations: {conf:#?}");
return Err(AudioPlayerError::DualChannelNotSupported.into());
}
let f_ratio = def_conf.sample_rate().0 as f64 / sample_rate.0 as f64;
(
def_conf.sample_rate(),
Some(FastFixedOut::<f32>::new(
f_ratio,
1.1,
PolynomialDegree::Nearest, def_conf.sample_rate().0 as usize / 30,
2,
)?),
)
};
let config = cpal::StreamConfig {
channels: config.channels,
sample_rate: output_sample_rate,
buffer_size: cpal::BufferSize::Default,
};
let buffer = HeapRb::new(output_sample_rate.0 as usize / 2);
let (buffer_producer, mut buffer_consumer) = buffer.split();
let output_data_fn = move |data: &mut [f32], _: &cpal::OutputCallbackInfo| {
for sample in data {
*sample = buffer_consumer.pop().unwrap_or(0.);
}
};
let output_stream =
output_device.build_output_stream(&config, output_data_fn, Self::err_fn, None)?;
Ok(Self {
buffer_producer,
output_stream,
pre_resampled_buffer: Vec::new(),
pre_resampled_split_buffers: [Vec::new(), Vec::new()],
resample_process_buffers: [Vec::new(), Vec::new()],
resampled_buffer: Vec::new(),
resampler,
})
}
pub fn bufferred_sample_count(&self) -> usize {
self.buffer_producer.len()
}
pub fn buffer_capacity(&self) -> usize {
self.buffer_producer.capacity()
}
pub fn play(&self) {
self.output_stream.play().unwrap();
}
pub fn pause(&self) {
self.output_stream.pause().unwrap();
}
pub fn queue_buffer_u8(&mut self, buffer: &[u8]) {
let samples = buffer
.chunks(2)
.map(|b| i16::from_le_bytes([b[0], b[1]]) as f32 / 32768.0)
.collect::<Vec<_>>();
self.queue_buffer(&samples);
}
pub fn queue_buffer(&mut self, buffer: &[f32]) {
fn read_frames(inbuffer: &[f32], n_frames: usize, outputs: &mut [Vec<f32>]) {
for output in outputs.iter_mut() {
output.clear();
output.reserve(n_frames);
}
let mut value: f32;
let mut inbuffer_iter = inbuffer.iter();
for _ in 0..n_frames {
for output in outputs.iter_mut() {
value = *inbuffer_iter.next().unwrap();
output.push(value);
}
}
}
fn write_frames(waves: &[Vec<f32>], outbuffer: &mut Vec<f32>) {
let nbr = waves[0].len();
for frame in 0..nbr {
for wave in waves.iter() {
outbuffer.push(wave[frame]);
}
}
}
if let Some(resampler) = &mut self.resampler {
self.pre_resampled_buffer.extend_from_slice(&buffer);
loop {
let frames = resampler.input_frames_next();
if self.pre_resampled_buffer.len() < frames * 2 {
return;
}
read_frames(
&self.pre_resampled_buffer,
frames,
&mut self.pre_resampled_split_buffers,
);
self.resample_process_buffers[0].clear();
self.resample_process_buffers[0].clear();
let output_frames = resampler.output_frames_next();
self.resample_process_buffers[0].resize(output_frames, 0.);
self.resample_process_buffers[1].resize(output_frames, 0.);
resampler
.process_into_buffer(
&self.pre_resampled_split_buffers,
&mut self.resample_process_buffers,
None,
)
.unwrap();
if self.resampled_buffer.len() < output_frames * 2 {
self.resampled_buffer
.reserve(output_frames * 2 - self.resampled_buffer.len());
}
self.resampled_buffer.clear();
write_frames(&self.resample_process_buffers, &mut self.resampled_buffer);
self.buffer_producer.push_slice(&self.resampled_buffer);
self.pre_resampled_buffer = self.pre_resampled_buffer.split_off(frames * 2);
}
} else {
self.buffer_producer.push_slice(&buffer);
}
}
fn err_fn(err: cpal::StreamError) {
eprintln!("[Audio] an error occurred on audio stream: {err}");
}
}