use num_integer::Integer;
use std::cmp;
use std::collections::VecDeque;
use std::rc::Weak;
use std::{error, fmt, ptr, slice};
use widestring::U16CString;
use windows::Win32::UI::Shell::PropertiesSystem::PROPERTYKEY;
use windows::{
core::PCSTR,
Win32::Devices::FunctionDiscovery::{
PKEY_DeviceInterface_FriendlyName, PKEY_Device_DeviceDesc, PKEY_Device_FriendlyName,
},
Win32::Foundation::{HANDLE, WAIT_OBJECT_0},
Win32::Media::Audio::{
eCapture, eCommunications, eConsole, eMultimedia, eRender, AudioSessionStateActive,
AudioSessionStateExpired, AudioSessionStateInactive, IAudioCaptureClient, IAudioClient,
IAudioClock, IAudioRenderClient, IAudioSessionControl, IAudioSessionEvents, IMMDevice,
IMMDeviceCollection, IMMDeviceEnumerator, MMDeviceEnumerator,
AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY, AUDCLNT_BUFFERFLAGS_SILENT,
AUDCLNT_BUFFERFLAGS_TIMESTAMP_ERROR, AUDCLNT_SHAREMODE_EXCLUSIVE, AUDCLNT_SHAREMODE_SHARED,
AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM, AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
AUDCLNT_STREAMFLAGS_LOOPBACK, AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY, DEVICE_STATE_ACTIVE,
DEVICE_STATE_DISABLED, DEVICE_STATE_NOTPRESENT, DEVICE_STATE_UNPLUGGED, WAVEFORMATEX,
WAVEFORMATEXTENSIBLE,
},
Win32::Media::KernelStreaming::WAVE_FORMAT_EXTENSIBLE,
Win32::System::Com::StructuredStorage::PropVariantToStringAlloc,
Win32::System::Com::STGM_READ,
Win32::System::Com::{
CoCreateInstance, CoInitializeEx, CoUninitialize, CLSCTX_ALL, COINIT_APARTMENTTHREADED,
COINIT_MULTITHREADED,
},
Win32::System::Threading::{CreateEventA, WaitForSingleObject},
};
use crate::{make_channelmasks, AudioSessionEvents, EventCallbacks, WaveFormat};
pub(crate) type WasapiRes<T> = Result<T, Box<dyn error::Error>>;
#[derive(Debug)]
pub struct WasapiError {
desc: String,
}
impl fmt::Display for WasapiError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.desc)
}
}
impl error::Error for WasapiError {
fn description(&self) -> &str {
&self.desc
}
}
impl WasapiError {
pub fn new(desc: &str) -> Self {
WasapiError {
desc: desc.to_owned(),
}
}
}
pub fn initialize_mta() -> Result<(), windows::core::Error> {
unsafe { CoInitializeEx(None, COINIT_MULTITHREADED) }
}
pub fn initialize_sta() -> Result<(), windows::core::Error> {
unsafe { CoInitializeEx(None, COINIT_APARTMENTTHREADED) }
}
pub fn deinitialize() {
unsafe { CoUninitialize() }
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Direction {
Render,
Capture,
}
impl fmt::Display for Direction {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
Direction::Render => write!(f, "Render"),
Direction::Capture => write!(f, "Capture"),
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Role {
Console,
Multimedia,
Communications,
}
impl fmt::Display for Role {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
Role::Console => write!(f, "Console"),
Role::Multimedia => write!(f, "Multimedia"),
Role::Communications => write!(f, "Communications"),
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum ShareMode {
Shared,
Exclusive,
}
impl fmt::Display for ShareMode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
ShareMode::Shared => write!(f, "Shared"),
ShareMode::Exclusive => write!(f, "Exclusive"),
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum SampleType {
Float,
Int,
}
impl fmt::Display for SampleType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
SampleType::Float => write!(f, "Float"),
SampleType::Int => write!(f, "Int"),
}
}
}
#[derive(Debug, Eq, PartialEq)]
pub enum SessionState {
Active,
Inactive,
Expired,
}
impl fmt::Display for SessionState {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
SessionState::Active => write!(f, "Active"),
SessionState::Inactive => write!(f, "Inactive"),
SessionState::Expired => write!(f, "Expired"),
}
}
}
#[derive(Debug, Eq, PartialEq)]
pub enum DeviceState {
Active,
Disabled,
NotPresent,
Unplugged,
}
impl fmt::Display for DeviceState {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
DeviceState::Active => write!(f, "Active"),
DeviceState::Disabled => write!(f, "Disabled"),
DeviceState::NotPresent => write!(f, "NotPresent"),
DeviceState::Unplugged => write!(f, "Unplugged"),
}
}
}
pub fn get_default_device(direction: &Direction) -> WasapiRes<Device> {
get_default_device_for_role(direction, &Role::Console)
}
pub fn get_default_device_for_role(direction: &Direction, role: &Role) -> WasapiRes<Device> {
let dir = match direction {
Direction::Capture => eCapture,
Direction::Render => eRender,
};
let e_role = match role {
Role::Console => eConsole,
Role::Multimedia => eMultimedia,
Role::Communications => eCommunications,
};
let enumerator: IMMDeviceEnumerator =
unsafe { CoCreateInstance(&MMDeviceEnumerator, None, CLSCTX_ALL)? };
let device = unsafe { enumerator.GetDefaultAudioEndpoint(dir, e_role)? };
let dev = Device {
device,
direction: *direction,
};
debug!("default device {:?}", dev.get_friendlyname());
Ok(dev)
}
pub fn calculate_period_100ns(frames: i64, samplerate: i64) -> i64 {
((10000.0 * 1000.0 / samplerate as f64 * frames as f64) + 0.5) as i64
}
pub struct DeviceCollection {
collection: IMMDeviceCollection,
direction: Direction,
}
impl DeviceCollection {
pub fn new(direction: &Direction) -> WasapiRes<DeviceCollection> {
let dir = match direction {
Direction::Capture => eCapture,
Direction::Render => eRender,
};
let enumerator: IMMDeviceEnumerator =
unsafe { CoCreateInstance(&MMDeviceEnumerator, None, CLSCTX_ALL)? };
let devs = unsafe { enumerator.EnumAudioEndpoints(dir, DEVICE_STATE_ACTIVE)? };
Ok(DeviceCollection {
collection: devs,
direction: *direction,
})
}
pub fn get_nbr_devices(&self) -> WasapiRes<u32> {
let count = unsafe { self.collection.GetCount()? };
Ok(count)
}
pub fn get_device_at_index(&self, idx: u32) -> WasapiRes<Device> {
let device = unsafe { self.collection.Item(idx)? };
Ok(Device {
device,
direction: self.direction,
})
}
pub fn get_device_with_name(&self, name: &str) -> WasapiRes<Device> {
let count = unsafe { self.collection.GetCount()? };
trace!("nbr devices {}", count);
for n in 0..count {
let device = self.get_device_at_index(n)?;
let devname = device.get_friendlyname()?;
if name == devname {
return Ok(device);
}
}
Err(WasapiError::new(format!("Unable to find device {}", name).as_str()).into())
}
pub fn get_direction(&self) -> Direction {
self.direction
}
}
pub struct DeviceCollectionIter<'a> {
collection: &'a DeviceCollection,
index: u32,
}
impl<'a> Iterator for DeviceCollectionIter<'a> {
type Item = WasapiRes<Device>;
fn next(&mut self) -> Option<Self::Item> {
if self.index < self.collection.get_nbr_devices().unwrap() {
let device = self.collection.get_device_at_index(self.index);
self.index += 1;
Some(device)
} else {
None
}
}
}
impl<'a> IntoIterator for &'a DeviceCollection {
type Item = WasapiRes<Device>;
type IntoIter = DeviceCollectionIter<'a>;
fn into_iter(self) -> Self::IntoIter {
DeviceCollectionIter {
collection: self,
index: 0,
}
}
}
pub struct Device {
device: IMMDevice,
direction: Direction,
}
impl Device {
pub fn get_iaudioclient(&self) -> WasapiRes<AudioClient> {
let audio_client = unsafe { self.device.Activate::<IAudioClient>(CLSCTX_ALL, None)? };
Ok(AudioClient {
client: audio_client,
direction: self.direction,
sharemode: None,
})
}
pub fn get_state(&self) -> WasapiRes<DeviceState> {
let state: u32 = unsafe { self.device.GetState()? };
trace!("state: {:?}", state);
let state_enum = match state {
_ if state == DEVICE_STATE_ACTIVE => DeviceState::Active,
_ if state == DEVICE_STATE_DISABLED => DeviceState::Disabled,
_ if state == DEVICE_STATE_NOTPRESENT => DeviceState::NotPresent,
_ if state == DEVICE_STATE_UNPLUGGED => DeviceState::Unplugged,
x => return Err(WasapiError::new(&format!("Got an illegal state: {}", x)).into()),
};
Ok(state_enum)
}
pub fn get_friendlyname(&self) -> WasapiRes<String> {
self.get_string_property(&PKEY_Device_FriendlyName)
}
pub fn get_interface_friendlyname(&self) -> WasapiRes<String> {
self.get_string_property(&PKEY_DeviceInterface_FriendlyName)
}
pub fn get_description(&self) -> WasapiRes<String> {
self.get_string_property(&PKEY_Device_DeviceDesc)
}
fn get_string_property(&self, key: &PROPERTYKEY) -> WasapiRes<String> {
let store = unsafe { self.device.OpenPropertyStore(STGM_READ)? };
let prop = unsafe { store.GetValue(key)? };
let propstr = unsafe { PropVariantToStringAlloc(&prop)? };
let wide_name = unsafe { U16CString::from_ptr_str(propstr.0) };
let name = wide_name.to_string_lossy();
trace!("name: {}", name);
Ok(name)
}
pub fn get_id(&self) -> WasapiRes<String> {
let idstr = unsafe { self.device.GetId()? };
let wide_id = unsafe { U16CString::from_ptr_str(idstr.0) };
let id = wide_id.to_string_lossy();
trace!("id: {}", id);
Ok(id)
}
pub fn get_direction(&self) -> Direction {
self.direction
}
}
pub struct AudioClient {
client: IAudioClient,
direction: Direction,
sharemode: Option<ShareMode>,
}
impl AudioClient {
pub fn get_mixformat(&self) -> WasapiRes<WaveFormat> {
let temp_fmt_ptr = unsafe { self.client.GetMixFormat()? };
let temp_fmt = unsafe { *temp_fmt_ptr };
let mix_format =
if temp_fmt.cbSize == 22 && temp_fmt.wFormatTag as u32 == WAVE_FORMAT_EXTENSIBLE {
unsafe {
WaveFormat {
wave_fmt: (temp_fmt_ptr as *const _ as *const WAVEFORMATEXTENSIBLE).read(),
}
}
} else {
WaveFormat::from_waveformatex(temp_fmt)?
};
Ok(mix_format)
}
pub fn is_supported(
&self,
wave_fmt: &WaveFormat,
sharemode: &ShareMode,
) -> WasapiRes<Option<WaveFormat>> {
let supported = match sharemode {
ShareMode::Exclusive => {
unsafe {
self.client
.IsFormatSupported(
AUDCLNT_SHAREMODE_EXCLUSIVE,
wave_fmt.as_waveformatex_ref(),
None,
)
.ok()?
};
None
}
ShareMode::Shared => {
let mut supported_format: *mut WAVEFORMATEX = std::ptr::null_mut();
unsafe {
self.client
.IsFormatSupported(
AUDCLNT_SHAREMODE_SHARED,
wave_fmt.as_waveformatex_ref(),
Some(&mut supported_format),
)
.ok()?
};
if supported_format.is_null() {
debug!("The requested format is supported");
None
} else {
let temp_fmt: WAVEFORMATEX = unsafe { supported_format.read() };
debug!("The requested format is not supported but a simular one is");
let new_fmt = if temp_fmt.cbSize == 22
&& temp_fmt.wFormatTag as u32 == WAVE_FORMAT_EXTENSIBLE
{
debug!("got the nearest matching format as a WAVEFORMATEXTENSIBLE");
let temp_fmt_ext: WAVEFORMATEXTENSIBLE = unsafe {
(supported_format as *const _ as *const WAVEFORMATEXTENSIBLE).read()
};
WaveFormat {
wave_fmt: temp_fmt_ext,
}
} else {
debug!("got the nearest matching format as a WAVEFORMATEX, converting..");
WaveFormat::from_waveformatex(temp_fmt)?
};
Some(new_fmt)
}
}
};
Ok(supported)
}
pub fn is_supported_exclusive_with_quirks(
&self,
wave_fmt: &WaveFormat,
) -> WasapiRes<WaveFormat> {
let mut wave_fmt = wave_fmt.clone();
let supported_direct = self.is_supported(&wave_fmt, &ShareMode::Exclusive);
if supported_direct.is_ok() {
debug!("The requested format is supported as provided");
return Ok(wave_fmt);
}
if wave_fmt.get_nchannels() <= 2 {
debug!("Repeating query with format as WAVEFORMATEX");
let wave_formatex = wave_fmt.to_waveformatex().unwrap();
if self
.is_supported(&wave_formatex, &ShareMode::Exclusive)
.is_ok()
{
debug!("The requested format is supported as WAVEFORMATEX");
return Ok(wave_formatex);
}
}
let masks = make_channelmasks(wave_fmt.get_nchannels() as usize);
for mask in masks {
debug!("Repeating query with channel mask: {:#010b}", mask);
wave_fmt.wave_fmt.dwChannelMask = mask;
if self.is_supported(&wave_fmt, &ShareMode::Exclusive).is_ok() {
debug!(
"The requested format is supported with a modified mask: {:#010b}",
mask
);
return Ok(wave_fmt);
}
}
Err(WasapiError::new("Unable to find a supported format").into())
}
pub fn get_periods(&self) -> WasapiRes<(i64, i64)> {
let mut def_time = 0;
let mut min_time = 0;
unsafe {
self.client
.GetDevicePeriod(Some(&mut def_time), Some(&mut min_time))?
};
trace!("default period {}, min period {}", def_time, min_time);
Ok((def_time, min_time))
}
pub fn calculate_aligned_period_near(
&self,
desired_period: i64,
align_bytes: Option<u32>,
wave_fmt: &WaveFormat,
) -> WasapiRes<i64> {
let (_default_period, min_period) = self.get_periods()?;
let adjusted_desired_period = cmp::max(desired_period, min_period);
let frame_bytes = wave_fmt.get_blockalign();
let period_alignment_bytes = match align_bytes {
Some(0) => frame_bytes,
Some(bytes) => frame_bytes.lcm(&bytes),
None => frame_bytes,
};
let period_alignment_frames = period_alignment_bytes as i64 / frame_bytes as i64;
let desired_period_frames =
(adjusted_desired_period as f64 * wave_fmt.get_samplespersec() as f64 / 10000000.0)
.round() as i64;
let min_period_frames =
(min_period as f64 * wave_fmt.get_samplespersec() as f64 / 10000000.0).ceil() as i64;
let mut nbr_segments = desired_period_frames / period_alignment_frames;
if nbr_segments * period_alignment_frames < min_period_frames {
nbr_segments += 1;
}
let aligned_period = calculate_period_100ns(
period_alignment_frames * nbr_segments,
wave_fmt.get_samplespersec() as i64,
);
Ok(aligned_period)
}
pub fn initialize_client(
&mut self,
wavefmt: &WaveFormat,
period: i64,
direction: &Direction,
sharemode: &ShareMode,
convert: bool,
) -> WasapiRes<()> {
if sharemode == &ShareMode::Exclusive && convert {
return Err(
WasapiError::new("Cant use automatic format conversion in exclusive mode").into(),
);
}
let mut streamflags = match (&self.direction, direction, sharemode) {
(Direction::Render, Direction::Capture, ShareMode::Shared) => {
AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_LOOPBACK
}
(Direction::Render, Direction::Capture, ShareMode::Exclusive) => {
return Err(WasapiError::new("Cant use Loopback with exclusive mode").into());
}
(Direction::Capture, Direction::Render, _) => {
return Err(WasapiError::new("Cant render to a capture device").into());
}
_ => AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
};
if convert {
streamflags |=
AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM | AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY;
}
let mode = match sharemode {
ShareMode::Exclusive => AUDCLNT_SHAREMODE_EXCLUSIVE,
ShareMode::Shared => AUDCLNT_SHAREMODE_SHARED,
};
let device_period = match sharemode {
ShareMode::Exclusive => period,
ShareMode::Shared => 0,
};
self.sharemode = Some(*sharemode);
unsafe {
self.client.Initialize(
mode,
streamflags,
period,
device_period,
wavefmt.as_waveformatex_ref(),
None,
)?;
}
Ok(())
}
pub fn set_get_eventhandle(&self) -> WasapiRes<Handle> {
let h_event = unsafe { CreateEventA(None, false, false, PCSTR::null())? };
unsafe { self.client.SetEventHandle(h_event)? };
Ok(Handle { handle: h_event })
}
pub fn get_bufferframecount(&self) -> WasapiRes<u32> {
let buffer_frame_count = unsafe { self.client.GetBufferSize()? };
trace!("buffer_frame_count {}", buffer_frame_count);
Ok(buffer_frame_count)
}
pub fn get_current_padding(&self) -> WasapiRes<u32> {
let padding_count = unsafe { self.client.GetCurrentPadding()? };
trace!("padding_count {}", padding_count);
Ok(padding_count)
}
pub fn get_available_space_in_frames(&self) -> WasapiRes<u32> {
let frames = match self.sharemode {
Some(ShareMode::Exclusive) => {
let buffer_frame_count = unsafe { self.client.GetBufferSize()? };
trace!("buffer_frame_count {}", buffer_frame_count);
buffer_frame_count
}
Some(ShareMode::Shared) => {
let padding_count = unsafe { self.client.GetCurrentPadding()? };
let buffer_frame_count = unsafe { self.client.GetBufferSize()? };
buffer_frame_count - padding_count
}
_ => return Err(WasapiError::new("Client has not been initialized").into()),
};
Ok(frames)
}
pub fn start_stream(&self) -> WasapiRes<()> {
unsafe { self.client.Start()? };
Ok(())
}
pub fn stop_stream(&self) -> WasapiRes<()> {
unsafe { self.client.Stop()? };
Ok(())
}
pub fn reset_stream(&self) -> WasapiRes<()> {
unsafe { self.client.Reset()? };
Ok(())
}
pub fn get_audiorenderclient(&self) -> WasapiRes<AudioRenderClient> {
let client = unsafe { self.client.GetService::<IAudioRenderClient>()? };
Ok(AudioRenderClient { client })
}
pub fn get_audiocaptureclient(&self) -> WasapiRes<AudioCaptureClient> {
let client = unsafe { self.client.GetService::<IAudioCaptureClient>()? };
Ok(AudioCaptureClient {
client,
sharemode: self.sharemode,
})
}
pub fn get_audiosessioncontrol(&self) -> WasapiRes<AudioSessionControl> {
let control = unsafe { self.client.GetService::<IAudioSessionControl>()? };
Ok(AudioSessionControl { control })
}
pub fn get_audioclock(&self) -> WasapiRes<AudioClock> {
let clock = unsafe { self.client.GetService::<IAudioClock>()? };
Ok(AudioClock { clock })
}
pub fn get_direction(&self) -> Direction {
self.direction
}
pub fn get_sharemode(&self) -> Option<ShareMode> {
self.sharemode
}
}
pub struct AudioSessionControl {
control: IAudioSessionControl,
}
impl AudioSessionControl {
pub fn get_state(&self) -> WasapiRes<SessionState> {
let state = unsafe { self.control.GetState()? };
#[allow(non_upper_case_globals)]
let sessionstate = match state {
_ if state == AudioSessionStateActive => SessionState::Active,
_ if state == AudioSessionStateInactive => SessionState::Inactive,
_ if state == AudioSessionStateExpired => SessionState::Expired,
x => {
return Err(
WasapiError::new(&format!("Got an illegal session state {:?}", x)).into(),
);
}
};
Ok(sessionstate)
}
pub fn register_session_notification(&self, callbacks: Weak<EventCallbacks>) -> WasapiRes<()> {
let events: IAudioSessionEvents = AudioSessionEvents::new(callbacks).into();
match unsafe { self.control.RegisterAudioSessionNotification(&events) } {
Ok(()) => Ok(()),
Err(err) => {
Err(WasapiError::new(&format!("Failed to register notifications, {}", err)).into())
}
}
}
}
pub struct AudioClock {
clock: IAudioClock,
}
impl AudioClock {
pub fn get_frequency(&self) -> WasapiRes<u64> {
let freq = unsafe { self.clock.GetFrequency()? };
Ok(freq)
}
pub fn get_position(&self) -> WasapiRes<(u64, u64)> {
let mut pos = 0;
let mut timer = 0;
unsafe { self.clock.GetPosition(&mut pos, Some(&mut timer))? };
Ok((pos, timer))
}
}
pub struct AudioRenderClient {
client: IAudioRenderClient,
}
impl AudioRenderClient {
pub fn write_to_device(
&self,
nbr_frames: usize,
byte_per_frame: usize,
data: &[u8],
buffer_flags: Option<BufferFlags>,
) -> WasapiRes<()> {
let nbr_bytes = nbr_frames * byte_per_frame;
if nbr_bytes != data.len() {
return Err(WasapiError::new(
format!(
"Wrong length of data, got {}, expected {}",
data.len(),
nbr_bytes
)
.as_str(),
)
.into());
}
let bufferptr = unsafe { self.client.GetBuffer(nbr_frames as u32)? };
let bufferslice = unsafe { slice::from_raw_parts_mut(bufferptr, nbr_bytes) };
bufferslice.copy_from_slice(data);
let flags = match buffer_flags {
Some(bflags) => bflags.to_u32(),
None => 0,
};
unsafe { self.client.ReleaseBuffer(nbr_frames as u32, flags)? };
trace!("wrote {} frames", nbr_frames);
Ok(())
}
pub fn write_to_device_from_deque(
&self,
nbr_frames: usize,
byte_per_frame: usize,
data: &mut VecDeque<u8>,
buffer_flags: Option<BufferFlags>,
) -> WasapiRes<()> {
let nbr_bytes = nbr_frames * byte_per_frame;
if nbr_bytes > data.len() {
return Err(WasapiError::new(
format!("To little data, got {}, need {}", data.len(), nbr_bytes).as_str(),
)
.into());
}
let bufferptr = unsafe { self.client.GetBuffer(nbr_frames as u32)? };
let bufferslice = unsafe { slice::from_raw_parts_mut(bufferptr, nbr_bytes) };
for element in bufferslice.iter_mut() {
*element = data.pop_front().unwrap();
}
let flags = match buffer_flags {
Some(bflags) => bflags.to_u32(),
None => 0,
};
unsafe { self.client.ReleaseBuffer(nbr_frames as u32, flags)? };
trace!("wrote {} frames", nbr_frames);
Ok(())
}
}
#[derive(Debug)]
pub struct BufferFlags {
pub data_discontinuity: bool,
pub silent: bool,
pub timestamp_error: bool,
}
impl BufferFlags {
pub fn new(flags: u32) -> Self {
BufferFlags {
data_discontinuity: flags & AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY.0 as u32 > 0,
silent: flags & AUDCLNT_BUFFERFLAGS_SILENT.0 as u32 > 0,
timestamp_error: flags & AUDCLNT_BUFFERFLAGS_TIMESTAMP_ERROR.0 as u32 > 0,
}
}
pub fn to_u32(&self) -> u32 {
let mut value = 0;
if self.data_discontinuity {
value += AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY.0 as u32;
}
if self.silent {
value += AUDCLNT_BUFFERFLAGS_SILENT.0 as u32;
}
if self.timestamp_error {
value += AUDCLNT_BUFFERFLAGS_TIMESTAMP_ERROR.0 as u32;
}
value
}
}
pub struct AudioCaptureClient {
client: IAudioCaptureClient,
sharemode: Option<ShareMode>,
}
impl AudioCaptureClient {
pub fn get_next_nbr_frames(&self) -> WasapiRes<Option<u32>> {
if let Some(ShareMode::Exclusive) = self.sharemode {
return Ok(None);
}
let nbr_frames = unsafe { self.client.GetNextPacketSize()? };
Ok(Some(nbr_frames))
}
pub fn read_from_device(
&self,
bytes_per_frame: usize,
data: &mut [u8],
) -> WasapiRes<(u32, BufferFlags)> {
let data_len_in_frames = data.len() / bytes_per_frame;
let mut buffer_ptr = ptr::null_mut();
let mut nbr_frames_returned = 0;
let mut flags = 0;
unsafe {
self.client.GetBuffer(
&mut buffer_ptr,
&mut nbr_frames_returned,
&mut flags,
None,
None,
)?
};
let bufferflags = BufferFlags::new(flags);
if nbr_frames_returned == 0 {
unsafe { self.client.ReleaseBuffer(nbr_frames_returned)? };
return Ok((0, bufferflags));
}
if data_len_in_frames < nbr_frames_returned as usize {
unsafe { self.client.ReleaseBuffer(nbr_frames_returned)? };
return Err(WasapiError::new(
format!(
"Wrong length of data, got {} frames, expected at least {} frames",
data_len_in_frames, nbr_frames_returned
)
.as_str(),
)
.into());
}
let len_in_bytes = nbr_frames_returned as usize * bytes_per_frame;
let bufferslice = unsafe { slice::from_raw_parts(buffer_ptr, len_in_bytes) };
data[..len_in_bytes].copy_from_slice(bufferslice);
unsafe { self.client.ReleaseBuffer(nbr_frames_returned)? };
trace!("read {} frames", nbr_frames_returned);
Ok((nbr_frames_returned, bufferflags))
}
pub fn read_from_device_to_deque(
&self,
bytes_per_frame: usize,
data: &mut VecDeque<u8>,
) -> WasapiRes<BufferFlags> {
let mut buffer_ptr = ptr::null_mut();
let mut nbr_frames_returned = 0;
let mut flags = 0;
unsafe {
self.client.GetBuffer(
&mut buffer_ptr,
&mut nbr_frames_returned,
&mut flags,
None,
None,
)?
};
let bufferflags = BufferFlags::new(flags);
let len_in_bytes = nbr_frames_returned as usize * bytes_per_frame;
let bufferslice = unsafe { slice::from_raw_parts(buffer_ptr, len_in_bytes) };
for element in bufferslice.iter() {
data.push_back(*element);
}
unsafe { self.client.ReleaseBuffer(nbr_frames_returned)? };
trace!("read {} frames", nbr_frames_returned);
Ok(bufferflags)
}
pub fn get_sharemode(&self) -> Option<ShareMode> {
self.sharemode
}
}
pub struct Handle {
handle: HANDLE,
}
impl Handle {
pub fn wait_for_event(&self, timeout_ms: u32) -> WasapiRes<()> {
let retval = unsafe { WaitForSingleObject(self.handle, timeout_ms) };
if retval.0 != WAIT_OBJECT_0.0 {
return Err(WasapiError::new("Wait timed out").into());
}
Ok(())
}
}