1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
|
pub use crate::iter::{SupportedInputConfigs, SupportedOutputConfigs};
use super::sys;
use crate::host::com;
use crate::ChannelCount;
use crate::DefaultStreamConfigError;
use crate::DeviceDescription;
use crate::DeviceDescriptionBuilder;
use crate::DeviceId;
use crate::DeviceIdError;
use crate::DeviceNameError;
use crate::DevicesError;
use crate::FrameCount;
use crate::SampleFormat;
use crate::SampleRate;
use crate::SupportedBufferSize;
use crate::SupportedStreamConfig;
use crate::SupportedStreamConfigRange;
use crate::SupportedStreamConfigsError;
use std::hash::{Hash, Hasher};
use std::sync::atomic::AtomicU32;
use std::sync::{Arc, Mutex};
/// A ASIO Device
#[derive(Clone)]
pub struct Device {
name: String,
// Metadata cached during enumeration
channels_in: ChannelCount,
channels_out: ChannelCount,
sample_rate: SampleRate,
buffer_size_min: FrameCount,
buffer_size_max: FrameCount,
input_sample_format: Option<SampleFormat>,
output_sample_format: Option<SampleFormat>,
supported_sample_rates: Vec<SampleRate>,
// Input and/or Output stream.
// A driver can only have one of each.
// They need to be created at the same time.
pub(super) asio_streams: Arc<Mutex<sys::AsioStreams>>,
pub(super) current_callback_flag: Arc<AtomicU32>,
}
/// All available devices.
pub struct Devices {
asio: Arc<sys::Asio>,
drivers: std::vec::IntoIter<String>,
current_driver: Option<sys::Driver>,
}
impl PartialEq for Device {
fn eq(&self, other: &Self) -> bool {
self.name == other.name
}
}
impl Eq for Device {}
impl Hash for Device {
fn hash<H: Hasher>(&self, state: &mut H) {
self.name.hash(state);
}
}
impl Device {
pub fn description(&self) -> Result<DeviceDescription, DeviceNameError> {
let direction = crate::device_description::direction_from_counts(
Some(self.channels_in),
Some(self.channels_out),
);
Ok(DeviceDescriptionBuilder::new(self.name.clone())
.driver(self.name.clone())
.direction(direction)
.build())
}
pub fn id(&self) -> Result<DeviceId, DeviceIdError> {
Ok(DeviceId(crate::platform::HostId::Asio, self.name.clone()))
}
/// Gets the supported input configs.
/// TODO currently only supports the default.
/// Need to find all possible configs.
pub fn supported_input_configs(
&self,
) -> Result<SupportedInputConfigs, SupportedStreamConfigsError> {
let default = self
.default_input_config()
.map_err(|_| SupportedStreamConfigsError::DeviceNotAvailable)?;
Ok(self.configs_for(default).into_iter())
}
/// Gets the supported output configs.
/// TODO currently only supports the default.
/// Need to find all possible configs.
pub fn supported_output_configs(
&self,
) -> Result<SupportedOutputConfigs, SupportedStreamConfigsError> {
let default = self
.default_output_config()
.map_err(|_| SupportedStreamConfigsError::DeviceNotAvailable)?;
Ok(self.configs_for(default).into_iter())
}
/// Returns the default input config
pub fn default_input_config(&self) -> Result<SupportedStreamConfig, DefaultStreamConfigError> {
self.default_config(self.channels_in, self.input_sample_format)
}
/// Returns the default output config
pub fn default_output_config(&self) -> Result<SupportedStreamConfig, DefaultStreamConfigError> {
self.default_config(self.channels_out, self.output_sample_format)
}
fn default_config(
&self,
channels: ChannelCount,
sample_format: Option<SampleFormat>,
) -> Result<SupportedStreamConfig, DefaultStreamConfigError> {
if channels == 0 {
return Err(DefaultStreamConfigError::StreamTypeNotSupported);
}
let sample_format =
sample_format.ok_or(DefaultStreamConfigError::StreamTypeNotSupported)?;
Ok(SupportedStreamConfig {
channels,
sample_rate: self.sample_rate,
buffer_size: SupportedBufferSize::Range {
min: self.buffer_size_min,
max: self.buffer_size_max,
},
sample_format,
})
}
fn configs_for(&self, default: SupportedStreamConfig) -> Vec<SupportedStreamConfigRange> {
let mut configs = Vec::with_capacity(default.channels as usize);
for &rate in &self.supported_sample_rates {
for channels in 1..=default.channels {
configs.push(SupportedStreamConfigRange {
channels,
min_sample_rate: rate,
max_sample_rate: rate,
buffer_size: default.buffer_size,
sample_format: default.sample_format,
});
}
}
configs
}
}
impl Devices {
pub fn new(asio: Arc<sys::Asio>) -> Result<Self, DevicesError> {
// Make sure that COM is initialized.
com::com_initialized();
let drivers = asio.driver_names().into_iter();
Ok(Self {
asio,
drivers,
current_driver: None,
})
}
}
impl Iterator for Devices {
type Item = Device;
/// Enumerate devices by briefly loading each driver to capture its metadata.
fn next(&mut self) -> Option<Device> {
// Drop the previously loaded driver before attempting to load the next one.
self.current_driver = None;
loop {
match self.drivers.next() {
Some(name) => match self.asio.load_driver(&name) {
Ok(driver) => {
let Ok(channels) = driver.channels() else {
continue;
};
if channels.ins == 0 && channels.outs == 0 {
continue;
}
// Some drivers (e.g. Realtek ASIO) return 0 for sample_rate() until a
// stream is active. Treat 0 as "not yet known" rather than skipping.
let sample_rate = driver.sample_rate().unwrap_or(0.0);
let Ok(buffer_size_range) = driver.buffersize_range() else {
continue;
};
let input_sample_format = driver
.input_data_type()
.ok()
.and_then(|t| convert_data_type(&t));
let output_sample_format = driver
.output_data_type()
.ok()
.and_then(|t| convert_data_type(&t));
let supported_sample_rates: Vec<SampleRate> = crate::COMMON_SAMPLE_RATES
.iter()
.copied()
.filter(|&r| driver.can_sample_rate(r.into()).unwrap_or(false))
.collect();
self.current_driver = Some(driver);
let asio_streams = Arc::new(Mutex::new(sys::AsioStreams {
input: None,
output: None,
}));
return Some(Device {
name,
channels_in: channels.ins as ChannelCount,
channels_out: channels.outs as ChannelCount,
sample_rate: sample_rate as SampleRate,
buffer_size_min: buffer_size_range.min as FrameCount,
buffer_size_max: buffer_size_range.max as FrameCount,
input_sample_format,
output_sample_format,
supported_sample_rates,
asio_streams,
// Initialize with sentinel value so it never matches global flag state (0 or 1).
current_callback_flag: Arc::new(AtomicU32::new(u32::MAX)),
});
}
// A different driver is already loaded (e.g. an active Stream holds it). Stop
// cleanly rather than spinning through the rest of the list.
Err(sys::LoadDriverError::DriverAlreadyExists) => return None,
// Driver failed to load for its own reasons; skip and try the next.
Err(_) => continue,
},
None => return None,
}
}
}
}
pub(crate) fn convert_data_type(ty: &sys::AsioSampleType) -> Option<SampleFormat> {
let fmt = match *ty {
sys::AsioSampleType::ASIOSTInt16MSB => SampleFormat::I16,
sys::AsioSampleType::ASIOSTInt16LSB => SampleFormat::I16,
sys::AsioSampleType::ASIOSTInt24MSB => SampleFormat::I24,
sys::AsioSampleType::ASIOSTInt24LSB => SampleFormat::I24,
sys::AsioSampleType::ASIOSTInt32MSB => SampleFormat::I32,
sys::AsioSampleType::ASIOSTInt32LSB => SampleFormat::I32,
sys::AsioSampleType::ASIOSTFloat32MSB => SampleFormat::F32,
sys::AsioSampleType::ASIOSTFloat32LSB => SampleFormat::F32,
sys::AsioSampleType::ASIOSTFloat64MSB => SampleFormat::F64,
sys::AsioSampleType::ASIOSTFloat64LSB => SampleFormat::F64,
_ => return None,
};
Some(fmt)
}
|