use std::{ error::Error, sync::{Arc, Mutex}, }; use crate::{ device_notification_client::DeviceNotificationClient, session_notification::SessionNotification, }; use windows::{ core::{Interface, PCWSTR}, Win32::{ Devices::FunctionDiscovery::PKEY_Device_FriendlyName, Media::Audio::{ eRender, IAudioSessionControl2, IAudioSessionManager2, IAudioSessionNotification, IMMDeviceEnumerator, IMMNotificationClient, MMDeviceEnumerator, DEVICE_STATE_ACTIVE, }, System::Com::{CoCreateInstance, CLSCTX_ALL, STGM_READ}, }, }; pub(crate) struct SMSessionNotifier { device_enumerator: IMMDeviceEnumerator, device_notification_client: IMMNotificationClient, session_notification: IAudioSessionNotification, session_managers: Vec, notification_function: Box, } impl SMSessionNotifier { pub(crate) fn new( callback: Box, ) -> Arc>> { let session_notifier_arc: Arc>> = Arc::new(Mutex::new(None)); let sn2 = session_notifier_arc.clone(); let sn3 = session_notifier_arc.clone(); let notifier = SMSessionNotifier { session_managers: Vec::new(), device_enumerator: unsafe { CoCreateInstance(&MMDeviceEnumerator, None, CLSCTX_ALL).unwrap() }, device_notification_client: IMMNotificationClient::from(DeviceNotificationClient { callback: Box::new(move |id| { match sn2.lock().unwrap().as_mut().unwrap().add_device_by_id(id) { Ok(_) => {}, Err(e) => { println!("Failed to add new device: {:?}", e); }, } }), }), session_notification: IAudioSessionNotification::from(SessionNotification { callback: Box::new(move |session| { (sn3.lock().unwrap().as_mut().unwrap().notification_function)(session) }), }), notification_function: callback, }; *session_notifier_arc.lock().unwrap() = Some(notifier); return session_notifier_arc.clone(); } pub(crate) fn boot_devices(self: &mut SMSessionNotifier) -> Result<(), Box> { unsafe { self.device_enumerator .RegisterEndpointNotificationCallback(&self.device_notification_client)?; let device_collection = self .device_enumerator .EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE)?; for device in (0..device_collection.GetCount()?).map(|x| device_collection.Item(x)) { let mmdevice = device?; self.add_device(mmdevice)?; } println!("All devices initialized."); return Ok(()); } } fn add_device_by_id(self: &mut SMSessionNotifier, id: &PCWSTR) -> Result<(), Box> { unsafe { let device_enumerator: IMMDeviceEnumerator = CoCreateInstance(&MMDeviceEnumerator, None, CLSCTX_ALL)?; let device = device_enumerator.GetDevice(id)?; self.add_device(device)?; Ok(()) } } unsafe fn add_device( self: &mut SMSessionNotifier, device: windows::Win32::Media::Audio::IMMDevice, ) -> Result<(), Box> { let sm: IAudioSessionManager2 = device.Activate(CLSCTX_ALL, None)?; sm.RegisterSessionNotification(&self.session_notification)?; let session_enumerator = sm.GetSessionEnumerator()?; let session_count = session_enumerator.GetCount()?; let device_sessions = (0..session_count) .map(|idx| { session_enumerator .GetSession(idx) .and_then(|session| session.cast()) }) .collect::, _>>()?; for session in device_sessions { (self.notification_function)(session); } let prop_store = device.OpenPropertyStore(STGM_READ)?; let prop_var = prop_store.GetValue(&PKEY_Device_FriendlyName)?; println!("Device Added: {} Existing Sessions: {}", prop_var.Anonymous.Anonymous.Anonymous.pwszVal.to_string()?, session_count); self.session_managers.push(sm); Ok(()) } }