use std::ffi::CStr;
use std::ptr::NonNull;
use crate::error::{Error, Result};
use crate::ffi;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AudioDeviceType {
Input,
Output,
}
impl AudioDeviceType {
fn from_ffi(device_type: i32) -> Self {
match device_type {
x if x == ffi::AUDIO_DEVICE_TYPE_OUTPUT as i32 => AudioDeviceType::Output,
_ => AudioDeviceType::Input,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AudioFormat {
S16,
F32,
}
impl AudioFormat {
pub(crate) fn from_ffi(format: i32) -> Self {
match format {
x if x == ffi::AUDIO_FORMAT_F32 as i32 => AudioFormat::F32,
_ => AudioFormat::S16,
}
}
}
pub struct AudioDevice {
raw: NonNull<ffi::AudioDevice>,
device_type: AudioDeviceType,
}
impl AudioDevice {
pub fn name(&self) -> Result<String> {
let name_ptr = unsafe { ffi::audio_device_name(self.raw.as_ptr()) };
if name_ptr.is_null() {
return Err(Error::NullPointer("device name"));
}
let name = unsafe { CStr::from_ptr(name_ptr) };
Ok(name.to_string_lossy().into_owned())
}
pub fn unique_id(&self) -> Result<String> {
let id_ptr = unsafe { ffi::audio_device_unique_id(self.raw.as_ptr()) };
if id_ptr.is_null() {
return Err(Error::NullPointer("device unique_id"));
}
let id = unsafe { CStr::from_ptr(id_ptr) };
Ok(id.to_string_lossy().into_owned())
}
pub fn channels(&self) -> i32 {
unsafe { ffi::audio_device_channels(self.raw.as_ptr()) }
}
pub fn sample_rate(&self) -> i32 {
unsafe { ffi::audio_device_sample_rate(self.raw.as_ptr()) }
}
pub fn device_type(&self) -> AudioDeviceType {
self.device_type
}
}
unsafe impl Send for AudioDevice {}
unsafe impl Sync for AudioDevice {}
pub struct AudioDeviceList {
devices_ptr: *mut *mut ffi::AudioDevice,
count: i32,
devices: Vec<AudioDevice>,
}
impl AudioDeviceList {
pub fn enumerate() -> Result<Self> {
Self::enumerate_internal(None)
}
pub fn enumerate_input() -> Result<Self> {
Self::enumerate_internal(Some(AudioDeviceType::Input))
}
pub fn enumerate_output() -> Result<Self> {
Self::enumerate_internal(Some(AudioDeviceType::Output))
}
fn enumerate_internal(filter: Option<AudioDeviceType>) -> Result<Self> {
let mut devices_ptr: *mut *mut ffi::AudioDevice = std::ptr::null_mut();
let mut count: i32 = 0;
let ret = unsafe { ffi::audio_enumerate_devices(&mut devices_ptr, &mut count) };
if ret < 0 {
return Err(Error::DeviceAccessDenied);
}
let devices: Vec<AudioDevice> = if count > 0 && !devices_ptr.is_null() {
(0..count as usize)
.filter_map(|i| {
let device_ptr = unsafe { *devices_ptr.add(i) };
NonNull::new(device_ptr).map(|raw| {
let device_type = AudioDeviceType::from_ffi(unsafe {
ffi::audio_device_type(raw.as_ptr())
});
AudioDevice { raw, device_type }
})
})
.filter(|d| filter.is_none_or(|f| d.device_type == f))
.collect()
} else {
Vec::new()
};
Ok(Self {
devices_ptr,
count,
devices,
})
}
pub fn devices(&self) -> &[AudioDevice] {
&self.devices
}
pub fn len(&self) -> usize {
self.devices.len()
}
pub fn is_empty(&self) -> bool {
self.devices.is_empty()
}
}
impl Drop for AudioDeviceList {
fn drop(&mut self) {
if !self.devices_ptr.is_null() {
unsafe {
ffi::audio_free_devices(self.devices_ptr, self.count);
}
}
}
}
unsafe impl Send for AudioDeviceList {}
unsafe impl Sync for AudioDeviceList {}