use std::ptr;
use windows::{
core::Interface,
Win32::{
Foundation::{CloseHandle, MAX_PATH},
Media::Audio::{
eRender, IAudioSessionControl, IAudioSessionControl2, IAudioSessionEnumerator,
IAudioSessionManager2, IMMDeviceCollection, IMMDeviceEnumerator, ISimpleAudioVolume,
MMDeviceEnumerator, DEVICE_STATE_ACTIVE,
},
System::{
Com::{CoCreateInstance, CoInitialize, CoUninitialize, CLSCTX_ALL},
ProcessStatus::GetModuleFileNameExW,
Threading::{OpenProcess, PROCESS_QUERY_LIMITED_INFORMATION},
},
},
};
use windows_result::Error;
pub struct WinMix {
com_initialized: bool,
}
impl WinMix {
pub unsafe fn enumerate(&self) -> Result<Vec<Session>, Error> {
let mut result = Vec::<Session>::new();
let res: IMMDeviceEnumerator = CoCreateInstance(&MMDeviceEnumerator, None, CLSCTX_ALL)?;
let collection: IMMDeviceCollection =
res.EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE)?;
let device_count = collection.GetCount()?;
for device_id in 0..device_count {
let dev = collection.Item(device_id)?;
let manager: IAudioSessionManager2 = dev.Activate(CLSCTX_ALL, None)?;
let enumerator: IAudioSessionEnumerator = manager.GetSessionEnumerator()?;
let session_count = enumerator.GetCount()?;
for session_id in 0..session_count {
let ctrl: IAudioSessionControl = enumerator.GetSession(session_id)?;
let ctrl2: IAudioSessionControl2 = ctrl.cast()?;
let pid = ctrl2.GetProcessId()?;
if pid == 0 {
continue;
}
let proc = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, false, pid)?;
let mut path: [u16; MAX_PATH as usize] = [0; MAX_PATH as usize];
let res = GetModuleFileNameExW(proc, None, &mut path);
CloseHandle(proc)?;
if res == 0 {
}
let vol: ISimpleAudioVolume = ctrl2.cast()?;
let mut path = String::from_utf16_lossy(&path);
path.truncate(path.trim_matches(char::from(0)).len());
result.push(Session {
pid,
path,
vol: SimpleVolume { handle: vol },
});
}
}
Ok(result)
}
}
impl Default for WinMix {
fn default() -> WinMix {
unsafe {
let hres = CoInitialize(None);
WinMix {
com_initialized: hres.is_ok(),
}
}
}
}
impl Drop for WinMix {
fn drop(&mut self) {
unsafe {
if self.com_initialized {
CoUninitialize();
}
}
}
}
pub struct Session {
pub pid: u32,
pub path: String,
pub vol: SimpleVolume,
}
pub struct SimpleVolume {
handle: ISimpleAudioVolume,
}
impl SimpleVolume {
pub unsafe fn get_master_volume(&self) -> Result<f32, Error> {
self.handle.GetMasterVolume()
}
pub unsafe fn set_master_volume(&self, level: f32) -> Result<(), Error> {
self.handle.SetMasterVolume(level, ptr::null())
}
pub unsafe fn get_mute(&self) -> Result<bool, Error> {
match self.handle.GetMute() {
Ok(val) => Ok(val.as_bool()),
Err(e) => Err(e),
}
}
pub unsafe fn set_mute(&self, val: bool) -> Result<(), Error> {
self.handle.SetMute(val, ptr::null())
}
}