extern crate alloc;
use alloc::sync::Arc;
use alloc::vec::Vec;
use crate::apo::ProcessInput;
use crate::buffer::BufferFlags;
use crate::error::HResult;
use crate::format::{Format, FormatNegotiation};
use crate::instance::{AnyApoInstance, ApoInstance, LockedFormats};
use crate::realtime::{RealtimeContext, State};
use crate::ProcessingObject;
#[cfg(windows)]
pub mod class_factory;
#[cfg(windows)]
pub mod exports;
#[cfg(windows)]
pub mod instance_com;
#[derive(Copy, Clone, Debug)]
pub struct AuxiliaryInputBuffer<'a> {
pub id: u32,
pub samples: &'a [f32],
pub flags: BufferFlags,
}
pub trait AecProcessingObject: ProcessingObject {
fn add_aux_input(&mut self, id: u32, format: &Format, init_data: &[u8]) -> Result<(), HResult> {
let _ = (id, format, init_data);
Ok(())
}
fn remove_aux_input(&mut self, id: u32) {
let _ = id;
}
fn is_aux_format_supported(&self, format: &Format) -> FormatNegotiation {
ProcessingObject::is_input_format_supported(self, format)
}
fn accept_aux_input(&mut self, rt: &RealtimeContext, input: AuxiliaryInputBuffer<'_>) {
let _ = (rt, input);
}
}
pub trait AnyAecApoInstance: AnyApoInstance {
fn as_any_apo_instance(&self) -> &dyn AnyApoInstance;
fn add_aux_input(&self, id: u32, format: &Format, init_data: &[u8]) -> Result<(), HResult>;
fn remove_aux_input(&self, id: u32);
fn is_aux_format_supported(&self, format: &Format) -> FormatNegotiation;
fn accept_aux_input(&self, rt: &RealtimeContext, input: AuxiliaryInputBuffer<'_>);
}
pub struct AecApoInstance<T: AecProcessingObject> {
inner: ApoInstance<T>,
}
unsafe impl<T: AecProcessingObject> Sync for AecApoInstance<T> {}
impl<T: AecProcessingObject> AecApoInstance<T> {
#[must_use]
pub fn new() -> Self {
Self {
inner: ApoInstance::<T>::new(),
}
}
#[inline]
#[must_use]
pub fn inner(&self) -> &ApoInstance<T> {
&self.inner
}
}
impl<T: AecProcessingObject> Default for AecApoInstance<T> {
fn default() -> Self {
Self::new()
}
}
impl<T: AecProcessingObject> AnyApoInstance for AecApoInstance<T> {
#[inline]
fn add_ref(&self) -> u32 {
self.inner.add_ref()
}
#[inline]
fn release(&self) -> u32 {
self.inner.release()
}
#[inline]
fn refcount(&self) -> u32 {
self.inner.refcount()
}
#[inline]
fn state(&self) -> State {
self.inner.state()
}
#[inline]
fn initialize(&self) -> Result<(), HResult> {
self.inner.initialize()
}
#[inline]
fn is_input_format_supported(&self, format: &Format) -> FormatNegotiation {
self.inner.is_input_format_supported(format)
}
#[inline]
fn is_output_format_supported(&self, format: &Format) -> FormatNegotiation {
self.inner.is_output_format_supported(format)
}
#[inline]
fn lock_for_process(&self, input: &Format, output: &Format) -> Result<(), HResult> {
self.inner.lock_for_process(input, output)
}
#[inline]
fn unlock_for_process(&self) -> Result<(), HResult> {
self.inner.unlock_for_process()
}
#[inline]
fn process(
&self,
rt: &RealtimeContext,
input: ProcessInput<'_>,
output: &mut [f32],
) -> Result<BufferFlags, HResult> {
self.inner.process(rt, input, output)
}
#[inline]
fn locked_formats(&self) -> Option<LockedFormats> {
self.inner.locked_formats()
}
#[inline]
fn clsid(&self) -> crate::Clsid {
T::CLSID
}
#[inline]
fn name(&self) -> &'static str {
T::NAME
}
#[inline]
fn copyright(&self) -> &'static str {
T::COPYRIGHT
}
#[inline]
fn category(&self) -> crate::ApoCategory {
T::CATEGORY
}
#[inline]
fn system_effects(&self) -> Vec<crate::SystemEffect> {
self.inner.system_effects()
}
#[inline]
fn set_system_effect_state(&self, id: &crate::Clsid, state: crate::SystemEffectState) {
self.inner.set_system_effect_state(id, state);
}
}
impl<T: AecProcessingObject> AnyAecApoInstance for AecApoInstance<T> {
fn as_any_apo_instance(&self) -> &dyn AnyApoInstance {
self
}
fn add_aux_input(&self, id: u32, format: &Format, init_data: &[u8]) -> Result<(), HResult> {
let inner = unsafe { &mut *self.inner.inner_cell().get() };
inner.add_aux_input(id, format, init_data)
}
fn remove_aux_input(&self, id: u32) {
let inner = unsafe { &mut *self.inner.inner_cell().get() };
inner.remove_aux_input(id);
}
fn is_aux_format_supported(&self, format: &Format) -> FormatNegotiation {
let inner = unsafe { &*self.inner.inner_cell().get() };
inner.is_aux_format_supported(format)
}
fn accept_aux_input(&self, rt: &RealtimeContext, input: AuxiliaryInputBuffer<'_>) {
let inner = unsafe { &mut *self.inner.inner_cell().get() };
inner.accept_aux_input(rt, input);
}
}
#[doc(hidden)]
#[must_use]
pub fn make_aec_instance<T: AecProcessingObject + 'static>() -> Arc<dyn AnyAecApoInstance> {
Arc::new(AecApoInstance::<T>::new())
}
#[cfg(test)]
mod tests {
use super::*;
use crate::apo::ApoCategory;
use crate::clsid::Clsid;
use core::cell::Cell;
struct AuxTrace {
added: Cell<Option<u32>>,
removed: Cell<Option<u32>>,
accepts: Cell<u32>,
custom_aux_negotiation: Cell<bool>,
}
impl ProcessingObject for AuxTrace {
const CLSID: Clsid = Clsid::from_u128(0x00112233_4455_6677_8899_AABBCCDDEEFF);
const NAME: &'static str = "aec aux-trace";
const COPYRIGHT: &'static str = "test";
const CATEGORY: ApoCategory = ApoCategory::Mfx;
fn new() -> Self {
Self {
added: Cell::new(None),
removed: Cell::new(None),
accepts: Cell::new(0),
custom_aux_negotiation: Cell::new(false),
}
}
fn process(
&mut self,
_rt: &RealtimeContext,
input: ProcessInput<'_>,
output: &mut [f32],
) -> BufferFlags {
output.copy_from_slice(input.samples());
input.flags()
}
}
impl AecProcessingObject for AuxTrace {
fn add_aux_input(
&mut self,
id: u32,
_format: &Format,
_init_data: &[u8],
) -> Result<(), HResult> {
self.added.set(Some(id));
Ok(())
}
fn remove_aux_input(&mut self, id: u32) {
self.removed.set(Some(id));
}
fn is_aux_format_supported(&self, format: &Format) -> FormatNegotiation {
if self.custom_aux_negotiation.get() {
FormatNegotiation::Reject
} else {
ProcessingObject::is_input_format_supported(self, format)
}
}
fn accept_aux_input(&mut self, _rt: &RealtimeContext, _input: AuxiliaryInputBuffer<'_>) {
self.accepts.set(self.accepts.get() + 1);
}
}
fn rt() -> RealtimeContext {
unsafe { RealtimeContext::new_unchecked() }
}
#[test]
fn aec_instance_delegates_aux_lifecycle_calls() {
let typed = AecApoInstance::<AuxTrace>::new();
let f = Format::pcm_float32(48_000, 1);
let rt = rt();
let samples = [0.0_f32; 4];
AnyAecApoInstance::add_aux_input(&typed, 9, &f, &[1, 2, 3]).unwrap();
AnyAecApoInstance::accept_aux_input(
&typed,
&rt,
AuxiliaryInputBuffer {
id: 9,
samples: &samples,
flags: BufferFlags::VALID,
},
);
AnyAecApoInstance::accept_aux_input(
&typed,
&rt,
AuxiliaryInputBuffer {
id: 9,
samples: &samples,
flags: BufferFlags::VALID,
},
);
AnyAecApoInstance::remove_aux_input(&typed, 9);
let trace: &AuxTrace = unsafe { &*typed.inner.inner_cell().get() };
assert_eq!(trace.added.get(), Some(9));
assert_eq!(trace.removed.get(), Some(9));
assert_eq!(trace.accepts.get(), 2);
}
#[test]
fn make_aec_instance_yields_dyn_any_aec_apo_instance() {
let inst: Arc<dyn AnyAecApoInstance> = make_aec_instance::<AuxTrace>();
assert_eq!(AnyApoInstance::refcount(inst.as_ref()), 0);
assert_eq!(AnyApoInstance::state(inst.as_ref()), State::Uninitialized);
}
#[test]
fn aux_format_supported_defaults_to_input_format_supported() {
let typed = AecApoInstance::<AuxTrace>::new();
let f = Format::pcm_float32(48_000, 1);
let result = AnyAecApoInstance::is_aux_format_supported(&typed, &f);
assert_eq!(result, FormatNegotiation::Accept);
}
#[test]
fn aux_format_supported_uses_override_when_provided() {
let typed = AecApoInstance::<AuxTrace>::new();
let trace: &AuxTrace = unsafe { &*typed.inner.inner_cell().get() };
trace.custom_aux_negotiation.set(true);
let f = Format::pcm_float32(48_000, 1);
let result = AnyAecApoInstance::is_aux_format_supported(&typed, &f);
assert_eq!(result, FormatNegotiation::Reject);
}
#[test]
fn aec_instance_state_machine_inherits_from_apo_instance() {
let typed = AecApoInstance::<AuxTrace>::new();
assert_eq!(typed.state(), State::Uninitialized);
assert_eq!(typed.refcount(), 0);
typed.initialize().unwrap();
assert_eq!(typed.state(), State::Initialized);
}
}