1use std::ffi::CStr;
2
3use crate::NativeFormats;
4
5#[cfg(all(feature = "log", not(feature = "tracing")))]
6use log::{error, warn};
7#[cfg(feature = "tracing")]
8use tracing::{error, warn};
9
10pub type SessionID = u32;
14
15#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
19#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
20pub struct DeviceID {
21 #[cfg_attr(feature = "serde", serde(default))]
23 pub name: String,
24 #[cfg_attr(feature = "serde", serde(default))]
27 pub session_id: SessionID,
28}
29
30impl DeviceID {
31 pub fn as_serialized_string(&self) -> String {
37 format!("{}", self)
38 }
39
40 pub fn from_serialized_string(s: &str) -> Self {
47 if let Some((name, session_id)) = s.rsplit_once(':') {
48 let session_id: u32 = match session_id.trim().parse() {
49 Ok(id) => id,
50 Err(e) => {
51 #[cfg(not(any(feature = "tracing", feature = "log")))]
52 let _ = e;
53
54 #[cfg(any(feature = "tracing", feature = "log"))]
55 warn!("Failed to parse session ID for audio device: {}", e);
56 0
57 }
58 };
59
60 Self {
61 name: name.trim().to_string(),
62 session_id,
63 }
64 } else {
65 Self {
66 name: s.trim().to_string(),
67 session_id: 0,
68 }
69 }
70 }
71}
72
73impl std::fmt::Display for DeviceID {
74 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
75 write!(f, "{}:{}", &self.name, self.session_id)
76 }
77}
78
79#[derive(Debug, Clone, PartialEq)]
81#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
82pub struct DeviceInfo {
83 pub id: DeviceID,
85 pub output_channels: u32,
87 pub input_channels: u32,
89 pub duplex_channels: u32,
91
92 pub is_default_output: bool,
94 pub is_default_input: bool,
96
97 pub native_formats: NativeFormats,
102
103 pub preferred_sample_rate: u32,
105 pub sample_rates: Vec<u32>,
107}
108
109impl DeviceInfo {
110 pub fn name(&self) -> &str {
112 &self.id.name
113 }
114
115 pub fn from_raw(d: rtaudio_sys::rtaudio_device_info_t) -> Self {
116 let mut sample_rates = Vec::new();
117 for sr in d.sample_rates.iter() {
118 if *sr <= 0 {
119 break;
120 }
121
122 sample_rates.push(*sr as u32);
123 }
124
125 let name_slice: &[u8] =
128 unsafe { std::slice::from_raw_parts(d.name.as_ptr() as *const u8, d.name.len()) };
129
130 let name = match CStr::from_bytes_until_nul(&name_slice) {
131 Ok(n) => n.to_string_lossy().to_string(),
132 Err(e) => {
133 #[cfg(not(any(feature = "tracing", feature = "log")))]
134 let _ = e;
135
136 #[cfg(any(feature = "tracing", feature = "log"))]
137 error!("RtAudio: Failed to parse audio device name: {}", e);
138
139 String::from("error")
140 }
141 };
142
143 Self {
144 id: DeviceID {
145 name,
146 session_id: d.id as u32,
147 },
148 output_channels: d.output_channels as u32,
149 input_channels: d.input_channels as u32,
150 duplex_channels: d.duplex_channels as u32,
151 is_default_output: d.is_default_output != 0,
152 is_default_input: d.is_default_input != 0,
153 native_formats: NativeFormats::from_bits_truncate(d.native_formats),
154 preferred_sample_rate: d.preferred_sample_rate as u32,
155 sample_rates,
156 }
157 }
158}