#[cfg(target_os="windows")]
mod device {
use windows::{Win32::{Devices::FunctionDiscovery::PKEY_Device_FriendlyName, Media::Audio::{Endpoints::IAudioEndpointVolume, IMMDevice}, System::Com::{CLSCTX_ALL, STGM_READ}}, core::PWSTR,};
use std::ptr;
use crate::{debug_eprintln, device::DeviceTrait, error::Error, wasapi::{self, IMMWrapper}};
pub struct WASAPIDevice {
device: IMMDevice
}
impl DeviceTrait for WASAPIDevice {
fn from_name(name: String) -> Result<Self, Error> {
let devices = wasapi::get_device_identifiers()?;
let mut uid = None;
for (u_id, names) in devices {
if name == names {
uid = Some(u_id);
break
}
}
if let Some(uid) = uid {
let mut id = format!("{}\0", uid).encode_utf16().collect::<Vec<u16>>();
let pwstr = PWSTR(id.as_mut_ptr());
let device;
unsafe {
let enumerator = wasapi::get_enumerator();
device = enumerator.GetDevice(pwstr).map_err(|e| Error::DeviceAccessFailed(format!("Failed to capture IMMDevice {e}")))?;
}
Ok(Self {
device,
})
} else {
Err(Error::DeviceNotFound)
}
}
fn from_uid(uid: String) -> Result<Self, Error> {
let devices = wasapi::get_device_identifiers()?;
let mut matched = false;
for (u_id, _name) in devices {
if uid == u_id {
matched = true;
break
}
}
let mut id = format!("{}\0", uid).encode_utf16().collect::<Vec<u16>>();
let pwstr = PWSTR(id.as_mut_ptr());
if matched {
let device;
unsafe {
let enumerator = wasapi::get_enumerator();
device = enumerator.GetDevice(pwstr).map_err(|e| Error::DeviceAccessFailed(format!("Failed to capture IMMDevice {e}")))?;
}
Ok(Self {
device
})
} else {
Err(Error::DeviceNotFound)
}
}
fn get_name(&self) -> Result<String, Error> {
let result = unsafe {self.device.OpenPropertyStore(STGM_READ)};
match result {
Ok(properties) => {
return Ok(unsafe {properties.GetValue(&PKEY_Device_FriendlyName).map_err(|e| Error::DeviceAccessFailed(format!("Failed to access property store values {e}")))}?.to_string());
},
Err(error) => {
return Err(Error::DeviceAccessFailed(format!("Failed to access property store {error}")))
}
}
}
fn get_vol(&self) -> Result<f32, Error> {
let mut vol: f32 = 0.0;
unsafe {
let volume_controls = self.device.Activate::<IAudioEndpointVolume>(CLSCTX_ALL, None).unwrap();
if volume_controls.GetMute().unwrap().into() {
vol = 0.0;
} else {
let channel_count = volume_controls.GetChannelCount().unwrap();
let mut total_volumes = 0.0;
for channel in 0..channel_count {
total_volumes += volume_controls.GetChannelVolumeLevelScalar(channel).unwrap();
}
vol = (total_volumes / channel_count as f32);
}
}
Ok(vol)
}
fn set_vol(&self, value: f32) -> Result<(), Error> {
let mut success = None;
unsafe {
let volume_controls = self.device.Activate::<IAudioEndpointVolume>(CLSCTX_ALL, None).unwrap();
if volume_controls.GetMute().unwrap().into() {
volume_controls.SetMute(false, ptr::null()).unwrap();
}
let channel_count = volume_controls.GetChannelCount().unwrap();
for channel in 0..channel_count {
volume_controls.SetChannelVolumeLevelScalar(channel, value, ptr::null()).unwrap();
}
if value == 0.0 {
volume_controls.SetMute(true, ptr::null()).unwrap();
}
}
success.replace(true);
match success {
Some(_) => {
Ok(())
},
None => {
Err(Error::Placeholder)
}
}
}
fn get_mute(&self) -> Result<bool, Error> {
let mut mute = 0;
unsafe {
let volume_controls = self.device.Activate::<IAudioEndpointVolume>(CLSCTX_ALL, None).unwrap();
if volume_controls.GetMute().unwrap().into() {
mute = 1;
}
}
match mute {
1 => {
Ok(true)
}
_ => {
Ok(false)
}
}
}
fn set_mute(&self, mute: bool) -> Result<(), Error> {
let mut status = false;
unsafe {
let volume_controls = self.device.Activate::<IAudioEndpointVolume>(CLSCTX_ALL, None).unwrap();
match volume_controls.SetMute(mute, ptr::null()) {
Ok(_) => {
status = true;
}
Err(e) => {
return Err(Error::MuteSetFailed(format!("Error setting mute status {}", e)));
}
}
}
match status {
true => {
Ok(())
},
false => {
Err(Error::Placeholder)
}
}
}
}
impl WASAPIDevice {
pub fn from_imm_device(wrapper: IMMWrapper) -> Result<Self, Error> {
let uid;
uid = wasapi::get_imm_device_uid(&wrapper)?;
let devices = wasapi::get_device_identifiers()?;
let mut matched = false;
for (u_id, _name) in devices {
if uid == u_id {
matched = true;
break
}
}
if matched {
Ok(Self {
device: wrapper.device,
})
} else {
Err(Error::DeviceNotFound)
}
}
pub fn get_device_uid(&self) -> Result<String, Error> {
unsafe {
Ok(self.device.GetId().map_err(|e| Error::DeviceAccessFailed(format!("Failed to get Device Id {e}")))?
.to_string().map_err(|e| Error::DeviceAccessFailed(format!("PWSTR conversion failed {e}")))?)
}
}
}
}
#[cfg(not(target_os="windows"))]
mod device {
use crate::{device::DeviceTrait, error::Error, wasapi::IMMWrapper};
pub struct WASAPIDevice {}
impl DeviceTrait for WASAPIDevice {}
impl WASAPIDevice {
pub fn get_device_uid(&self) -> Result<String, Error> {
Err(Error::PlatformUnsupported)
}
pub fn from_imm_device(wrapper: IMMWrapper) -> Result<Self, Error> {
Err(Error::PlatformUnsupported)
}
}
}
pub(crate) use device::*;