use crate::{arc, av, define_obj_type, ns, objc, os};
use super::{ConnectionPoint, Format, InputNode, Node, NodeBus, OutputNode, mixer_node::MixerNode};
#[derive(PartialEq, Eq, Debug, Clone, Copy)]
#[repr(transparent)]
pub struct ManualRenderingError(pub os::Status);
impl ManualRenderingError {
pub const INVALID_MODE: Self = Self(os::Status(-80800));
pub const INITIALIZED: Self = Self(os::Status(-80801));
pub const NOT_RUNNING: Self = Self(os::Status(-80801));
}
impl PartialEq<os::Status> for ManualRenderingError {
fn eq(&self, other: &os::Status) -> bool {
self.0 == *other
}
}
#[derive(PartialEq, Eq, Debug, Clone, Copy)]
#[repr(isize)]
pub enum ManualRenderingStatus {
Error = -1,
Success = 0,
InsufficientDataFromInputNode = 1,
CannotDoInCurrentContext = 2,
}
#[derive(PartialEq, Eq, Debug, Clone, Copy)]
#[repr(isize)]
pub enum ManualRenderingMode {
Offline = 0,
Realtime = 1,
}
define_obj_type!(
#[doc(alias = "AVAudioEngine")]
pub Engine(ns::Id), AV_AUDIO_ENGINE
);
impl Engine {
#[objc::msg_send(attachNode:)]
pub fn attach_node(&mut self, node: &Node);
#[objc::msg_send(detachNode:)]
pub fn detach_node(&mut self, node: &Node);
#[objc::msg_send(connect:to:fromBus:toBus:format:)]
pub fn connect_node_to_node_bus_to_bus(
&mut self,
node_from: &Node,
node_to: &Node,
from_bus: NodeBus,
to_bus: NodeBus,
format: Option<&Format>,
);
#[objc::msg_send(connect:to:format:)]
pub fn connect_node_to_node(
&mut self,
node_from: &Node,
node_to: &Node,
format: Option<&Format>,
);
#[objc::msg_send(connect:toConnectionPoints:fromBus:format:)]
pub fn connect_node_to_connection_points_from_bus(
&mut self,
node: &Node,
connection_pods: &ns::Array<ConnectionPoint>,
from_bus: NodeBus,
format: Option<&Format>,
);
#[objc::msg_send(disconnectNodeInput:bus:)]
pub fn disconnect_node_input_bus(&mut self, node: &Node, bus: NodeBus);
#[objc::msg_send(disconnectNodeInput:)]
pub fn disconnect_node_input(&mut self, node: &Node);
#[objc::msg_send(disconnectNodeOutput:bus:)]
pub fn disconnect_node_output_bus(&mut self, node: &Node, bus: NodeBus);
#[objc::msg_send(disconnectNodeOutput:)]
pub fn disconnect_node_output(&mut self, node: &Node);
#[objc::msg_send(prepare)]
pub fn prepare(&mut self);
#[objc::msg_send(startAndReturnError:)]
pub unsafe fn start_and_return_err<'ar>(&self, error: *mut Option<&'ar ns::Error>) -> bool;
#[inline]
pub fn start(&mut self) -> ns::Result {
ns::if_false(|err| unsafe { self.start_and_return_err(err) })
}
#[objc::msg_send(inputNode)]
pub fn input_node(&self) -> arc::R<InputNode>;
#[objc::msg_send(outputNode)]
pub fn output_node(&self) -> arc::R<OutputNode>;
#[objc::msg_send(mainMixerNode)]
pub fn main_mixer_node(&self) -> &MixerNode;
#[objc::msg_send(mainMixerNode)]
pub fn main_mixer_node_mut(&mut self) -> &mut MixerNode;
#[objc::msg_send(reset)]
pub fn reset(&mut self);
#[objc::msg_send(pause)]
pub fn pause(&mut self);
#[objc::msg_send(stop)]
pub fn stop(&mut self);
#[objc::msg_send(isRunning)]
pub fn is_running(&self) -> bool;
#[objc::msg_send(isAutoShutdownEnabled)]
pub fn is_auto_shutdown_enabled(&self) -> bool;
#[objc::msg_send(setAutoShutdownEnabled:)]
pub fn set_auto_shutdown_enabled(&mut self, val: bool);
#[objc::msg_send(attachedNodes)]
pub fn attached_nodes(&self) -> arc::R<ns::Set<av::AudioNode>>;
}
impl Engine {
#[objc::msg_send(enableManualRenderingMode:format:maximumFrameCount:error:)]
pub unsafe fn enable_manual_rendering_mode_err<'ar>(
&mut self,
mode: ManualRenderingMode,
format: &av::AudioFormat,
max_frame_count: av::AudioFrameCount,
error: *mut Option<&'ar ns::Error>,
);
#[inline]
pub fn enable_manual_rendering_mode(
&mut self,
mode: ManualRenderingMode,
format: &av::AudioFormat,
max_frame_count: av::AudioFrameCount,
) -> ns::Result {
ns::if_err(|err| unsafe {
self.enable_manual_rendering_mode_err(mode, format, max_frame_count, err)
})
}
#[objc::msg_send(disableManualRenderingMode)]
pub fn disable_manual_rendering_mode(&mut self);
#[objc::msg_send(renderOffline:toBuffer:error:)]
pub fn render_offline_err<'ar>(
&mut self,
number_of_frames: av::AudioFrameCount,
to_buf: &mut av::audio::PcmBuf,
error: *mut Option<&'ar ns::Error>,
) -> av::audio::EngineManualRenderingStatus;
#[inline]
pub fn render_offline<'ar>(
&mut self,
number_of_frames: av::AudioFrameCount,
to_buf: &mut av::audio::PcmBuf,
) -> Result<av::audio::EngineManualRenderingStatus, &'ar ns::Error> {
let mut error = None;
let status = self.render_offline_err(number_of_frames, to_buf, &mut error);
if status == ManualRenderingStatus::Error && error.is_some() {
return Err(unsafe { error.unwrap_unchecked() });
}
Ok(status)
}
#[objc::msg_send(isInManualRenderingMode)]
pub fn is_in_manual_rendering_mode(&self) -> bool;
#[objc::msg_send(manualRenderingMode)]
pub fn manual_rendering_mode(&self) -> ManualRenderingMode;
#[objc::msg_send(manualRenderingFormat)]
pub fn manual_rendering_format(&self) -> arc::R<av::audio::Format>;
#[objc::msg_send(manualRenderingMaximumFrameCount)]
pub fn manual_rendering_max_frame_count(&self) -> av::audio::FrameCount;
#[objc::msg_send(manualRenderingSampleTime)]
pub fn manual_rendering_sample_time(&self) -> av::audio::FramePos;
}
#[link(name = "av", kind = "static")]
unsafe extern "C" {
static AV_AUDIO_ENGINE: &'static objc::Class<Engine>;
}
#[cfg(test)]
mod tests {
use crate::av;
#[test]
fn basics() {
let engine = av::audio::Engine::new();
assert!(!engine.is_running());
assert!(!engine.is_in_manual_rendering_mode());
assert_eq!(engine.manual_rendering_format().channel_count(), 0);
assert_eq!(engine.is_auto_shutdown_enabled(), false);
assert_eq!(engine.attached_nodes().len(), 0);
let s1 = engine.attached_nodes();
let s2 = engine.attached_nodes();
unsafe {
assert_eq!(
s1.as_type_ref().as_type_ptr(),
s2.as_type_ref().as_type_ptr()
);
}
let _output_node = engine.output_node();
let input_node = engine.input_node();
let _en = input_node.engine().expect("engine");
assert_eq!(engine.attached_nodes().len(), 2);
let s3 = engine.attached_nodes();
unsafe {
assert_ne!(
s1.as_type_ref().as_type_ptr(),
s3.as_type_ref().as_type_ptr()
);
}
}
#[test]
fn manual_redering_modes() {
let format =
av::audio::Format::standard_with_sample_rate_and_channels(44_100.0, 2).unwrap();
let mut engine = av::audio::Engine::new();
engine
.enable_manual_rendering_mode(
av::audio::engine::ManualRenderingMode::Offline,
&format,
1024,
)
.expect("Failed to enter manual rendering mode");
assert!(engine.is_in_manual_rendering_mode());
assert_eq!(
engine.manual_rendering_format().channel_count(),
format.channel_count()
);
let mut pcm_buf = av::audio::PcmBuf::with_format(&format, 1024).unwrap();
pcm_buf.set_frame_len(1024).unwrap();
engine.start().expect("Failed to start engine");
assert!(engine.is_running());
engine.stop();
let player_node = av::AudioPlayerNode::new();
engine.attach_node(&player_node);
let mixer_node = engine.main_mixer_node().retained();
engine.connect_node_to_node(&player_node, &mixer_node, None);
engine.start().expect("Failed to start engine");
assert_eq!(engine.manual_rendering_sample_time(), 0);
player_node.play();
let status = engine.render_offline(1024, &mut pcm_buf).unwrap();
assert_eq!(status, av::audio::EngineManualRenderingStatus::Success);
assert_eq!(pcm_buf.frame_len(), 1024);
assert_eq!(engine.manual_rendering_sample_time(), 1024);
}
}