use crate::{stream, Buffer, Device, Requester, Stream, StreamError};
use cpal::traits::{DeviceTrait, EventLoopTrait, HostTrait};
use sample::{Sample, ToSample};
use std::sync::atomic::AtomicBool;
use std::sync::mpsc;
use std::sync::{Arc, Mutex};
pub type Result<'a, S = f32> = std::result::Result<&'a mut Buffer<S>, StreamError>;
pub trait RenderFn<M, S>: Fn(&mut M, &mut Buffer<S>) {}
pub trait RenderResultFn<M, S>: Fn(&mut M, Result<S>) {}
pub type DefaultRenderFn<M, S> = fn(&mut M, &mut Buffer<S>);
pub type DefaultRenderResultFn<M, S> = fn(&mut M, Result<S>);
pub(crate) fn default_render_fn<M, S>(_: &mut M, _: &mut Buffer<S>) {}
pub enum Render<A, B> {
BufferFn(A),
ResultFn(B),
}
pub struct Builder<M, FA, FB, S = f32> {
pub builder: super::Builder<M, S>,
pub render: Render<FA, FB>,
}
pub type BuilderInit<M, S = f32> =
Builder<M, DefaultRenderFn<M, S>, DefaultRenderResultFn<M, S>, S>;
type OutputDevices = cpal::OutputDevices<cpal::Devices>;
pub struct Devices {
pub(crate) devices: OutputDevices,
}
impl<M, S, F> RenderFn<M, S> for F where F: Fn(&mut M, &mut Buffer<S>) {}
impl<M, S, F> RenderResultFn<M, S> for F where F: Fn(&mut M, Result<S>) {}
impl<A, B> Render<A, B> {
pub(crate) fn render<M, S>(&self, model: &mut M, result: Result<S>)
where
A: RenderFn<M, S>,
B: RenderResultFn<M, S>,
{
match *self {
Render::BufferFn(ref f) => {
let buffer = match result {
Ok(b) => b,
Err(err) => {
panic!(
"An output stream error occurred: {}\nIf you wish to handle this \
error within your code, consider building your output stream with \
a `render_result` function rather than a `render` function.",
err,
);
}
};
f(model, buffer);
}
Render::ResultFn(ref f) => f(model, result),
}
}
}
impl<M, FA, FB, S> Builder<M, FA, FB, S> {
pub fn render<G>(self, render: G) -> Builder<M, G, DefaultRenderResultFn<M, S>, S> {
let Builder { builder, .. } = self;
let render = Render::BufferFn(render);
Builder { render, builder }
}
pub fn render_result<G>(self, render_result: G) -> Builder<M, DefaultRenderFn<M, S>, G, S> {
let Builder { builder, .. } = self;
let render = Render::ResultFn(render_result);
Builder { render, builder }
}
pub fn sample_rate(mut self, sample_rate: u32) -> Self {
assert!(sample_rate > 0);
self.builder.sample_rate = Some(sample_rate);
self
}
pub fn channels(mut self, channels: usize) -> Self {
assert!(channels > 0);
self.builder.channels = Some(channels);
self
}
pub fn device(mut self, device: Device) -> Self {
self.builder.device = Some(device);
self
}
pub fn frames_per_buffer(mut self, frames_per_buffer: usize) -> Self {
assert!(frames_per_buffer > 0);
self.builder.frames_per_buffer = Some(frames_per_buffer);
self
}
pub fn build(self) -> std::result::Result<Stream<M>, super::BuildError>
where
S: 'static + Send + Sample + ToSample<u16> + ToSample<i16> + ToSample<f32>,
M: 'static + Send,
FA: 'static + RenderFn<M, S> + Send,
FB: 'static + RenderResultFn<M, S> + Send,
{
let Builder {
render,
builder:
stream::Builder {
host,
event_loop,
process_fn_tx,
model,
sample_rate,
channels,
frames_per_buffer,
device,
..
},
} = self;
let sample_rate = sample_rate
.map(|sr| cpal::SampleRate(sr))
.or(Some(cpal::SampleRate(super::DEFAULT_SAMPLE_RATE)));
let sample_format = super::cpal_sample_format::<S>();
let device = match device {
None => host
.default_output_device()
.ok_or(super::BuildError::DefaultDevice)?,
Some(Device { device }) => device,
};
let format = super::find_best_matching_format(
&device,
sample_format,
channels,
sample_rate,
device.default_output_format().ok(),
|device| device.supported_output_formats().map(|fs| fs.collect()),
)?
.expect("no matching supported audio output formats for the target device");
let stream_id = event_loop.build_output_stream(&device, &format)?;
let (update_tx, update_rx) = mpsc::channel();
let model = Arc::new(Mutex::new(Some(model)));
let model_2 = model.clone();
let num_channels = format.channels as usize;
let sample_rate = format.sample_rate.0;
let mut pending_updates: Vec<Box<dyn FnMut(&mut M) + 'static + Send>> = Vec::new();
let frames_per_buffer = frames_per_buffer.unwrap_or(Buffer::<S>::DEFAULT_LEN_FRAMES);
let mut requester = Requester::new(frames_per_buffer, num_channels);
let mut samples = vec![S::equilibrium(); frames_per_buffer * num_channels];
let proc_output = move |data: cpal::StreamDataResult| {
macro_rules! process_pending_updates {
() => {
pending_updates.extend(update_rx.try_iter());
if !pending_updates.is_empty() {
if let Ok(mut guard) = model_2.lock() {
let mut model = guard.take().unwrap();
for mut update in pending_updates.drain(..) {
update(&mut model);
}
*guard = Some(model);
}
}
};
}
process_pending_updates!();
let output = match data {
Err(err) => {
if let Ok(mut guard) = model_2.lock() {
let mut m = guard.take().unwrap();
render.render(&mut m, Err(err));
}
return;
}
Ok(cpal::StreamData::Output { buffer }) => buffer,
_ => unreachable!(),
};
samples.clear();
samples.resize(output.len(), S::equilibrium());
if let Ok(mut guard) = model_2.lock() {
let mut m = guard.take().unwrap();
m = requester.fill_buffer(m, &render, &mut samples, num_channels, sample_rate);
*guard = Some(m);
}
fn fill_output<O, S>(output: &mut [O], buffer: &[S])
where
O: Sample,
S: Sample + ToSample<O>,
{
for (out_sample, sample) in output.iter_mut().zip(buffer) {
*out_sample = sample.to_sample();
}
}
match output {
cpal::UnknownTypeOutputBuffer::U16(mut buffer) => {
fill_output(&mut buffer, &samples);
}
cpal::UnknownTypeOutputBuffer::I16(mut buffer) => {
fill_output(&mut buffer, &samples);
}
cpal::UnknownTypeOutputBuffer::F32(mut buffer) => {
fill_output(&mut buffer, &samples)
}
}
process_pending_updates!();
};
process_fn_tx
.send((stream_id.clone(), Box::new(proc_output)))
.unwrap();
let shared = Arc::new(super::Shared {
model,
stream_id,
event_loop,
is_paused: AtomicBool::new(false),
});
let stream = Stream {
shared,
process_fn_tx,
update_tx,
cpal_format: format,
};
Ok(stream)
}
}
impl<M, S> Default for Render<DefaultRenderFn<M, S>, DefaultRenderResultFn<M, S>> {
fn default() -> Self {
Render::BufferFn(default_render_fn)
}
}
impl Iterator for Devices {
type Item = Device;
fn next(&mut self) -> Option<Self::Item> {
self.devices.next().map(|device| Device { device })
}
}