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