use std::ffi::CStr;
use crate::NativeFormats;
#[cfg(all(feature = "log", not(feature = "tracing")))]
use log::{error, warn};
#[cfg(feature = "tracing")]
use tracing::{error, warn};
pub type SessionID = u32;
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct DeviceID {
#[cfg_attr(feature = "serde", serde(default))]
pub name: String,
#[cfg_attr(feature = "serde", serde(default))]
pub session_id: SessionID,
}
impl DeviceID {
pub fn as_serialized_string(&self) -> String {
format!("{}", self)
}
pub fn from_serialized_string(s: &str) -> Self {
if let Some((name, session_id)) = s.rsplit_once(':') {
let session_id: u32 = match session_id.trim().parse() {
Ok(id) => id,
Err(e) => {
#[cfg(not(any(feature = "tracing", feature = "log")))]
let _ = e;
#[cfg(any(feature = "tracing", feature = "log"))]
warn!("Failed to parse session ID for audio device: {}", e);
0
}
};
Self {
name: name.trim().to_string(),
session_id,
}
} else {
Self {
name: s.trim().to_string(),
session_id: 0,
}
}
}
}
impl std::fmt::Display for DeviceID {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}:{}", &self.name, self.session_id)
}
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct DeviceInfo {
pub id: DeviceID,
pub output_channels: u32,
pub input_channels: u32,
pub duplex_channels: u32,
pub is_default_output: bool,
pub is_default_input: bool,
pub native_formats: NativeFormats,
pub preferred_sample_rate: u32,
pub sample_rates: Vec<u32>,
}
impl DeviceInfo {
pub fn name(&self) -> &str {
&self.id.name
}
pub fn from_raw(d: rtaudio_sys::rtaudio_device_info_t) -> Self {
let mut sample_rates = Vec::new();
for sr in d.sample_rates.iter() {
if *sr <= 0 {
break;
}
sample_rates.push(*sr as u32);
}
let name_slice: &[u8] =
unsafe { std::slice::from_raw_parts(d.name.as_ptr() as *const u8, d.name.len()) };
let name = match CStr::from_bytes_until_nul(&name_slice) {
Ok(n) => n.to_string_lossy().to_string(),
Err(e) => {
#[cfg(not(any(feature = "tracing", feature = "log")))]
let _ = e;
#[cfg(any(feature = "tracing", feature = "log"))]
error!("RtAudio: Failed to parse audio device name: {}", e);
String::from("error")
}
};
Self {
id: DeviceID {
name,
session_id: d.id as u32,
},
output_channels: d.output_channels as u32,
input_channels: d.input_channels as u32,
duplex_channels: d.duplex_channels as u32,
is_default_output: d.is_default_output != 0,
is_default_input: d.is_default_input != 0,
native_formats: NativeFormats::from_bits_truncate(d.native_formats),
preferred_sample_rate: d.preferred_sample_rate as u32,
sample_rates,
}
}
}