use std::result;
use symphonia::core::audio::{AudioBufferRef, SignalSpec, RawSample, SampleBuffer};
use symphonia::core::conv::{ConvertibleSample, IntoSample};
use symphonia::core::units::Duration;
use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
use cpal::{self, SizedSample};
use rb::*;
pub trait AudioStream {
fn write(&mut self, decoded: AudioBufferRef<'_>) -> Result<()>;
fn flush(&mut self);
}
#[derive(Debug)]
pub enum AudioOutputError {
OpenStreamError,
PlayStreamError,
StreamClosedError,
}
pub type Result<T> = result::Result<T, AudioOutputError>;
pub trait OutputSample: SizedSample + IntoSample<f64> +cpal::Sample + ConvertibleSample + RawSample + std::marker::Send + 'static {}
pub struct AudioOutput<T>
where T: OutputSample,
{
ring_buf_producer: rb::Producer<T>,
sample_buf: SampleBuffer<T>,
stream: cpal::Stream,
resampler: Option<T>,
}
impl OutputSample for i8 {}
impl OutputSample for i16 {}
impl OutputSample for i32 {}
impl OutputSample for u8 {}
impl OutputSample for u16 {}
impl OutputSample for u32 {}
impl OutputSample for f32 {}
impl OutputSample for f64 {}
pub fn open_stream(spec: SignalSpec, duration: Duration) -> Result<Box<dyn AudioStream>> {
let host = cpal::default_host();
let device = match host.default_output_device() {
Some(device) => device,
_ => return Err(AudioOutputError::OpenStreamError),
};
let config = match device.default_output_config() {
Ok(config) => config,
Err(err) => return Err(AudioOutputError::OpenStreamError),
};
return match config.sample_format(){
cpal::SampleFormat::I8 => AudioOutput::<i8>::create_stream(spec, &device, &config.into(), duration),
cpal::SampleFormat::I16 => AudioOutput::<i16>::create_stream(spec, &device, &config.into(), duration),
cpal::SampleFormat::I32 => AudioOutput::<i32>::create_stream(spec, &device, &config.into(), duration),
cpal::SampleFormat::U8 => AudioOutput::<u8>::create_stream(spec, &device, &config.into(), duration),
cpal::SampleFormat::U16 => AudioOutput::<u16>::create_stream(spec, &device, &config.into(), duration),
cpal::SampleFormat::U32 => AudioOutput::<u32>::create_stream(spec, &device, &config.into(), duration),
cpal::SampleFormat::F32 => AudioOutput::<f32>::create_stream(spec, &device, &config.into(), duration),
cpal::SampleFormat::F64 => AudioOutput::<f64>::create_stream(spec, &device, &config.into(), duration),
_ => todo!(),
};
}
impl<T: OutputSample> AudioOutput<T> {
fn create_stream(spec: SignalSpec, device: &cpal::Device, config: &cpal::StreamConfig, duration: Duration) -> Result<Box<dyn AudioStream>> {
let num_channels = config.channels as usize;
let ring_len = ((200 * config.sample_rate.0 as usize) / 1000) * num_channels;
let ring_buf= rb::SpscRb::new(ring_len);
let ring_buf_producer = ring_buf.producer();
let ring_buf_consumer = ring_buf.consumer();
let stream_result = device.build_output_stream(
config,
move |data: &mut [T], _: &cpal::OutputCallbackInfo| {
let written = ring_buf_consumer.read(data).unwrap_or(0);
data[written..].iter_mut().for_each(|sample| *sample = T::MID);
},
move |err| println!("Yeah we erroring out here"),
None
);
if let Err(err) = stream_result {
return Err(AudioOutputError::OpenStreamError);
}
let stream = stream_result.unwrap();
if let Err(err) = stream.play() {
return Err(AudioOutputError::PlayStreamError);
}
let sample_buf = SampleBuffer::<T>::new(duration, spec);
let resampler: Option<T> = None;
Ok(Box::new(AudioOutput { ring_buf_producer, sample_buf, stream, resampler}))
}
}
impl<T: OutputSample> AudioStream for AudioOutput<T> {
fn write(&mut self, decoded: AudioBufferRef<'_>) -> Result<()> {
if decoded.frames() == 0 {
return Ok(());
}
let mut samples: &[T] = if let Some(resampler) = &mut self.resampler {
println!("this should not print");
return Ok(())
} else {
self.sample_buf.copy_interleaved_ref(decoded);
self.sample_buf.samples()
};
while let Some(written) = self.ring_buf_producer.write_blocking(samples) {
samples = &samples[written..];
}
Ok(())
}
fn flush(&mut self) {
if let Some(resampler) = &mut self.resampler {
}
let _ = self.stream.pause();
}
}