use cpal::traits::DeviceTrait;
use cpal::{Device, Error, ErrorKind, SampleFormat, Stream, StreamConfig};
use ringbuf::traits::{Consumer, Producer, Split};
use ringbuf::{HeapCons, HeapProd, HeapRb};
use std::marker::PhantomData;
pub mod mixer;
pub mod types;
pub mod utils;
pub use mixer::{ClearSignal, MixCommand, Mixer, MixerHandle};
pub use types::{OutputType, SampleRate, SampleType};
pub use utils::{
default_device, device_name, find_device, get_host_by_id, get_host_by_name, get_host_in_name,
list_device_names, list_devices, list_hosts,
};
const DEFAULT_BUFFER_FRAMES: i32 = 1024;
const COMMAND_QUEUE_LEN: usize = 256;
pub struct OutputClass<S: SampleType> {
channels: u16,
device: Device,
out_type: OutputType,
buffer_size: Option<i32>,
sample_rate: SampleRate,
stream: Option<Stream>,
stream_config: StreamConfig,
commands: HeapProd<MixCommand<S>>,
consumer: Option<HeapCons<S>>,
mixer: MixerHandle,
sample_type: PhantomData<S>,
}
impl<S: SampleType> OutputClass<S> {
pub fn new(device: Option<Device>, out_type: OutputType, channels: u16, sample_rate:SampleRate, buffer_size: Option<i32>) -> Self {
let buffer_size_form = match buffer_size {
Some(frames) => cpal::BufferSize::Fixed(frames as u32),
None => cpal::BufferSize::Default,
};
let frames = buffer_size.unwrap_or(DEFAULT_BUFFER_FRAMES).max(1) as usize;
let stream_config = StreamConfig {
channels,
sample_rate: sample_rate as u32,
buffer_size: buffer_size_form,
};
let capacity = frames * channels.max(1) as usize;
let (audio_producer, consumer) = HeapRb::<S>::new(capacity).split();
let (commands, pending) = HeapRb::<MixCommand<S>>::new(COMMAND_QUEUE_LEN).split();
let mixer = MixerHandle::spawn(Mixer::new(pending, audio_producer));
OutputClass {
channels,
device: device
.unwrap_or_else(|| default_device().expect("no default output device available")),
out_type,
buffer_size,
sample_rate,
stream: None,
stream_config,
commands,
consumer: Some(consumer),
mixer,
sample_type: PhantomData,
}
}
pub fn channels(&self) -> u16 {
self.channels
}
pub fn device(&self) -> Device {
self.device.clone()
}
pub fn name(&self) -> String {
device_name(&self.device)
}
pub fn buffer_size(&self) -> Option<i32> {
self.buffer_size
}
pub fn sample_rate(&self) -> i32 {
self.sample_rate as i32
}
pub fn sample_format(&self) -> SampleFormat {
S::format()
}
pub fn sample_type(&self) -> SampleFormat {
S::format()
}
pub fn out_type(&self) -> OutputType {
self.out_type
}
fn data_callback(
data: &mut [S],
buffer: &mut HeapCons<S>,
clear: &ClearSignal,
cleared: &mut usize,
) {
let epoch = clear.epoch();
if epoch != *cleared {
buffer.clear();
data.fill(S::SILENCE);
if clear.acked() == epoch {
*cleared = epoch;
}
return;
}
let read = buffer.pop_slice(data);
if read < data.len() {
data[read..].fill(S::SILENCE);
}
}
pub fn build_stream(&mut self) -> Result<&Stream, Error> {
let mut buffer = self.consumer.take().ok_or_else(|| {
Error::with_message(
ErrorKind::UnsupportedOperation,
"stream already built for this output",
)
})?;
let clear = self.mixer.clear_signal();
let mut cleared = clear.epoch();
let stream = self.device.build_output_stream(
self.stream_config,
move |data: &mut [S], _: &cpal::OutputCallbackInfo| {
Self::data_callback(data, &mut buffer, &clear, &mut cleared)
},
Self::err_fn,
None,
)?;
self.stream = Some(stream);
Ok(self.stream.as_ref().unwrap())
}
pub fn add_samples(&mut self, samples: &[S], index: usize) -> Result<usize, Error> {
let command = MixCommand {
index,
samples: samples.to_vec(),
};
self.commands.try_push(command).map_err(|_| {
Error::with_message(ErrorKind::ResourceExhausted, "mixer command queue is full")
})?;
Ok(index + samples.len())
}
pub fn add_samples_time(&mut self, samples: &[S], time: usize) -> Result<usize, Error> {
let index = time * self.sample_rate as usize * self.channels.max(1) as usize;
let command = MixCommand {
index,
samples: samples.to_vec(),
};
self.commands.try_push(command).map_err(|_| {
Error::with_message(ErrorKind::ResourceExhausted, "mixer command queue is full")
})?;
Ok(index + samples.len())
}
pub fn stop(&mut self) {
self.mixer.clear_samples();
}
pub fn close(&mut self) {
self.stream.take();
}
pub fn mixer(&self) -> &MixerHandle {
&self.mixer
}
pub fn align_samples(&self, samples: &[S], sample_rate: SampleRate, channels: u16, interleaved: bool) -> Result<Vec<S>, Error> {
if channels == 0 {
return Err(Error::with_message(
ErrorKind::InvalidInput,
"sample data must have at least one channel",
));
}
let src_channels = channels as usize;
if samples.len() % src_channels != 0 {
return Err(Error::with_message(
ErrorKind::InvalidInput,
"sample count is not a whole number of frames",
));
}
if sample_rate == self.sample_rate && channels == self.channels && interleaved {
return Ok(samples.to_vec());
}
let dst_channels = self.channels.max(1) as usize;
let frames = samples.len() / src_channels;
let mut buffer = if interleaved || src_channels == 1 {
samples.to_vec()
} else {
Self::weave(samples, frames, src_channels)
};
if src_channels != dst_channels {
buffer = Self::map_channels(&buffer, frames, src_channels, dst_channels);
}
if sample_rate != self.sample_rate {
buffer = Self::resample(
&buffer,
frames,
dst_channels,
sample_rate as u32,
self.sample_rate as u32,
);
}
Ok(buffer)
}
fn weave(samples: &[S], frames: usize, channels: usize) -> Vec<S> {
let mut out = Vec::with_capacity(samples.len());
for frame in 0..frames {
for channel in 0..channels {
out.push(samples[(channel * frames) + frame]);
}
}
out
}
fn map_channels(samples: &[S], frames: usize, src_channels: usize, dst_channels: usize) -> Vec<S> {
let mut out = Vec::with_capacity(frames * dst_channels);
for frame in 0..frames {
let start = frame * src_channels;
if src_channels == 1 {
out.extend(std::iter::repeat(samples[start]).take(dst_channels));
} else if dst_channels == 1 {
let mut sum = S::SILENCE;
for sample in &samples[start..start + src_channels] {
sum = sum.mix(*sample);
}
out.push(sum);
} else {
for channel in 0..dst_channels {
out.push(if channel < src_channels {
samples[start + channel]
} else {
S::SILENCE
});
}
}
}
out
}
fn resample(samples: &[S], frames: usize, channels: usize, from: u32, to: u32) -> Vec<S> {
if frames == 0 {
return Vec::new();
}
let ratio = to as f64 / from as f64;
let out_frames = ((frames as f64) * ratio).round().max(1.0) as usize;
let last = frames - 1;
let mut out = Vec::with_capacity(out_frames * channels);
for frame in 0..out_frames {
let position = frame as f64 / ratio;
let left = (position.floor() as usize).min(last);
let right = (left + 1).min(last);
let t = (position - position.floor()) as f32;
for channel in 0..channels {
let a = samples[left * channels + channel].to_f32();
let b = samples[right * channels + channel].to_f32();
out.push(S::from_f32(a + (b - a) * t));
}
}
out
}
fn err_fn(err: cpal::Error) {
eprintln!("audio output stream error: {}", err);
}
}