pub mod device;
#[cfg(target_os="windows")]
pub mod wasapi {
use windows::core::PWSTR;
use windows::Win32::System::Com::CLSCTX_ALL;
use windows::Win32::Media::Audio::Endpoints::IAudioEndpointVolume;
use windows::Win32::Media::Audio::{DEVICE_STATE_ACTIVE, IMMDevice, eMultimedia, eRender};
use windows::Win32::Devices::FunctionDiscovery::PKEY_Device_FriendlyName;
use windows::Win32::System::Com::STGM_READ;
use crate::VolumeControl;
use crate::device::DeviceTrait;
use crate::wasapi::device::WASAPIDevice;
use crate::{debug_eprintln, error::Error};
pub fn get_device_identifiers() -> Result<Vec<(String, String)>, Error> {
let mut devices: Vec<(String, String)> = Vec::new();
unsafe {
let enumerator = get_enumerator();
let device_col = enumerator.EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE).unwrap();
let dev_count = device_col.GetCount().unwrap();
for device_id in 0..dev_count{
let device = device_col.Item(device_id).unwrap();
let result = device.OpenPropertyStore(STGM_READ);
match result {
Ok(properties) => {
let name = properties.GetValue(&PKEY_Device_FriendlyName).unwrap();
let uid = get_imm_device_uid(&IMMWrapper { device })?;
devices.push((uid, name.to_string()));
},
Err(error) => {
return Err(Error::DeviceEnumerationFailed(format!("Failed to get audio devices {error}")))
}
}
}
}
Ok(devices)
}
pub(super) unsafe fn get_enumerator() -> windows::Win32::Media::Audio::IMMDeviceEnumerator {
use windows::core::{Error};
use windows::Win32::Media::Audio::IMMDeviceEnumerator;
use windows::Win32::Media::Audio::{MMDeviceEnumerator};
use windows::Win32::System::Com::{CoCreateInstance, CoInitializeEx, CLSCTX_ALL, COINIT_MULTITHREADED};
unsafe {
let _ = CoInitializeEx(None, COINIT_MULTITHREADED).unwrap();
let hresult: Result<IMMDeviceEnumerator, Error> = CoCreateInstance(&MMDeviceEnumerator, None, CLSCTX_ALL);
match hresult {
Ok(devices) => {
devices
},
Err(error) => {
panic!("{}", error);
}
}
}
}
pub fn get_default_output_device() -> Result<WASAPIDevice, Error> {
let default_device;
unsafe {
let enumerator = get_enumerator();
default_device = enumerator.GetDefaultAudioEndpoint(eRender, eMultimedia).unwrap();
}
Ok(WASAPIDevice::from_imm_device(IMMWrapper {device: default_device})?)
}
pub fn get_imm_device_uid(wrapper: &IMMWrapper) -> Result<String, Error> {
unsafe {
Ok(wrapper.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}")))?)
}
}
pub fn get_sound_devices() -> Result<Vec<String>, Error> {
Ok(get_device_identifiers()?.into_iter().map(|(_pwstr, name)| name).collect())
}
pub fn get_vol() -> Result<f32, Error> {
let default_device = get_default_output_device()?;
Ok(default_device.get_vol()?)
}
pub fn set_vol(value: f32) -> Result<(), Error> {
let default_device = get_default_output_device()?;
Ok(default_device.set_vol(value)?)
}
pub fn get_mute() -> Result<bool, Error> {
let default_device = get_default_output_device()?;
Ok(default_device.get_mute()?)
}
pub fn set_mute(state: bool) -> Result<(), Error> {
let default_device = get_default_output_device()?;
Ok(default_device.set_mute(state)?)
}
pub struct IMMWrapper {
#[cfg(target_os="windows")]
pub device: IMMDevice,
}
pub struct EnumeratorWrapper {
pub enumerator: windows::Win32::Media::Audio::IMMDeviceEnumerator
}
}
#[cfg(not(target_os="windows"))]
pub mod wasapi {
use crate::{error::Error, wasapi::device::WASAPIDevice};
pub fn get_device_identifiers() -> Result<Vec<(String, String)>, Error> {
Err(Error::PlatformUnsupported)
}
pub fn get_default_output_device() -> Result<WASAPIDevice, Error> {
Err(Error::PlatformUnsupported)
}
pub fn get_imm_device_uid(wrapper: &IMMWrapper) -> Result<String, Error> {
Err(Error::PlatformUnsupported)
}
pub fn get_sound_devices() -> Result<Vec<String>, Error> {
Err(Error::PlatformUnsupported)
}
pub fn get_vol() -> Result<f32, Error> {
Err(Error::PlatformUnsupported)
}
pub fn set_vol(value: f32) -> Result<(), Error> {
Err(Error::PlatformUnsupported)
}
pub fn get_mute() -> Result<bool, Error> {
Err(Error::PlatformUnsupported)
}
pub fn set_mute(state: bool) -> Result<(), Error> {
Err(Error::PlatformUnsupported)
}
pub struct IMMWrapper {}
}
pub(crate) use wasapi::*;