use crate::support;
use std::sync::{Arc, Condvar, Mutex};
use wasapi::*;
use windows::Win32::Media::Audio::{
ActivateAudioInterfaceAsync, DEVINTERFACE_AUDIO_CAPTURE, DEVINTERFACE_AUDIO_RENDER,
IActivateAudioInterfaceAsyncOperation, IActivateAudioInterfaceCompletionHandler,
IActivateAudioInterfaceCompletionHandler_Impl,
};
use windows::core::{HRESULT, Interface, PCWSTR, Ref, implement};
#[implement(IActivateAudioInterfaceCompletionHandler)]
struct Handler(Arc<(Mutex<bool>, Condvar)>);
impl IActivateAudioInterfaceCompletionHandler_Impl for Handler_Impl {
fn ActivateCompleted(
&self,
_operation: Ref<IActivateAudioInterfaceAsyncOperation>,
) -> windows::core::Result<()> {
let (lock, cvar) = &*self.0;
*lock.lock().unwrap() = true;
cvar.notify_one();
Ok(())
}
}
fn interface_path(id: &str, direction: &Direction) -> String {
let class = match direction {
Direction::Render => DEVINTERFACE_AUDIO_RENDER,
Direction::Capture => DEVINTERFACE_AUDIO_CAPTURE,
};
format!("\\\\?\\SWD#MMDEVAPI#{id}#{{{class:?}}}")
}
fn activate(id: &str, direction: &Direction) -> windows::core::Result<IAudioClient> {
let path = interface_path(id, direction);
println!("activating {path}");
let path: Vec<u16> = path.encode_utf16().chain(std::iter::once(0)).collect();
let setup = Arc::new((Mutex::new(false), Condvar::new()));
let callback: IActivateAudioInterfaceCompletionHandler = Handler(setup.clone()).into();
let operation = unsafe {
ActivateAudioInterfaceAsync(PCWSTR(path.as_ptr()), &IAudioClient::IID, None, &callback)?
};
let (lock, cvar) = &*setup;
let mut completed = lock.lock().unwrap();
while !*completed {
completed = cvar.wait(completed).unwrap();
}
drop(completed);
let mut client = None;
let mut result = HRESULT::default();
unsafe { operation.GetActivateResult(&mut result, &mut client)? };
result.ok()?;
client.unwrap().cast()
}
#[test]
fn activated_client_renders() {
let Some(fixture) = support::fixture() else {
return;
};
let id = fixture.cable.render.get_id().unwrap();
let interface = activate(&id, &Direction::Render).expect("ActivateAudioInterfaceAsync failed");
let mut client = AudioClient::from_iaudioclient(interface, Direction::Render);
let format = client.get_mixformat().unwrap();
let (def_period, _min_period) = client.get_device_period().unwrap();
let mode = support::mode_for(
&client,
ShareMode::Shared,
TimingMode::Events,
def_period,
&format,
)
.unwrap();
client
.initialize_client(&format, &Direction::Render, &mode)
.unwrap();
let run = support::render_for(&client, &format, support::RUN_TIME).unwrap();
support::assert_rate(&run, &format, "activated render");
}
#[test]
fn activated_client_reports_its_state() {
let Some(fixture) = support::fixture() else {
return;
};
let id = fixture.cable.capture.get_id().unwrap();
let interface = activate(&id, &Direction::Capture).expect("ActivateAudioInterfaceAsync failed");
let mut client = AudioClient::from_iaudioclient(interface, Direction::Capture);
assert_eq!(client.get_direction(), Direction::Capture);
assert_eq!(client.get_sharemode(), None);
assert_eq!(client.get_timing_mode(), None);
let format = client.get_mixformat().unwrap();
let (def_period, _min_period) = client.get_device_period().unwrap();
let mode = support::mode_for(
&client,
ShareMode::Shared,
TimingMode::Polling,
def_period,
&format,
)
.unwrap();
client
.initialize_client(&format, &Direction::Capture, &mode)
.unwrap();
assert_eq!(client.get_direction(), Direction::Capture);
assert_eq!(client.get_sharemode(), Some(ShareMode::Shared));
assert_eq!(client.get_timing_mode(), Some(TimingMode::Polling));
}