#![allow(missing_docs)]
extern crate alloc;
use alloc::sync::Arc;
use windows::Win32::Foundation::HANDLE;
use windows::Win32::Media::Audio::Apo::{
IApoAcousticEchoCancellation, IApoAcousticEchoCancellation_Impl,
IApoAuxiliaryInputConfiguration, IApoAuxiliaryInputConfiguration_Impl, IApoAuxiliaryInputRT,
IApoAuxiliaryInputRT_Impl, IAudioMediaType, IAudioProcessingObject,
IAudioProcessingObjectConfiguration, IAudioProcessingObjectConfiguration_Impl,
IAudioProcessingObjectRT, IAudioProcessingObjectRT_Impl, IAudioProcessingObject_Impl,
IAudioSystemEffects, IAudioSystemEffects2, IAudioSystemEffects2_Impl, IAudioSystemEffects3,
IAudioSystemEffects3_Impl, IAudioSystemEffects_Impl, APO_CONNECTION_DESCRIPTOR,
APO_CONNECTION_PROPERTY, APO_REG_PROPERTIES, AUDIO_SYSTEMEFFECT, AUDIO_SYSTEMEFFECT_STATE,
};
use windows_core::{implement, Ref, GUID};
use crate::aec::AnyAecApoInstance;
#[implement(
IAudioProcessingObject,
IAudioProcessingObjectConfiguration,
IAudioProcessingObjectRT,
IAudioSystemEffects,
IAudioSystemEffects2,
IAudioSystemEffects3,
IApoAcousticEchoCancellation,
IApoAuxiliaryInputConfiguration,
IApoAuxiliaryInputRT
)]
pub struct AecApoInstanceCom {
instance: Arc<dyn AnyAecApoInstance>,
}
impl AecApoInstanceCom {
#[must_use]
pub fn new(instance: Arc<dyn AnyAecApoInstance>) -> Self {
crate::raw::exports::outstanding_inc();
Self { instance }
}
#[must_use]
pub fn instance(&self) -> &Arc<dyn AnyAecApoInstance> {
&self.instance
}
}
impl Drop for AecApoInstanceCom {
fn drop(&mut self) {
crate::raw::exports::outstanding_dec();
}
}
impl IAudioProcessingObject_Impl for AecApoInstanceCom_Impl {
fn Reset(&self) -> windows_core::Result<()> {
Ok(())
}
fn GetLatency(&self) -> windows_core::Result<i64> {
Ok(0)
}
fn GetRegistrationProperties(&self) -> windows_core::Result<*mut APO_REG_PROPERTIES> {
crate::aec::exports::build_aec_registration_properties(self.instance.as_any_apo_instance())
}
fn Initialize(&self, _cbdatasize: u32, _pbydata: *const u8) -> windows_core::Result<()> {
crate::raw::dispatch::initialize(self.instance.as_any_apo_instance())
}
fn IsInputFormatSupported(
&self,
_poppositeformat: Ref<IAudioMediaType>,
prequestedinputformat: Ref<IAudioMediaType>,
) -> windows_core::Result<IAudioMediaType> {
crate::raw::dispatch::negotiate_format(
self.instance.as_any_apo_instance(),
prequestedinputformat,
crate::raw::media_type::NegotiationDirection::Input,
)
}
fn IsOutputFormatSupported(
&self,
_poppositeformat: Ref<IAudioMediaType>,
prequestedoutputformat: Ref<IAudioMediaType>,
) -> windows_core::Result<IAudioMediaType> {
crate::raw::dispatch::negotiate_format(
self.instance.as_any_apo_instance(),
prequestedoutputformat,
crate::raw::media_type::NegotiationDirection::Output,
)
}
fn GetInputChannelCount(&self) -> windows_core::Result<u32> {
crate::raw::dispatch::get_input_channel_count(self.instance.as_any_apo_instance())
}
}
impl IAudioProcessingObjectConfiguration_Impl for AecApoInstanceCom_Impl {
#[allow(clippy::not_unsafe_ptr_arg_deref)]
fn LockForProcess(
&self,
u32numinputconnections: u32,
ppinputconnections: *const *const APO_CONNECTION_DESCRIPTOR,
u32numoutputconnections: u32,
ppoutputconnections: *const *const APO_CONNECTION_DESCRIPTOR,
) -> windows_core::Result<()> {
unsafe {
crate::raw::dispatch::lock_for_process(
self.instance.as_any_apo_instance(),
u32numinputconnections,
ppinputconnections,
u32numoutputconnections,
ppoutputconnections,
)
}
}
fn UnlockForProcess(&self) -> windows_core::Result<()> {
crate::raw::dispatch::unlock_for_process(self.instance.as_any_apo_instance())
}
}
impl IAudioProcessingObjectRT_Impl for AecApoInstanceCom_Impl {
#[allow(clippy::not_unsafe_ptr_arg_deref)]
fn APOProcess(
&self,
u32numinputconnections: u32,
ppinputconnections: *const *const APO_CONNECTION_PROPERTY,
u32numoutputconnections: u32,
ppoutputconnections: *mut *mut APO_CONNECTION_PROPERTY,
) {
unsafe {
crate::raw::dispatch::apo_process(
self.instance.as_any_apo_instance(),
u32numinputconnections,
ppinputconnections,
u32numoutputconnections,
ppoutputconnections,
);
}
}
fn CalcInputFrames(&self, u32outputframecount: u32) -> u32 {
u32outputframecount
}
fn CalcOutputFrames(&self, u32inputframecount: u32) -> u32 {
u32inputframecount
}
}
impl IAudioSystemEffects_Impl for AecApoInstanceCom_Impl {}
impl IAudioSystemEffects2_Impl for AecApoInstanceCom_Impl {
#[allow(clippy::not_unsafe_ptr_arg_deref)]
fn GetEffectsList(
&self,
ppeffectsids: *mut *mut GUID,
pceffects: *mut u32,
event: HANDLE,
) -> windows_core::Result<()> {
crate::raw::dispatch::get_effects_list(
self.instance.as_any_apo_instance(),
ppeffectsids,
pceffects,
event,
)
}
}
impl IAudioSystemEffects3_Impl for AecApoInstanceCom_Impl {
#[allow(clippy::not_unsafe_ptr_arg_deref)]
fn GetControllableSystemEffectsList(
&self,
effects: *mut *mut AUDIO_SYSTEMEFFECT,
numeffects: *mut u32,
event: HANDLE,
) -> windows_core::Result<()> {
crate::raw::dispatch::get_controllable_system_effects_list(
self.instance.as_any_apo_instance(),
effects,
numeffects,
event,
)
}
fn SetAudioSystemEffectState(
&self,
effectid: &GUID,
state: AUDIO_SYSTEMEFFECT_STATE,
) -> windows_core::Result<()> {
crate::raw::dispatch::set_audio_system_effect_state(
self.instance.as_any_apo_instance(),
effectid,
state,
)
}
}
impl IApoAcousticEchoCancellation_Impl for AecApoInstanceCom_Impl {}
impl IApoAuxiliaryInputConfiguration_Impl for AecApoInstanceCom_Impl {
#[allow(clippy::not_unsafe_ptr_arg_deref)]
fn AddAuxiliaryInput(
&self,
dwinputid: u32,
cbdatasize: u32,
pbydata: *const u8,
pinputconnection: *const APO_CONNECTION_DESCRIPTOR,
) -> windows_core::Result<()> {
unsafe {
crate::raw::dispatch::aec_add_auxiliary_input(
self.instance.as_ref(),
dwinputid,
cbdatasize,
pbydata,
pinputconnection,
)
}
}
fn RemoveAuxiliaryInput(&self, dwinputid: u32) -> windows_core::Result<()> {
self.instance.remove_aux_input(dwinputid);
Ok(())
}
fn IsInputFormatSupported(
&self,
prequestedinputformat: Ref<IAudioMediaType>,
) -> windows_core::Result<IAudioMediaType> {
crate::raw::dispatch::aec_is_aux_format_supported(
self.instance.as_ref(),
prequestedinputformat,
)
}
}
impl IApoAuxiliaryInputRT_Impl for AecApoInstanceCom_Impl {
#[allow(clippy::not_unsafe_ptr_arg_deref)]
fn AcceptInput(&self, dwinputid: u32, pinputconnection: *const APO_CONNECTION_PROPERTY) {
unsafe {
crate::raw::dispatch::aec_accept_input(
self.instance.as_ref(),
dwinputid,
pinputconnection,
);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::aec::{AecApoInstance, AecProcessingObject};
use crate::apo::{ApoCategory, ProcessInput, ProcessingObject};
use crate::buffer::{BufferFlags, CONNECTION_PROPERTY_SIGNATURE};
use crate::clsid::Clsid;
use crate::error::HResult;
use crate::format::Format;
use crate::realtime::RealtimeContext;
use core::cell::Cell;
use windows_core::ComObject;
struct AecDummy {
aux_added: Cell<Option<u32>>,
}
impl ProcessingObject for AecDummy {
const CLSID: Clsid = Clsid::from_u128(0xDDCCBBAA_FFEE_4433_2211_77665544DDEE);
const NAME: &'static str = "aec dummy";
const COPYRIGHT: &'static str = "test";
const CATEGORY: ApoCategory = ApoCategory::Mfx;
fn new() -> Self {
Self {
aux_added: Cell::new(None),
}
}
fn process(
&mut self,
_rt: &RealtimeContext,
input: ProcessInput<'_>,
output: &mut [f32],
) -> BufferFlags {
output.copy_from_slice(input.samples());
input.flags()
}
}
impl AecProcessingObject for AecDummy {
fn add_aux_input(
&mut self,
id: u32,
_format: &Format,
_init_data: &[u8],
) -> Result<(), HResult> {
self.aux_added.set(Some(id));
Ok(())
}
}
fn new_aec_com() -> AecApoInstanceCom {
AecApoInstanceCom::new(Arc::new(AecApoInstance::<AecDummy>::new()))
}
#[test]
fn new_aec_com_starts_with_zero_refcount() {
let com = new_aec_com();
assert_eq!(com.instance().refcount(), 0);
assert_eq!(
com.instance().state(),
crate::realtime::State::Uninitialized
);
}
#[test]
fn iapoacoustic_echo_cancellation_resolves_via_cast() {
use windows::Win32::Media::Audio::Apo::IApoAcousticEchoCancellation;
use windows_core::Interface;
let apo: IApoAcousticEchoCancellation = ComObject::new(new_aec_com()).into_interface();
let _unk: windows_core::IUnknown = apo.cast().unwrap();
}
#[test]
fn add_remove_auxiliary_input_dispatches_to_user() {
use crate::format::Format;
use crate::raw::media_type::media_type_from_format;
use core::mem::ManuallyDrop;
use windows::Win32::Media::Audio::Apo::{
IApoAuxiliaryInputConfiguration, APO_CONNECTION_BUFFER_TYPE_ALLOCATED,
};
let cfg: IApoAuxiliaryInputConfiguration = ComObject::new(new_aec_com()).into_interface();
let format = Format::pcm_float32(48_000, 1);
let media = media_type_from_format(&format);
let desc = APO_CONNECTION_DESCRIPTOR {
Type: APO_CONNECTION_BUFFER_TYPE_ALLOCATED,
pBuffer: 0,
u32MaxFrameCount: 256,
pFormat: ManuallyDrop::new(Some(media)),
u32Signature: CONNECTION_PROPERTY_SIGNATURE,
};
unsafe { cfg.AddAuxiliaryInput(42, &[1, 2, 3], &desc) }.expect("AddAuxiliaryInput failed");
unsafe { cfg.RemoveAuxiliaryInput(42) }.expect("RemoveAuxiliaryInput failed");
}
}