mod renderer_with_cpu_usage;
mod stream_manager;
use std::sync::Mutex;
use renderer_with_cpu_usage::RendererWithCpuUsage;
use rtrb::Consumer;
use stream_manager::{StreamManager, StreamManagerController};
use crate::backend::{Backend, Renderer};
use cpal::{
BufferSize, Device, StreamConfig, StreamError,
traits::{DeviceTrait, HostTrait},
};
use super::{CpalBackendSettings, Error};
enum State {
Empty,
Uninitialized {
device: Device,
config: StreamConfig,
},
Initialized {
stream_manager_controller: StreamManagerController,
},
}
pub struct CpalBackend {
state: State,
custom_device: bool,
buffer_size: BufferSize,
cpu_usage_consumer: Option<Mutex<Consumer<f32>>>,
}
impl CpalBackend {
#[cfg_attr(docsrs, doc(cfg(not(wasm32))))]
pub fn pop_cpu_usage(&mut self) -> Option<f32> {
self.cpu_usage_consumer
.as_mut()
.unwrap()
.get_mut()
.unwrap()
.pop()
.ok()
}
#[cfg_attr(docsrs, doc(cfg(not(wasm32))))]
pub fn pop_error(&mut self) -> Option<StreamError> {
if let State::Initialized {
stream_manager_controller,
} = &mut self.state
{
stream_manager_controller.pop_handled_error()
} else {
None
}
}
#[must_use]
#[cfg_attr(docsrs, doc(cfg(not(wasm32))))]
pub fn num_stream_errors_discarded(&self) -> Option<u64> {
if let State::Initialized {
stream_manager_controller,
} = &self.state
{
Some(stream_manager_controller.num_stream_errors_discarded())
} else {
None
}
}
}
impl Backend for CpalBackend {
type Settings = CpalBackendSettings;
type Error = Error;
fn setup(
settings: Self::Settings,
_internal_buffer_size: usize,
) -> Result<(Self, u32), Self::Error> {
let host = cpal::default_host();
let (device, custom_device) = if let Some(device) = settings.device {
(device, true)
} else {
(
host.default_output_device()
.ok_or(Error::NoDefaultOutputDevice)?,
false,
)
};
let config = if let Some(config) = settings.config {
config
} else {
device.default_output_config()?.config()
};
let sample_rate = config.sample_rate;
let buffer_size = config.buffer_size;
Ok((
Self {
state: State::Uninitialized { device, config },
custom_device,
buffer_size,
cpu_usage_consumer: None,
},
sample_rate,
))
}
fn start(&mut self, renderer: Renderer) -> Result<(), Self::Error> {
let state = std::mem::replace(&mut self.state, State::Empty);
if let State::Uninitialized { device, config } = state {
let (renderer, cpu_usage_consumer) = RendererWithCpuUsage::new(renderer);
self.state = State::Initialized {
stream_manager_controller: StreamManager::start(
renderer,
device,
config,
self.custom_device,
self.buffer_size,
)?,
};
self.cpu_usage_consumer = Some(Mutex::new(cpu_usage_consumer));
} else {
panic!("Cannot initialize the backend multiple times")
}
Ok(())
}
}
impl Drop for CpalBackend {
fn drop(&mut self) {
if let State::Initialized {
stream_manager_controller,
} = &self.state
{
stream_manager_controller.stop();
}
}
}