use std::{marker::PhantomData, mem::MaybeUninit, sync::Arc};
use maudio_sys::ffi as sys;
use crate::{
engine::{
node_graph::{
nodes::{private_node::SplitterNodeProvider, AsNodePtr, NodeRef},
AsNodeGraphPtr, NodeGraph,
},
AllocationCallbacks,
},
AsRawRef, Binding, MaResult,
};
pub struct SplitterNode<'a> {
inner: *mut sys::ma_splitter_node,
alloc_cb: Option<Arc<AllocationCallbacks>>,
_marker: PhantomData<&'a NodeGraph>,
}
impl Binding for SplitterNode<'_> {
type Raw = *mut sys::ma_splitter_node;
fn from_ptr(_raw: Self::Raw) -> Self {
unimplemented!()
}
fn to_raw(&self) -> Self::Raw {
self.inner
}
}
#[doc(hidden)]
impl AsNodePtr for SplitterNode<'_> {
type __PtrProvider = SplitterNodeProvider;
}
impl<'a> SplitterNode<'a> {
fn new_with_cfg_alloc_internal<N: AsNodeGraphPtr + ?Sized>(
node_graph: &N,
config: &SplitterNodeBuilder<'_, N>,
alloc: Option<Arc<AllocationCallbacks>>,
) -> MaResult<Self> {
let alloc_cb: *const sys::ma_allocation_callbacks =
alloc.clone().map_or(core::ptr::null(), |c| c.as_raw_ptr());
let mut mem: Box<std::mem::MaybeUninit<sys::ma_splitter_node>> =
Box::new(MaybeUninit::uninit());
n_splitter_ffi::ma_splitter_node_init(
node_graph,
config.as_raw_ptr(),
alloc_cb,
mem.as_mut_ptr(),
)?;
let inner: *mut sys::ma_splitter_node = Box::into_raw(mem) as *mut sys::ma_splitter_node;
Ok(Self {
inner,
alloc_cb: alloc,
_marker: PhantomData,
})
}
pub fn as_node(&self) -> NodeRef<'a> {
assert!(!self.to_raw().is_null());
let ptr = self.to_raw().cast::<sys::ma_node>();
NodeRef::from_ptr(ptr)
}
#[inline]
fn alloc_cb_ptr(&self) -> *const sys::ma_allocation_callbacks {
match &self.alloc_cb {
Some(cb) => cb.as_raw_ptr(),
None => core::ptr::null(),
}
}
}
pub(crate) mod n_splitter_ffi {
use crate::{
engine::node_graph::{
nodes::routing::splitter::SplitterNode, private_node_graph, AsNodeGraphPtr,
},
Binding, MaResult, MaudioError,
};
use maudio_sys::ffi as sys;
#[inline]
pub fn ma_splitter_node_init<N: AsNodeGraphPtr + ?Sized>(
node_graph: &N,
config: *const sys::ma_splitter_node_config,
alloc_cb: *const sys::ma_allocation_callbacks,
node: *mut sys::ma_splitter_node,
) -> MaResult<()> {
let res = unsafe {
sys::ma_splitter_node_init(
private_node_graph::node_graph_ptr(node_graph),
config,
alloc_cb,
node,
)
};
MaudioError::check(res)
}
#[inline]
pub fn ma_splitter_node_uninit(node: &mut SplitterNode) {
unsafe {
sys::ma_splitter_node_uninit(node.to_raw(), node.alloc_cb_ptr());
}
}
}
impl<'a> Drop for SplitterNode<'a> {
fn drop(&mut self) {
n_splitter_ffi::ma_splitter_node_uninit(self);
drop(unsafe { Box::from_raw(self.to_raw()) });
}
}
pub struct SplitterNodeBuilder<'a, N: AsNodeGraphPtr + ?Sized> {
inner: sys::ma_splitter_node_config,
node_graph: &'a N,
}
impl<N: AsNodeGraphPtr + ?Sized> AsRawRef for SplitterNodeBuilder<'_, N> {
type Raw = sys::ma_splitter_node_config;
fn as_raw(&self) -> &Self::Raw {
&self.inner
}
}
impl<'a, N: AsNodeGraphPtr + ?Sized> SplitterNodeBuilder<'a, N> {
pub fn new(node_graph: &'a N, channels: u32) -> Self {
let ptr = unsafe { sys::ma_splitter_node_config_init(channels) };
Self {
inner: ptr,
node_graph,
}
}
pub fn output_bus_count(&mut self, count: u32) -> &mut Self {
self.inner.outputBusCount = count;
self
}
pub fn build(&self) -> MaResult<SplitterNode<'a>> {
SplitterNode::new_with_cfg_alloc_internal(self.node_graph, self, None)
}
}
#[cfg(test)]
mod test {
use crate::engine::{
node_graph::{
node_builder::NodeState,
nodes::{routing::splitter::SplitterNodeBuilder, NodeOps},
},
Engine, EngineOps,
};
#[test]
fn test_splitter_basic_init() {
let engine = Engine::new_for_tests().unwrap();
let node_graph = engine.as_node_graph().unwrap();
let _node = SplitterNodeBuilder::new(&node_graph, 2)
.output_bus_count(2)
.build()
.unwrap();
}
#[test]
fn test_splitter_default_output_bus_count_builds() {
let engine = Engine::new_for_tests().unwrap();
let node_graph = engine.as_node_graph().unwrap();
let _node = SplitterNodeBuilder::new(&node_graph, 2).build().unwrap();
}
#[test]
fn test_splitter_various_output_bus_counts() {
let engine = Engine::new_for_tests().unwrap();
let node_graph = engine.as_node_graph().unwrap();
for &bus_count in &[1u32, 2, 4, 8] {
let _node = SplitterNodeBuilder::new(&node_graph, 2)
.output_bus_count(bus_count)
.build()
.unwrap();
}
}
#[test]
fn test_splitter_repeated_create_drop() {
let engine = Engine::new_for_tests().unwrap();
let node_graph = engine.as_node_graph().unwrap();
for _ in 0..100 {
let _node = SplitterNodeBuilder::new(&node_graph, 2)
.output_bus_count(2)
.build()
.unwrap();
}
}
#[test]
fn test_splitter_zero_channels_is_err() {
let engine = Engine::new_for_tests().unwrap();
let node_graph = engine.as_node_graph().unwrap();
let res = SplitterNodeBuilder::new(&node_graph, 0)
.output_bus_count(2)
.build();
assert!(res.is_err());
}
#[test]
fn test_splitter_zero_output_buses_is_err() {
let engine = Engine::new_for_tests().unwrap();
let node_graph = engine.as_node_graph().unwrap();
let res = SplitterNodeBuilder::new(&node_graph, 2)
.output_bus_count(0)
.build();
assert!(res.is_ok());
}
#[test]
fn test_splitter_as_node_is_valid() {
let engine = Engine::new_for_tests().unwrap();
let node_graph = engine.as_node_graph().unwrap();
let node = SplitterNodeBuilder::new(&node_graph, 2)
.output_bus_count(2)
.build()
.unwrap();
let _as_node = node.as_node();
}
#[test]
fn test_splitter_node_ref_bus_counts_and_channels() {
let engine = Engine::new_for_tests().unwrap();
let node_graph = engine.as_node_graph().unwrap();
let splitter = SplitterNodeBuilder::new(&node_graph, 2)
.output_bus_count(4)
.build()
.unwrap();
let node_ref = splitter.as_node();
assert_eq!(node_ref.in_bus_count(), 1);
assert_eq!(node_ref.out_bus_count(), 4);
assert_eq!(node_ref.input_channels(0), 2);
for out_bus in 0..4 {
assert_eq!(node_ref.output_channels(out_bus), 2);
}
}
#[test]
fn test_splitter_attach_and_detach_output_bus() {
let engine = Engine::new_for_tests().unwrap();
let node_graph = engine.as_node_graph().unwrap();
let splitter_a = SplitterNodeBuilder::new(&node_graph, 2)
.output_bus_count(2)
.build()
.unwrap();
let splitter_b = SplitterNodeBuilder::new(&node_graph, 2)
.output_bus_count(2)
.build()
.unwrap();
let mut a = splitter_a.as_node();
let mut b = splitter_b.as_node();
a.attach_output_bus(0, &mut b, 0).unwrap();
a.detach_output_bus(0).unwrap();
a.detach_all_outputs().unwrap();
}
#[test]
fn test_splitter_detach_all_outputs_after_multiple_attaches() {
let engine = Engine::new_for_tests().unwrap();
let node_graph = engine.as_node_graph().unwrap();
let splitter_a = SplitterNodeBuilder::new(&node_graph, 2)
.output_bus_count(4)
.build()
.unwrap();
let splitter_b = SplitterNodeBuilder::new(&node_graph, 2)
.output_bus_count(2)
.build()
.unwrap();
let mut a = splitter_a.as_node();
let mut b = splitter_b.as_node();
for out_bus in 0..4 {
a.attach_output_bus(out_bus, &mut b, 0).unwrap();
}
a.detach_all_outputs().unwrap();
}
#[test]
fn test_splitter_output_bus_volume_roundtrip() {
let engine = Engine::new_for_tests().unwrap();
let node_graph = engine.as_node_graph().unwrap();
let splitter = SplitterNodeBuilder::new(&node_graph, 2)
.output_bus_count(3)
.build()
.unwrap();
let mut node_ref = splitter.as_node();
let vols = [0.0_f32, 0.5_f32, 1.0_f32];
for (bus, &v) in vols.iter().enumerate() {
node_ref.set_output_bus_volume(bus as u32, v).unwrap();
let got = node_ref.output_bus_volume(bus as u32);
assert!((got - v).abs() < 1.0e-6);
}
}
#[test]
fn test_splitter_state_set_get() {
let engine = Engine::new_for_tests().unwrap();
let node_graph = engine.as_node_graph().unwrap();
let splitter = SplitterNodeBuilder::new(&node_graph, 2)
.output_bus_count(2)
.build()
.unwrap();
let mut node_ref = splitter.as_node();
node_ref.set_state(NodeState::Stopped).unwrap();
assert_eq!(node_ref.state().unwrap(), NodeState::Stopped);
node_ref.set_state(NodeState::Started).unwrap();
assert_eq!(node_ref.state().unwrap(), NodeState::Started);
}
#[test]
fn test_splitter_node_graph_ref_is_some() {
let engine = Engine::new_for_tests().unwrap();
let node_graph = engine.as_node_graph().unwrap();
let splitter = SplitterNodeBuilder::new(&node_graph, 2)
.output_bus_count(2)
.build()
.unwrap();
let node_ref = splitter.as_node();
assert!(node_ref.node_graph().is_some());
}
#[test]
fn test_splitter_invalid_attach_indices_is_err() {
let engine = Engine::new_for_tests().unwrap();
let node_graph = engine.as_node_graph().unwrap();
let splitter_a = SplitterNodeBuilder::new(&node_graph, 2)
.output_bus_count(2)
.build()
.unwrap();
let splitter_b = SplitterNodeBuilder::new(&node_graph, 2)
.output_bus_count(2)
.build()
.unwrap();
let mut a = splitter_a.as_node();
let mut b = splitter_b.as_node();
assert!(a.attach_output_bus(999, &mut b, 0).is_err());
assert!(a.attach_output_bus(0, &mut b, 999).is_err());
}
}