pub mod error;
mod utils;
use cpal::{
traits::{DeviceTrait, HostTrait, StreamTrait},
FromSample, SizedSample,
};
use error::{AudioPlayerError, PlayError};
use ringbuf::{
traits::{Producer, Split},
HeapProd, HeapRb,
};
use rubato::{FftFixedInOut, Resampler, Sample};
struct AudioResampler<T: Sample> {
resampler: FftFixedInOut<T>,
pre_resampled_buffer: Vec<T>,
pre_resampled_split_buffers: [Vec<T>; 2],
resample_process_buffers: [Vec<T>; 2],
resampled_buffer: Vec<T>,
}
impl<T: Sample + SizedSample> AudioResampler<T> {
fn new(input_rate: usize, output_rate: usize) -> Result<Self, AudioPlayerError> {
let resampler = FftFixedInOut::<T>::new(
input_rate,
output_rate,
input_rate / 60,
2,
)?;
Ok(Self {
resampler,
pre_resampled_buffer: Vec::new(),
pre_resampled_split_buffers: [Vec::new(), Vec::new()],
resample_process_buffers: [Vec::new(), Vec::new()],
resampled_buffer: Vec::new(),
})
}
fn resample_into_producer(&mut self, data: &[T], producer: &mut HeapProd<T>) {
fn read_frames<T: Copy>(inbuffer: &[T], n_frames: usize, outputs: &mut [Vec<T>]) {
for output in outputs.iter_mut() {
output.clear();
output.reserve(n_frames);
}
let mut value: T;
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<T: Copy>(waves: &[Vec<T>], outbuffer: &mut Vec<T>) {
let nbr = waves[0].len();
for frame in 0..nbr {
for wave in waves.iter() {
outbuffer.push(wave[frame]);
}
}
}
self.pre_resampled_buffer.extend_from_slice(data);
loop {
let frames = self.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 = self.resampler.output_frames_next();
self.resample_process_buffers[0].resize(output_frames, T::EQUILIBRIUM);
self.resample_process_buffers[1].resize(output_frames, T::EQUILIBRIUM);
self.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);
producer.push_slice(&self.resampled_buffer);
self.pre_resampled_buffer = self.pre_resampled_buffer.split_off(frames * 2);
}
}
}
#[derive(Debug, Clone, Copy, Default)]
pub enum BufferSize {
#[default]
QuarterSecond,
HalfSecond,
OneSecond,
Samples(usize),
}
impl BufferSize {
#[inline]
#[must_use]
fn store_for_samples(&self, sample_rate: usize, channels: usize) -> usize {
match self {
Self::QuarterSecond => sample_rate / 4 * channels,
Self::HalfSecond => sample_rate / 2 * channels,
Self::OneSecond => sample_rate * channels,
Self::Samples(alternative_samples) => *alternative_samples,
}
}
}
pub struct AudioPlayer<T: Sample> {
buffer_producer: HeapProd<T>,
resampler: Option<AudioResampler<T>>,
output_stream: cpal::Stream,
}
impl<T: Sample + SizedSample> AudioPlayer<T>
where
i8: FromSample<T>,
i16: FromSample<T>,
i32: FromSample<T>,
i64: FromSample<T>,
u8: FromSample<T>,
u16: FromSample<T>,
u32: FromSample<T>,
u64: FromSample<T>,
f32: FromSample<T>,
f64: FromSample<T>,
{
pub fn new(sample_rate: u32, buffer_size: BufferSize) -> Result<Self, AudioPlayerError> {
let host = cpal::default_host();
let output_device = host
.default_output_device()
.ok_or(AudioPlayerError::NoOutputDevice)?;
let sample_rate = cpal::SampleRate(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() == T::FORMAT
&& c.min_sample_rate() <= sample_rate
&& c.max_sample_rate() >= sample_rate
{
found_conf = true;
break;
}
}
let (output_sample_rate, output_format, resampler) = if found_conf {
(sample_rate, T::FORMAT, None)
} else {
let mut max_match = 0;
let mut matched_conf = None;
for c in &conf {
let mut curr_match = 0;
if c.channels() == 2 {
curr_match += 1;
if c.sample_format() == T::FORMAT {
curr_match += 3;
}
if c.min_sample_rate() <= sample_rate && c.max_sample_rate() >= sample_rate {
curr_match += 2;
}
}
if curr_match > max_match {
max_match = curr_match;
matched_conf = Some(c);
}
}
let used_conf = match matched_conf {
Some(conf) => conf
.try_with_sample_rate(sample_rate)
.unwrap_or_else(|| conf.with_max_sample_rate()),
None => output_device.default_output_config()?,
};
if used_conf.channels() != 2 {
eprintln!("No supported configuration found for audio device, please open an issue in github `Amjad50/dynwave`\n\
list of supported configurations: {:#?}", conf);
return Err(AudioPlayerError::DualChannelNotSupported);
}
(
used_conf.sample_rate(),
used_conf.sample_format(),
Some(AudioResampler::new(
sample_rate.0 as usize,
used_conf.sample_rate().0 as usize,
)?),
)
};
let config = cpal::StreamConfig {
channels: 2,
sample_rate: output_sample_rate,
buffer_size: cpal::BufferSize::Default,
};
let ring_buffer_len = buffer_size.store_for_samples(output_sample_rate.0 as usize, 2);
let buffer = HeapRb::new(ring_buffer_len);
let (buffer_producer, buffer_consumer) = buffer.split();
let output_data_fn = utils::create_output_processor(output_format, buffer_consumer);
let output_stream = output_device.build_output_stream_raw(
&config,
output_format,
output_data_fn,
Self::err_fn,
None,
)?;
Ok(Self {
buffer_producer,
output_stream,
resampler,
})
}
pub fn play(&self) -> Result<(), PlayError> {
self.output_stream.play().map_err(|e| e.into())
}
pub fn pause(&self) -> Result<(), PlayError> {
self.output_stream.pause().map_err(|e| e.into())
}
pub fn queue(&mut self, data: &[T]) {
if let Some(resampler) = &mut self.resampler {
resampler.resample_into_producer(data, &mut self.buffer_producer);
} else {
self.buffer_producer.push_slice(data);
}
}
fn err_fn(err: cpal::StreamError) {
eprintln!("an error occurred on audio stream: {}", err);
}
}