use sim_kernel::{Error, Result, Symbol};
use sim_lib_stream_audio::PcmSpec;
use sim_lib_stream_host::HostDirection;
use crate::coreaudio_backend_symbol;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct CoreAudioTiming {
sample_rate_hz: u32,
buffer_frames: usize,
input_latency_frames: u32,
output_latency_frames: u32,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct CoreAudioDevice {
id: Symbol,
name: String,
direction: HostDirection,
channels: usize,
timing: CoreAudioTiming,
default_output: bool,
default_input: bool,
}
impl CoreAudioTiming {
pub fn new(
sample_rate_hz: u32,
buffer_frames: usize,
input_latency_frames: u32,
output_latency_frames: u32,
) -> Result<Self> {
if sample_rate_hz == 0 {
return Err(Error::Eval(
"CoreAudio sample rate must be greater than zero".to_owned(),
));
}
if buffer_frames == 0 {
return Err(Error::Eval(
"CoreAudio buffer size must be greater than zero".to_owned(),
));
}
Ok(Self {
sample_rate_hz,
buffer_frames,
input_latency_frames,
output_latency_frames,
})
}
pub fn default_low_latency() -> Self {
Self::new(48_000, 128, 128, 128).expect("valid CoreAudio timing")
}
pub fn sample_rate_hz(self) -> u32 {
self.sample_rate_hz
}
pub fn buffer_frames(self) -> usize {
self.buffer_frames
}
pub fn input_latency_frames(self) -> u32 {
self.input_latency_frames
}
pub fn output_latency_frames(self) -> u32 {
self.output_latency_frames
}
}
impl CoreAudioDevice {
pub fn output(
id: impl Into<String>,
name: impl Into<String>,
channels: usize,
timing: CoreAudioTiming,
) -> Result<Self> {
Self::new(id, name, HostDirection::Output, channels, timing)
}
pub fn input(
id: impl Into<String>,
name: impl Into<String>,
channels: usize,
timing: CoreAudioTiming,
) -> Result<Self> {
Self::new(id, name, HostDirection::Input, channels, timing)
}
pub fn duplex(
id: impl Into<String>,
name: impl Into<String>,
channels: usize,
timing: CoreAudioTiming,
) -> Result<Self> {
Self::new(id, name, HostDirection::Duplex, channels, timing)
}
pub fn new(
id: impl Into<String>,
name: impl Into<String>,
direction: HostDirection,
channels: usize,
timing: CoreAudioTiming,
) -> Result<Self> {
if channels == 0 {
return Err(Error::Eval(
"CoreAudio device channel count must be greater than zero".to_owned(),
));
}
Ok(Self {
id: Symbol::new(id.into()),
name: name.into(),
direction,
channels,
timing,
default_output: false,
default_input: false,
})
}
pub fn with_default_output(mut self) -> Self {
self.default_output = true;
self
}
pub fn with_default_input(mut self) -> Self {
self.default_input = true;
self
}
pub fn id(&self) -> &Symbol {
&self.id
}
pub fn name(&self) -> &str {
&self.name
}
pub fn direction(&self) -> HostDirection {
self.direction
}
pub fn channels(&self) -> usize {
self.channels
}
pub fn timing(&self) -> CoreAudioTiming {
self.timing
}
pub fn default_output(&self) -> bool {
self.default_output
}
pub fn default_input(&self) -> bool {
self.default_input
}
pub fn spec(&self) -> Result<PcmSpec> {
PcmSpec::f32(self.channels, self.timing.sample_rate_hz)
}
pub fn is_compatible_with(&self, requested: HostDirection) -> bool {
self.direction == requested || self.direction == HostDirection::Duplex
}
pub fn port_symbol(&self) -> Symbol {
Symbol::new(format!("{}/port", self.id))
}
pub fn backend(&self) -> Symbol {
coreaudio_backend_symbol()
}
}
pub fn macos_audio_backend_priority() -> Vec<Symbol> {
vec![
Symbol::qualified("stream/host", "portaudio"),
Symbol::qualified("stream/host", "rtaudio"),
coreaudio_backend_symbol(),
]
}
pub fn macos_midi_backend_priority() -> Vec<Symbol> {
vec![
Symbol::qualified("stream/host", "rtmidi"),
Symbol::qualified("stream/host", "coremidi"),
]
}