nandi/jolt-nativepublic Fork 0
cfd3e3677bed92e80cfe447469a249cfdc0b1401
Commits
Clone
git clone https://git.rickub.com/nandi/jolt-native.git
git clone ssh://git@rickub.com/nandi/jolt-native.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

mod.rs · 1281 lines · 46.6 KBRust Blame HistoryRaw
Lift freeq's AV media plane out of sleek 90f8b89 nandi 19d ago1pub(crate) mod asio_import;
2#[macro_use]
3pub mod errors;
4
5use self::errors::{AsioError, AsioErrorWrapper, LoadDriverError};
6use num_traits::FromPrimitive;
7
8use std::ffi::{CStr, CString};
9use std::os::raw::{c_char, c_double, c_void};
10use std::ptr::null_mut;
11use std::sync::{
12 atomic::{AtomicBool, AtomicU32, AtomicU64, Ordering},
13 Arc, Mutex, MutexGuard, Weak,
14};
15use std::time::Duration;
16
17// On Windows (where ASIO actually runs), c_long is i32.
18// On non-Windows platforms (for docs.rs and local testing), redefine c_long as i32 to match.
19#[cfg(target_os = "windows")]
20use std::os::raw::c_long;
21#[cfg(not(target_os = "windows"))]
22type c_long = i32;
23
24// Bindings import
25use self::asio_import as ai;
26
27/// A handle to the ASIO API.
28///
29/// There should only be one instance of this type at any point in time.
30#[derive(Debug, Default)]
31pub struct Asio {
32 // Keeps track of whether or not a driver is already loaded.
33 //
34 // This is necessary as ASIO only supports one `Driver` at a time.
35 loaded_driver: Mutex<Weak<DriverInner>>,
36}
37
38/// A handle to a single ASIO driver.
39///
40/// Creating an instance of this type loads and initialises the driver.
41///
42/// Dropping all `Driver` instances will automatically dispose of any resources and de-initialise
43/// the driver.
44#[derive(Clone, Debug)]
45pub struct Driver {
46 inner: Arc<DriverInner>,
47}
48
49// Contains the state associated with a `Driver`.
50//
51// This state may be shared between multiple `Driver` handles representing the same underlying
52// driver. Only when the last `Driver` is dropped will the `Drop` implementation for this type run
53// and the necessary driver resources will be de-allocated and unloaded.
54//
55// The same could be achieved by returning an `Arc<Driver>` from the `Host::load_driver` API,
56// however the `DriverInner` abstraction is required in order to allow for the `Driver::destroy`
57// method to exist safely. By wrapping the `Arc<DriverInner>` in the `Driver` type, we can make
58// sure the user doesn't `try_unwrap` the `Arc` and invalidate the `Asio` instance's weak pointer.
59// This would allow for instantiation of a separate driver before the existing one is destroyed,
60// which is disallowed by ASIO.
61#[derive(Debug)]
62struct DriverInner {
63 state: Mutex<DriverState>,
64 // The unique name associated with this driver.
65 name: String,
66 // Track whether or not the driver has been destroyed.
67 //
68 // This allows for the user to manually destroy the driver and handle any errors if they wish.
69 //
70 // In the case that the driver has been manually destroyed this flag will be set to `true`
71 // indicating to the `drop` implementation that there is nothing to be done.
72 destroyed: bool,
73}
74
75/// All possible states of an ASIO `Driver` instance.
76///
77/// Mapped to the finite state machine in the ASIO SDK docs.
78#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
79pub(crate) enum DriverState {
80 Initialized,
81 Prepared,
82 Running,
83}
84
85/// Amount of input and output channels available.
86#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
87pub struct Channels {
88 pub ins: i32,
89 pub outs: i32,
90}
91
92/// Hardware latency in frames for the input and output streams.
93#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
94pub struct Latencies {
95 pub input: i32,
96 pub output: i32,
97}
98
99/// Minimum and maximum supported buffer sizes in frames.
100#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
101pub struct BufferSizeRange {
102 pub min: i32,
103 pub max: i32,
104}
105
106/// Information provided to the BufferCallback.
107#[derive(Debug)]
108pub struct CallbackInfo {
109 pub buffer_index: i32,
110 /// System time at the start of this buffer period, in nanoseconds.
111 pub system_time: u64,
112 pub callback_flag: u32,
113}
114
115/// Holds the pointer to the callbacks that come from cpal
116struct BufferCallback(Box<dyn FnMut(&CallbackInfo) + Send>);
117
118/// Input and Output streams.
119///
120/// There is only ever max one input and one output.
121///
122/// Only one is required.
123pub struct AsioStreams {
124 pub input: Option<AsioStream>,
125 pub output: Option<AsioStream>,
126}
127
128/// A stream to ASIO.
129///
130/// Contains the buffers.
131pub struct AsioStream {
132 /// A Double buffer per channel
133 pub buffer_infos: Vec<AsioBufferInfo>,
134 /// Size of each buffer
135 pub buffer_size: i32,
136}
137
138/// All the possible types from ASIO.
139/// This is a direct copy of the ASIOSampleType
140/// inside ASIO SDK.
141#[derive(Debug, FromPrimitive)]
142#[repr(C)]
143pub enum AsioSampleType {
144 ASIOSTInt16MSB = 0,
145 ASIOSTInt24MSB = 1, // used for 20 bits as well
146 ASIOSTInt32MSB = 2,
147 ASIOSTFloat32MSB = 3, // IEEE 754 32 bit float
148 ASIOSTFloat64MSB = 4, // IEEE 754 64 bit double float
149
150 // these are used for 32 bit data buffer, with different alignment of the data inside
151 // 32 bit PCI bus systems can be more easily used with these
152 ASIOSTInt32MSB16 = 8, // 32 bit data with 16 bit alignment
153 ASIOSTInt32MSB18 = 9, // 32 bit data with 18 bit alignment
154 ASIOSTInt32MSB20 = 10, // 32 bit data with 20 bit alignment
155 ASIOSTInt32MSB24 = 11, // 32 bit data with 24 bit alignment
156
157 ASIOSTInt16LSB = 16,
158 ASIOSTInt24LSB = 17, // used for 20 bits as well
159 ASIOSTInt32LSB = 18,
160 ASIOSTFloat32LSB = 19, // IEEE 754 32 bit float, as found on Intel x86 architecture
161 ASIOSTFloat64LSB = 20, // IEEE 754 64 bit double float, as found on Intel x86 architecture
162
163 // these are used for 32 bit data buffer, with different alignment of the data inside
164 // 32 bit PCI bus systems can more easily used with these
165 ASIOSTInt32LSB16 = 24, // 32 bit data with 18 bit alignment
166 ASIOSTInt32LSB18 = 25, // 32 bit data with 18 bit alignment
167 ASIOSTInt32LSB20 = 26, // 32 bit data with 20 bit alignment
168 ASIOSTInt32LSB24 = 27, // 32 bit data with 24 bit alignment
169
170 // ASIO DSD format.
171 ASIOSTDSDInt8LSB1 = 32, // DSD 1 bit data, 8 samples per byte. First sample in Least significant bit.
172 ASIOSTDSDInt8MSB1 = 33, // DSD 1 bit data, 8 samples per byte. First sample in Most significant bit.
173 ASIOSTDSDInt8NER8 = 40, // DSD 8 bit data, 1 sample per byte. No Endianness required.
174
175 ASIOSTLastEntry,
176}
177
178/// Gives information about buffers
179/// Receives pointers to buffers
180#[derive(Debug, Copy, Clone)]
181#[repr(C, packed(4))]
182pub struct AsioBufferInfo {
183 /// 0 for output 1 for input
184 pub is_input: i32,
185 /// Which channel. Starts at 0
186 pub channel_num: i32,
187 /// Pointer to each half of the double buffer.
188 pub buffers: [*mut c_void; 2],
189}
190
191/// Callbacks that ASIO calls
192#[repr(C, packed(4))]
193struct AsioCallbacks {
194 buffer_switch: extern "C" fn(double_buffer_index: c_long, direct_process: c_long) -> (),
195 sample_rate_did_change: extern "C" fn(s_rate: c_double) -> (),
196 asio_message: extern "C" fn(
197 selector: c_long,
198 value: c_long,
199 message: *mut (),
200 opt: *mut c_double,
201 ) -> c_long,
202 buffer_switch_time_info: extern "C" fn(
203 params: *mut ai::ASIOTime,
204 double_buffer_index: c_long,
205 direct_process: c_long,
206 ) -> *mut ai::ASIOTime,
207}
208
209static ASIO_CALLBACKS: AsioCallbacks = AsioCallbacks {
210 buffer_switch,
211 sample_rate_did_change,
212 asio_message,
213 buffer_switch_time_info,
214};
215
216/// All the possible types from ASIO.
217/// This is a direct copy of the asioMessage selectors
218/// inside ASIO SDK.
219#[rustfmt::skip]
220#[derive(Clone, Copy, Debug, FromPrimitive)]
221#[repr(C)]
222pub enum AsioMessageSelectors {
223 kAsioSelectorSupported = 1, // selector in <value>, returns 1L if supported,
224 // 0 otherwise
225 kAsioEngineVersion, // returns engine (host) asio implementation version,
226 // 2 or higher
227 kAsioResetRequest, // request driver reset. if accepted, this
228 // will close the driver (ASIO_Exit() ) and
229 // re-open it again (ASIO_Init() etc). some
230 // drivers need to reconfigure for instance
231 // when the sample rate changes, or some basic
232 // changes have been made in ASIO_ControlPanel().
233 // returns 1L; note the request is merely passed
234 // to the application, there is no way to determine
235 // if it gets accepted at this time (but it usually
236 // will be).
237 kAsioBufferSizeChange, // not yet supported, will currently always return 0L.
238 // for now, use kAsioResetRequest instead.
239 // once implemented, the new buffer size is expected
240 // in <value>, and on success returns 1L
241 kAsioResyncRequest, // the driver went out of sync, such that
242 // the timestamp is no longer valid. this
243 // is a request to re-start the engine and
244 // slave devices (sequencer). returns 1 for ok,
245 // 0 if not supported.
246 kAsioLatenciesChanged, // the drivers latencies have changed. The engine
247 // will refetch the latencies.
248 kAsioSupportsTimeInfo, // if host returns true here, it will expect the
249 // callback bufferSwitchTimeInfo to be called instead
250 // of bufferSwitch
251 kAsioSupportsTimeCode, //
252 kAsioMMCCommand, // unused - value: number of commands, message points to mmc commands
253 kAsioSupportsInputMonitor, // kAsioSupportsXXX return 1 if host supports this
254 kAsioSupportsInputGain, // unused and undefined
255 kAsioSupportsInputMeter, // unused and undefined
256 kAsioSupportsOutputGain, // unused and undefined
257 kAsioSupportsOutputMeter, // unused and undefined
258 kAsioOverload, // driver detected an overload
259 kAsioNumMessageSelectors, // sentinel value equal to the number of defined selectors
260}
261
262/// Events dispatched to registered driver event callbacks.
263#[derive(Clone, Copy, Debug)]
264pub enum AsioDriverEvent {
265 /// A message from the ASIO driver's `asioMessage` callback.
266 ///
267 /// `selector` identifies the message type; `value` is the raw payload passed by the driver.
268 /// For [`AsioMessageSelectors::kAsioSelectorSupported`] queries, `value` is the selector being
269 /// queried. Return `true` to advertise support for it, `false` to decline. For other selectors,
270 /// the return value is ignored.
271 Message {
272 selector: AsioMessageSelectors,
273 value: i32,
274 },
275
276 /// The ASIO driver reported a sample rate change.
277 ///
278 /// Only dispatched when the reported rate differs from the last known rate, so spurious
279 /// `sampleRateDidChange` calls (e.g. on AES/EBU sync status changes where the rate has not
280 /// actually changed) are suppressed.
281 SampleRateChanged(f64),
282}
283
284/// A rust-usable version of the `ASIOTime` type that does not contain a binary blob for fields.
285#[repr(C, packed(4))]
286pub struct AsioTime {
287 /// Must be `0`.
288 reserved: [i32; 4],
289 /// Required.
290 pub time_info: AsioTimeInfo,
291 /// Optional, evaluated if (time_code.flags & ktcValid).
292 pub time_code: AsioTimeCode,
293}
294
295/// A rust-compatible version of the `ASIOTimeInfo` type that does not contain a binary blob for
296/// fields.
297#[repr(C, packed(4))]
298pub struct AsioTimeInfo {
299 /// Absolute speed (1. = nominal).
300 pub speed: c_double,
301 /// System time related to sample_position, in nanoseconds.
302 ///
303 /// On Windows, must be derived from timeGetTime().
304 pub system_time: ai::ASIOTimeStamp,
305 /// Sample position since `ASIOStart()`.
306 pub sample_position: ai::ASIOSamples,
307 /// Current rate, unsigned.
308 pub sample_rate: AsioSampleRate,
309 /// See `AsioTimeInfoFlags`.
310 pub flags: i32,
311 /// Must be `0`.
312 reserved: [c_char; 12],
313}
314
315/// A rust-compatible version of the `ASIOTimeCode` type that does not use a binary blob for its
316/// fields.
317#[repr(C, packed(4))]
318pub struct AsioTimeCode {
319 /// Speed relation (fraction of nominal speed) optional.
320 ///
321 /// Set to 0. or 1. if not supported.
322 pub speed: c_double,
323 /// Time in samples unsigned.
324 pub time_code_samples: ai::ASIOSamples,
325 /// See `ASIOTimeCodeFlags`.
326 pub flags: i32,
327 /// Set to `0`.
328 future: [c_char; 64],
329}
330
331/// A rust-compatible version of the `ASIOSampleRate` type that does not use a binary blob for its
332/// fields.
333pub type AsioSampleRate = f64;
334
335// A helper type to simplify retrieval of available buffer sizes.
336#[derive(Default)]
337struct BufferSizes {
338 min: c_long,
339 max: c_long,
340 pref: c_long,
341 grans: c_long,
342}
343
344/// Identifies a buffer callback registered via [`Driver::add_callback`].
345#[derive(Clone, Copy, Debug, PartialEq, Eq)]
346pub struct BufferCallbackId(usize);
347
348/// A global way to access all the callbacks.
349///
350/// This is required because of how ASIO calls the `buffer_switch` function with no data
351/// parameters.
352static BUFFER_CALLBACK: Mutex<Vec<(BufferCallbackId, BufferCallback)>> = Mutex::new(Vec::new());
353
354/// Used to identify when to clear buffers.
355static CALLBACK_FLAG: AtomicU32 = AtomicU32::new(0);
356
357/// Indicates that ASIOOutputReady should be called
358static CALL_OUTPUT_READY: AtomicBool = AtomicBool::new(false);
359static CURRENT_SAMPLE_RATE: AtomicU64 = AtomicU64::new(0);
360
361/// Identifies a driver event callback registered via [`Driver::add_event_callback`].
362#[derive(Clone, Copy, Debug, PartialEq, Eq)]
363pub struct DriverEventCallbackId(usize);
364
365struct DriverEventCallback(Arc<dyn Fn(AsioDriverEvent) -> bool + Send + Sync>);
366
367/// A global registry for ASIO driver event callbacks.
368static DRIVER_EVENT_CALLBACKS: Mutex<Vec<(DriverEventCallbackId, DriverEventCallback)>> =
369 Mutex::new(Vec::new());
370
371impl Asio {
372 /// Initialise the ASIO API.
373 pub fn new() -> Self {
374 Self::default()
375 }
376
377 /// Returns the name for each available driver.
378 ///
379 /// This is used at the start to allow the user to choose which driver they want.
380 pub fn driver_names(&self) -> Vec<String> {
381 // The most drivers we can take
382 const MAX_DRIVERS: usize = 100;
383 // Max length for divers name
384 const MAX_DRIVER_NAME_LEN: usize = 32;
385
386 // 2D array of driver names set to 0.
387 let mut driver_names: [[c_char; MAX_DRIVER_NAME_LEN]; MAX_DRIVERS] =
388 [[0; MAX_DRIVER_NAME_LEN]; MAX_DRIVERS];
389 // Pointer to each driver name.
390 let mut driver_name_ptrs: [*mut i8; MAX_DRIVERS] = [null_mut(); MAX_DRIVERS];
391 for (ptr, name) in driver_name_ptrs.iter_mut().zip(&mut driver_names[..]) {
392 *ptr = (*name).as_mut_ptr();
393 }
394
395 unsafe {
396 let num_drivers =
397 ai::get_driver_names(driver_name_ptrs.as_mut_ptr(), MAX_DRIVERS as i32);
398 (0..num_drivers)
399 .map(|i| driver_name_to_utf8(&driver_names[i as usize]).to_string())
400 .collect()
401 }
402 }
403
404 /// If a driver has already been loaded, this will return that driver.
405 ///
406 /// Returns `None` if no driver is currently loaded.
407 ///
408 /// This can be useful to check before calling `load_driver` as ASIO only supports loading a
409 /// single driver at a time.
410 pub fn loaded_driver(&self) -> Option<Driver> {
411 self.loaded_driver
412 .lock()
413 .expect("failed to acquire loaded driver lock")
414 .upgrade()
415 .map(|inner| Driver { inner })
416 }
417
418 /// Load a driver from the given name.
419 ///
420 /// Driver names compatible with this method can be produced via the `asio.driver_names()`
421 /// method.
422 ///
423 /// NOTE: Despite many requests from users, ASIO only supports loading a single driver at a
424 /// time. Calling this method while a previously loaded `Driver` instance exists will result in
425 /// an error. That said, if this method is called with the name of a driver that has already
426 /// been loaded, that driver will be returned successfully.
427 pub fn load_driver(&self, driver_name: &str) -> Result<Driver, LoadDriverError> {
428 // Hold the lock for the entire operation to prevent a TOCTOU race where two threads
429 // both pass the "no driver loaded" check and then both call load_asio_driver.
430 let mut loaded = self
431 .loaded_driver
432 .lock()
433 .expect("failed to acquire loaded driver lock");
434
435 // Check whether or not a driver is already loaded.
436 if let Some(inner) = loaded.upgrade() {
437 let driver = Driver { inner };
438 if driver.name() == driver_name {
439 return Ok(driver);
440 } else {
441 return Err(LoadDriverError::DriverAlreadyExists);
442 }
443 }
444
445 // Make owned CString to send to load driver
446 let driver_name_cstring =
447 CString::new(driver_name).map_err(|_| LoadDriverError::LoadDriverFailed)?;
448 let mut driver_info = std::mem::MaybeUninit::<ai::ASIODriverInfo>::uninit();
449
450 unsafe {
451 match ai::load_asio_driver(driver_name_cstring.as_ptr() as *mut i8) {
452 false => Err(LoadDriverError::LoadDriverFailed),
453 true => {
454 // Initialize ASIO.
455 asio_result!(ai::ASIOInit(driver_info.as_mut_ptr()))?;
456 let _driver_info = driver_info.assume_init();
457 let mut rate: c_double = 0.0;
458 let _ = asio_result!(ai::get_sample_rate(&mut rate));
459 if rate > 0.0 {
460 CURRENT_SAMPLE_RATE.store(rate.to_bits(), Ordering::Release);
461 }
462 let state = Mutex::new(DriverState::Initialized);
463 let name = driver_name.to_string();
464 let destroyed = false;
465 let inner = Arc::new(DriverInner {
466 name,
467 state,
468 destroyed,
469 });
470 *loaded = Arc::downgrade(&inner);
471 let driver = Driver { inner };
472 Ok(driver)
473 }
474 }
475 }
476 }
477}
478
479impl BufferCallback {
480 /// Calls the inner callback.
481 fn run(&mut self, callback_info: &CallbackInfo) {
482 let cb = &mut self.0;
483 cb(callback_info);
484 }
485}
486
487impl Driver {
488 /// The name used to uniquely identify this driver.
489 pub fn name(&self) -> &str {
490 &self.inner.name
491 }
492
493 /// Returns the number of input and output channels available on the driver.
494 pub fn channels(&self) -> Result<Channels, AsioError> {
495 let _guard = self.inner.lock_state();
496 let mut ins: c_long = 0;
497 let mut outs: c_long = 0;
498 unsafe {
499 asio_result!(ai::ASIOGetChannels(&mut ins, &mut outs))?;
500 }
501 Ok(Channels { ins, outs })
502 }
503
504 /// Get the input and output hardware latency in frames.
505 pub fn latencies(&self) -> Result<Latencies, AsioError> {
506 let _guard = self.inner.lock_state();
507 let mut input_latency: c_long = 0;
508 let mut output_latency: c_long = 0;
509 unsafe {
510 asio_result!(ai::ASIOGetLatencies(
511 &mut input_latency,
512 &mut output_latency
513 ))?;
514 }
515 Ok(Latencies {
516 input: input_latency,
517 output: output_latency,
518 })
519 }
520
521 /// Get the min and max supported buffersize of the driver.
522 pub fn buffersize_range(&self) -> Result<BufferSizeRange, AsioError> {
523 let _guard = self.inner.lock_state();
524 let buffer_sizes = asio_get_buffer_sizes()?;
525 Ok(BufferSizeRange {
526 min: buffer_sizes.min,
527 max: buffer_sizes.max,
528 })
529 }
530
531 /// Get current sample rate of the driver.
532 pub fn sample_rate(&self) -> Result<f64, AsioError> {
533 let _guard = self.inner.lock_state();
534 let mut rate: c_double = 0.0;
535 unsafe {
536 asio_result!(ai::get_sample_rate(&mut rate))?;
537 }
538 Ok(rate)
539 }
540
541 /// Can the driver accept the given sample rate.
542 pub fn can_sample_rate(&self, sample_rate: f64) -> Result<bool, AsioError> {
543 let _guard = self.inner.lock_state();
544 unsafe {
545 match asio_result!(ai::can_sample_rate(sample_rate)) {
546 Ok(()) => Ok(true),
547 Err(AsioError::NoRate) => Ok(false),
548 Err(err) => Err(err),
549 }
550 }
551 }
552
553 /// Set the sample rate for the driver.
554 pub fn set_sample_rate(&self, sample_rate: f64) -> Result<(), AsioError> {
555 let actual = {
556 let _guard = self.inner.lock_state();
557 unsafe { asio_result!(ai::set_sample_rate(sample_rate))? };
558 let mut actual: c_double = 0.0;
559 unsafe { asio_result!(ai::get_sample_rate(&mut actual))? };
560 actual
561 };
562
563 // Check whether the driver applied the rate immediately.
564 if (actual - sample_rate).abs() < 1.0 {
565 CURRENT_SAMPLE_RATE.store(actual.to_bits(), Ordering::Release);
566 return Ok(());
567 }
568
569 // Some ASIO drivers (e.g. Steinberg) do not apply a rate change until after a
570 // complete buffer-creation cycle (CreateBuffers -> Start -> Stop -> DisposeBuffers),
571 // followed by a full driver teardown and reload.
572 let mut dummy_infos = prepare_buffer_infos(false, 1);
573 let buffer_size = self.create_buffers(&mut dummy_infos, None)?;
574
575 // Start briefly so the driver reconfigures its hardware clock.
576 self.start()?;
577
578 // Wait for one full buffer to be processed: this guarantees the driver has
579 // applied the rate change to the hardware clock before we stop it.
580 let buffer_duration = Duration::from_secs_f64(buffer_size as f64 / sample_rate);
581 std::thread::sleep(buffer_duration);
582
583 self.stop()?;
584 self.dispose_buffers()?;
585
586 // Full teardown so the driver is reset to a clean state. Some drivers
587 // (e.g. Steinberg) return errors from ASIOGetChannels after DisposeBuffers
588 // unless the driver is fully exited and reloaded.
589 {
590 let mut state = self.inner.lock_state();
591 unsafe {
592 let _ = asio_result!(ai::ASIOExit());
593 ai::remove_current_driver();
594 }
595 std::thread::sleep(buffer_duration);
596
597 // Safety: the name was validated as null-free when the driver was first loaded.
598 let name_cstring = CString::new(self.inner.name.as_str())
599 .expect("driver name already stored must not contain null bytes");
600 unsafe {
601 if !ai::load_asio_driver(name_cstring.as_ptr() as *mut i8) {
602 return Err(AsioError::NoDrivers);
603 }
604 let mut driver_info = std::mem::MaybeUninit::<ai::ASIODriverInfo>::uninit();
605 asio_result!(ai::ASIOInit(driver_info.as_mut_ptr()))?;
606 }
607 *state = DriverState::Initialized;
608
609 // Set the rate again on the freshly initialized driver.
610 unsafe { asio_result!(ai::set_sample_rate(sample_rate))? };
611
612 let mut actual: c_double = 0.0;
613 unsafe { asio_result!(ai::get_sample_rate(&mut actual))? };
614 if (actual - sample_rate).abs() >= 1.0 {
615 return Err(AsioError::NoRate);
616 }
617
618 CURRENT_SAMPLE_RATE.store(actual.to_bits(), Ordering::Release);
619 }
620 Ok(())
621 }
622
623 /// Get the current data type of the driver's input stream.
624 ///
625 /// This queries a single channel's type assuming all channels have the same sample type.
626 pub fn input_data_type(&self) -> Result<AsioSampleType, AsioError> {
627 let _guard = self.inner.lock_state();
628 stream_data_type(true)
629 }
630
631 /// Get the current data type of the driver's output stream.
632 ///
633 /// This queries a single channel's type assuming all channels have the same sample type.
634 pub fn output_data_type(&self) -> Result<AsioSampleType, AsioError> {
635 let _guard = self.inner.lock_state();
636 stream_data_type(false)
637 }
638
639 /// Ask ASIO to allocate the buffers and give the callback pointers.
640 ///
641 /// This will destroy any already allocated buffers.
642 ///
643 /// If buffersize is None then the preferred buffer size from ASIO is used,
644 /// otherwise the desired buffersize is used if the requested size is within
645 /// the range of accepted buffersizes for the device.
646 fn create_buffers(
647 &self,
648 buffer_infos: &mut [AsioBufferInfo],
649 buffer_size: Option<i32>,
650 ) -> Result<c_long, AsioError> {
651 let num_channels = buffer_infos.len();
652
653 let mut state = self.inner.lock_state();
654
655 // Retrieve the available buffer sizes.
656 let buffer_sizes = asio_get_buffer_sizes()?;
657 if buffer_sizes.pref <= 0 {
658 panic!(
659 "`ASIOGetBufferSize` produced unusable preferred buffer size of {}",
660 buffer_sizes.pref,
661 );
662 }
663
664 let buffer_size = match buffer_size {
665 Some(v) => {
666 if v <= buffer_sizes.max {
667 v
668 } else {
669 return Err(AsioError::InvalidBufferSize);
670 }
671 }
672 None => buffer_sizes.pref,
673 };
674
675 CALL_OUTPUT_READY.store(
676 asio_result!(unsafe { ai::ASIOOutputReady() }).is_ok(),
677 Ordering::Release,
678 );
679
680 // Ensure the driver is in the `Initialized` state.
681 if let DriverState::Running = *state {
682 state.stop()?;
683 }
684 if let DriverState::Prepared = *state {
685 state.dispose_buffers()?;
686 }
687 unsafe {
688 asio_result!(ai::ASIOCreateBuffers(
689 buffer_infos.as_mut_ptr() as *mut _,
690 num_channels as i32,
691 buffer_size,
692 &ASIO_CALLBACKS as *const _ as *mut _,
693 ))?;
694 }
695 *state = DriverState::Prepared;
696
697 Ok(buffer_size)
698 }
699
700 /// Creates the streams.
701 ///
702 /// `buffer_size` sets the desired buffer_size. If None is passed in, then the
703 /// default buffersize for the device is used.
704 ///
705 /// Both input and output streams need to be created together as a single slice of
706 /// `ASIOBufferInfo`.
707 fn create_streams(
708 &self,
709 mut input_buffer_infos: Vec<AsioBufferInfo>,
710 mut output_buffer_infos: Vec<AsioBufferInfo>,
711 buffer_size: Option<i32>,
712 ) -> Result<AsioStreams, AsioError> {
713 let (input, output) = match (
714 input_buffer_infos.is_empty(),
715 output_buffer_infos.is_empty(),
716 ) {
717 // Both stream exist.
718 (false, false) => {
719 // Create one continuous slice of buffers.
720 let split_point = input_buffer_infos.len();
721 let mut all_buffer_infos = input_buffer_infos;
722 all_buffer_infos.append(&mut output_buffer_infos);
723 // Create the buffers. On success, split the output and input again.
724 let buffer_size = self.create_buffers(&mut all_buffer_infos, buffer_size)?;
725 let output_buffer_infos = all_buffer_infos.split_off(split_point);
726 let input_buffer_infos = all_buffer_infos;
727 let input = Some(AsioStream {
728 buffer_infos: input_buffer_infos,
729 buffer_size,
730 });
731 let output = Some(AsioStream {
732 buffer_infos: output_buffer_infos,
733 buffer_size,
734 });
735 (input, output)
736 }
737 // Just input
738 (false, true) => {
739 let buffer_size = self.create_buffers(&mut input_buffer_infos, buffer_size)?;
740 let input = Some(AsioStream {
741 buffer_infos: input_buffer_infos,
742 buffer_size,
743 });
744 let output = None;
745 (input, output)
746 }
747 // Just output
748 (true, false) => {
749 let buffer_size = self.create_buffers(&mut output_buffer_infos, buffer_size)?;
750 let input = None;
751 let output = Some(AsioStream {
752 buffer_infos: output_buffer_infos,
753 buffer_size,
754 });
755 (input, output)
756 }
757 // Impossible
758 (true, true) => unreachable!("Trying to create streams without preparing"),
759 };
760 Ok(AsioStreams { input, output })
761 }
762
763 /// Prepare the input stream.
764 ///
765 /// Because only the latest call to ASIOCreateBuffers is relevant this call will destroy all
766 /// past active buffers and recreate them.
767 ///
768 /// For this reason we take the output stream if it exists.
769 ///
770 /// `num_channels` is the desired number of input channels.
771 ///
772 /// `buffer_size` sets the desired buffer_size. If None is passed in, then the
773 /// default buffersize for the device is used.
774 ///
775 /// This returns a full AsioStreams with both input and output if output was active.
776 pub fn prepare_input_stream(
777 &self,
778 output: Option<AsioStream>,
779 num_channels: usize,
780 buffer_size: Option<i32>,
781 ) -> Result<AsioStreams, AsioError> {
782 let input_buffer_infos = prepare_buffer_infos(true, num_channels);
783 let output_buffer_infos = output.map(|output| output.buffer_infos).unwrap_or_default();
784 self.create_streams(input_buffer_infos, output_buffer_infos, buffer_size)
785 }
786
787 /// Prepare the output stream.
788 ///
789 /// Because only the latest call to ASIOCreateBuffers is relevant this call will destroy all
790 /// past active buffers and recreate them.
791 ///
792 /// For this reason we take the input stream if it exists.
793 ///
794 /// `num_channels` is the desired number of output channels.
795 ///
796 /// `buffer_size` sets the desired buffer_size. If None is passed in, then the
797 /// default buffersize for the device is used.
798 ///
799 /// This returns a full AsioStreams with both input and output if input was active.
800 pub fn prepare_output_stream(
801 &self,
802 input: Option<AsioStream>,
803 num_channels: usize,
804 buffer_size: Option<i32>,
805 ) -> Result<AsioStreams, AsioError> {
806 let input_buffer_infos = input.map(|input| input.buffer_infos).unwrap_or_default();
807 let output_buffer_infos = prepare_buffer_infos(false, num_channels);
808 self.create_streams(input_buffer_infos, output_buffer_infos, buffer_size)
809 }
810
811 /// Releases buffers allocations.
812 ///
813 /// This will `stop` the stream if the driver is `Running`.
814 ///
815 /// No-op if no buffers are allocated.
816 pub fn dispose_buffers(&self) -> Result<(), AsioError> {
817 self.inner.dispose_buffers_inner()
818 }
819
820 /// Starts ASIO streams playing.
821 ///
822 /// The driver must be in the `Prepared` state
823 ///
824 /// If called successfully, the driver will be in the `Running` state.
825 ///
826 /// No-op if already `Running`.
827 pub fn start(&self) -> Result<(), AsioError> {
828 let mut state = self.inner.lock_state();
829 if let DriverState::Running = *state {
830 return Ok(());
831 }
832 unsafe {
833 asio_result!(ai::ASIOStart())?;
834 }
835 *state = DriverState::Running;
836 Ok(())
837 }
838
839 /// Stops ASIO streams playing.
840 ///
841 /// No-op if the state is not `Running`.
842 ///
843 /// If the state was `Running` and the stream is stopped successfully, the driver will be in
844 /// the `Prepared` state.
845 pub fn stop(&self) -> Result<(), AsioError> {
846 self.inner.stop_inner()
847 }
848
849 /// Adds a callback to the list of active callbacks.
850 ///
851 /// The given function receives the index of the buffer currently ready for processing.
852 ///
853 /// Returns an ID uniquely associated with the given callback so that it may be removed later.
854 pub fn add_callback<F>(&self, callback: F) -> BufferCallbackId
855 where
856 F: 'static + FnMut(&CallbackInfo) + Send,
857 {
858 let mut bc = BUFFER_CALLBACK.lock().unwrap();
859 let id = bc
860 .last()
861 .map(|&(id, _)| BufferCallbackId(id.0.checked_add(1).expect("stream ID overflowed")))
862 .unwrap_or(BufferCallbackId(0));
863 let cb = BufferCallback(Box::new(callback));
864 bc.push((id, cb));
865 id
866 }
867
868 /// Remove the callback with the given ID.
869 pub fn remove_callback(&self, rem_id: BufferCallbackId) {
870 let mut bc = BUFFER_CALLBACK.lock().unwrap();
871 bc.retain(|&(id, _)| id != rem_id);
872 }
873
874 /// Consumes and destroys the `Driver`, stopping the streams if they are running and releasing
875 /// any associated resources.
876 ///
877 /// Returns `Ok(true)` if the driver was successfully destroyed.
878 ///
879 /// Returns `Ok(false)` if the driver was not destroyed because another handle to the driver
880 /// still exists.
881 ///
882 /// Returns `Err` if some switching driver states failed or if ASIO returned an error on exit.
883 pub fn destroy(self) -> Result<bool, AsioError> {
884 let Driver { inner } = self;
885 match Arc::try_unwrap(inner) {
886 Err(_) => Ok(false),
887 Ok(mut inner) => {
888 inner.destroy_inner()?;
889 Ok(true)
890 }
891 }
892 }
893
894 /// Register a callback to receive ASIO driver events.
895 ///
896 /// The callback receives an [`AsioDriverEvent`] and returns a `bool`. The return value is
897 /// meaningful only for [`AsioDriverEvent::Message`] with selector
898 /// [`AsioMessageSelectors::kAsioSelectorSupported`]: return `true` to advertise support for
899 /// the queried selector, `false` to decline. For all other events the return value is ignored.
900 ///
901 /// Returns an ID uniquely associated with the given callback so that it may be removed later.
902 pub fn add_event_callback<F>(&self, callback: F) -> DriverEventCallbackId
903 where
904 F: Fn(AsioDriverEvent) -> bool + Send + Sync + 'static,
905 {
906 let mut dcb = DRIVER_EVENT_CALLBACKS.lock().unwrap();
907 let id = dcb
908 .last()
909 .map(|&(id, _)| {
910 DriverEventCallbackId(
911 id.0.checked_add(1)
912 .expect("DriverEventCallbackId overflowed"),
913 )
914 })
915 .unwrap_or(DriverEventCallbackId(0));
916
917 let cb = DriverEventCallback(Arc::new(callback));
918 dcb.push((id, cb));
919 id
920 }
921
922 /// Remove the event callback with the given ID.
923 pub fn remove_event_callback(&self, rem_id: DriverEventCallbackId) {
924 let mut dcb = DRIVER_EVENT_CALLBACKS.lock().unwrap();
925 dcb.retain(|&(id, _)| id != rem_id);
926 }
927}
928
929impl DriverState {
930 fn stop(&mut self) -> Result<(), AsioError> {
931 if let DriverState::Running = *self {
932 unsafe {
933 asio_result!(ai::ASIOStop())?;
934 }
935 *self = DriverState::Prepared;
936 }
937 Ok(())
938 }
939
940 fn dispose_buffers(&mut self) -> Result<(), AsioError> {
941 if let DriverState::Initialized = *self {
942 return Ok(());
943 }
944 if let DriverState::Running = *self {
945 self.stop()?;
946 }
947 unsafe {
948 asio_result!(ai::ASIODisposeBuffers())?;
949 }
950 *self = DriverState::Initialized;
951 Ok(())
952 }
953
954 fn destroy(&mut self) -> Result<(), AsioError> {
955 if let DriverState::Running = *self {
956 self.stop()?;
957 }
958 if let DriverState::Prepared = *self {
959 self.dispose_buffers()?;
960 }
961 unsafe {
962 asio_result!(ai::ASIOExit())?;
963 ai::remove_current_driver();
964 }
965 Ok(())
966 }
967}
968
969impl DriverInner {
970 fn lock_state(&self) -> MutexGuard<'_, DriverState> {
971 self.state.lock().expect("failed to lock `DriverState`")
972 }
973
974 fn stop_inner(&self) -> Result<(), AsioError> {
975 let mut state = self.lock_state();
976 state.stop()
977 }
978
979 fn dispose_buffers_inner(&self) -> Result<(), AsioError> {
980 let mut state = self.lock_state();
981 state.dispose_buffers()
982 }
983
984 fn destroy_inner(&mut self) -> Result<(), AsioError> {
985 {
986 let mut state = self.lock_state();
987 state.destroy()?;
988
989 // Clear any existing stream callbacks.
990 if let Ok(mut bcs) = BUFFER_CALLBACK.lock() {
991 bcs.clear();
992 }
993 }
994
995 // Signal that the driver has been destroyed.
996 self.destroyed = true;
997
998 Ok(())
999 }
1000}
1001
1002impl Drop for DriverInner {
1003 fn drop(&mut self) {
1004 if !self.destroyed {
1005 // We probably shouldn't `panic!` in the destructor? We also shouldn't ignore errors
1006 // though either.
1007 self.destroy_inner().ok();
1008 }
1009 }
1010}
1011
1012unsafe impl Send for AsioStream {}
1013
1014/// Used by the input and output stream creation process.
1015fn prepare_buffer_infos(is_input: bool, n_channels: usize) -> Vec<AsioBufferInfo> {
1016 let is_input = if is_input { 1 } else { 0 };
1017 (0..n_channels)
1018 .map(|ch| AsioBufferInfo {
1019 is_input,
1020 channel_num: ch as i32,
1021 // To be filled by ASIOCreateBuffers.
1022 buffers: [std::ptr::null_mut(); 2],
1023 })
1024 .collect()
1025}
1026
1027/// Retrieve the minimum, maximum and preferred buffer sizes along with the available
1028/// buffer size granularity.
1029fn asio_get_buffer_sizes() -> Result<BufferSizes, AsioError> {
1030 let mut b = BufferSizes::default();
1031 unsafe {
1032 let res = ai::ASIOGetBufferSize(&mut b.min, &mut b.max, &mut b.pref, &mut b.grans);
1033 asio_result!(res)?;
1034 }
1035 Ok(b)
1036}
1037
1038/// Retrieve the `ASIOChannelInfo` associated with the channel at the given index on either the
1039/// input or output stream (`true` for input).
1040fn asio_channel_info(channel: c_long, is_input: bool) -> Result<ai::ASIOChannelInfo, AsioError> {
1041 let mut channel_info = ai::ASIOChannelInfo {
1042 // Which channel we are querying
1043 channel,
1044 // Was it input or output
1045 isInput: if is_input { 1 } else { 0 },
1046 // Was it active
1047 isActive: 0,
1048 channelGroup: 0,
1049 // The sample type
1050 type_: 0,
1051 name: [0 as c_char; 32],
1052 };
1053 unsafe {
1054 asio_result!(ai::ASIOGetChannelInfo(&mut channel_info))?;
1055 Ok(channel_info)
1056 }
1057}
1058
1059/// Retrieve the data type of either the input or output stream.
1060///
1061/// If `is_input` is true, this will be queried on the input stream.
1062fn stream_data_type(is_input: bool) -> Result<AsioSampleType, AsioError> {
1063 let channel_info = asio_channel_info(0, is_input)?;
1064 Ok(FromPrimitive::from_i32(channel_info.type_).expect("unknown `ASIOSampletype` value"))
1065}
1066
1067/// ASIO uses null terminated c strings for driver names.
1068///
1069/// This converts to utf8.
1070fn driver_name_to_utf8(bytes: &[c_char]) -> std::borrow::Cow<'_, str> {
1071 unsafe { CStr::from_ptr(bytes.as_ptr()).to_string_lossy() }
1072}
1073
1074/// Convert an `ASIOTimeStamp` (high and low 32-bit halves) to a `u64` nanosecond value.
1075#[inline]
1076fn asio_timestamp_to_nanos(ts: ai::ASIOTimeStamp) -> u64 {
1077 (ts.hi as u64) << 32 | ts.lo as u64
1078}
1079
1080/// Indicates the stream sample rate has changed.
1081extern "C" fn sample_rate_did_change(s_rate: c_double) {
1082 let old_bits = CURRENT_SAMPLE_RATE.load(Ordering::Acquire);
1083 if s_rate.to_bits() != old_bits {
1084 CURRENT_SAMPLE_RATE.store(s_rate.to_bits(), Ordering::Release);
1085 dispatch_event(AsioDriverEvent::SampleRateChanged(s_rate));
1086 }
1087}
1088
1089const ASIO_VERSION: c_long = 2;
1090
1091/// Dispatch `event` to all registered driver event callbacks.
1092///
1093/// Returns `true` if any callback returns `true`. All callbacks are always called so that
1094/// notification side-effects (e.g. stream invalidation) reach every registered listener.
1095fn dispatch_event(event: AsioDriverEvent) -> bool {
1096 let callbacks: Vec<_> = {
1097 let lock = DRIVER_EVENT_CALLBACKS.lock().unwrap();
1098 lock.iter().map(|(_, cb)| cb.0.clone()).collect()
1099 };
1100 callbacks
1101 .iter()
1102 .fold(false, |handled, cb| cb(event) || handled)
1103}
1104
1105/// Message callback for ASIO to notify of certain events.
1106extern "C" fn asio_message(
1107 selector: c_long,
1108 value: c_long,
1109 _message: *mut (),
1110 _opt: *mut c_double,
1111) -> c_long {
1112 match AsioMessageSelectors::from_i64(selector as i64) {
1113 Some(AsioMessageSelectors::kAsioSelectorSupported) => {
1114 // For selectors that asio-sys itself always handles, advertise support
1115 // unconditionally. For all others, delegate to registered callbacks so
1116 // each host can opt-in.
1117 match AsioMessageSelectors::from_i64(value as i64) {
1118 Some(AsioMessageSelectors::kAsioSelectorSupported)
1119 | Some(AsioMessageSelectors::kAsioResetRequest)
1120 | Some(AsioMessageSelectors::kAsioEngineVersion)
1121 | Some(AsioMessageSelectors::kAsioResyncRequest)
1122 | Some(AsioMessageSelectors::kAsioLatenciesChanged)
1123 | Some(AsioMessageSelectors::kAsioSupportsTimeInfo) => true as c_long,
1124 _ => dispatch_event(AsioDriverEvent::Message {
1125 selector: AsioMessageSelectors::kAsioSelectorSupported,
1126 value,
1127 }) as c_long,
1128 }
1129 }
1130
1131 Some(AsioMessageSelectors::kAsioResetRequest) => {
1132 // The driver requests a full teardown and reinitialisation. Cannot be performed
1133 // here as this callback is invoked from within the driver; notify the host to
1134 // defer the reset to a safe point.
1135 dispatch_event(AsioDriverEvent::Message {
1136 selector: AsioMessageSelectors::kAsioResetRequest,
1137 value,
1138 });
1139 true as c_long
1140 }
1141
1142 Some(AsioMessageSelectors::kAsioResyncRequest) => {
1143 // The driver encountered non-fatal data loss (e.g. a timestamp discontinuity).
1144 // Notify the host so it can handle the gap appropriately.
1145 dispatch_event(AsioDriverEvent::Message {
1146 selector: AsioMessageSelectors::kAsioResyncRequest,
1147 value,
1148 });
1149 true as c_long
1150 }
1151
1152 Some(AsioMessageSelectors::kAsioLatenciesChanged) => {
1153 // The driver latencies have changed; have them re-queried.
1154 dispatch_event(AsioDriverEvent::Message {
1155 selector: AsioMessageSelectors::kAsioLatenciesChanged,
1156 value,
1157 });
1158 true as c_long
1159 }
1160
1161 Some(AsioMessageSelectors::kAsioEngineVersion) => {
1162 // Return the supported ASIO version of the host application. If a host application
1163 // does not implement this selector, ASIO 1.0 is assumed by the driver.
1164 ASIO_VERSION
1165 }
1166
1167 Some(AsioMessageSelectors::kAsioSupportsTimeInfo) => {
1168 // Informs the driver whether the asioCallbacks.bufferSwitchTimeInfo() callback is
1169 // supported. For compatibility with ASIO 1.0 drivers the host application should
1170 // always support the "old" bufferSwitch method, too, which we do.
1171 true as c_long
1172 }
1173
1174 // For all other selectors, delegate to registered callbacks.
1175 Some(other) => dispatch_event(AsioDriverEvent::Message {
1176 selector: other,
1177 value,
1178 }) as c_long,
1179
1180 None => false as c_long, // Unrecognised selector.
1181 }
1182}
1183
1184/// Similar to buffer switch but with time info.
1185///
1186/// If only `buffer_switch` is called by the driver instead, the `buffer_switch` callback will
1187/// create the necessary timing info and call this function.
1188///
1189/// TODO: Provide some access to `ai::ASIOTime` once CPAL gains support for time stamps.
1190extern "C" fn buffer_switch_time_info(
1191 time: *mut ai::ASIOTime,
1192 double_buffer_index: c_long,
1193 _direct_process: c_long,
1194) -> *mut ai::ASIOTime {
1195 // This lock is probably unavoidable, but locks in the audio stream are not great.
1196 let mut bcs = BUFFER_CALLBACK.lock().unwrap();
1197 let asio_time: &mut AsioTime = unsafe { &mut *(time as *mut AsioTime) };
1198 // Alternates: 0, 1, 0, 1, ...
1199 let callback_flag = CALLBACK_FLAG.fetch_xor(1, Ordering::Relaxed);
1200
1201 let callback_info = CallbackInfo {
1202 buffer_index: double_buffer_index,
1203 system_time: asio_timestamp_to_nanos(asio_time.time_info.system_time),
1204 callback_flag,
1205 };
1206 for &mut (_, ref mut bc) in bcs.iter_mut() {
1207 bc.run(&callback_info);
1208 }
1209
1210 if CALL_OUTPUT_READY.load(Ordering::Acquire) {
1211 unsafe { ai::ASIOOutputReady() };
1212 }
1213
1214 time
1215}
1216
1217/// This is called by ASIO.
1218///
1219/// Here we run the callback for each stream.
1220///
1221/// `double_buffer_index` is either `0` or `1` indicating which buffer to fill.
1222extern "C" fn buffer_switch(double_buffer_index: c_long, direct_process: c_long) {
1223 // Emulate the time info provided by the `buffer_switch_time_info` callback.
1224 // This is an attempt at matching the behaviour in `hostsample.cpp` from the SDK.
1225 let mut time = unsafe {
1226 let mut time: AsioTime = std::mem::zeroed();
1227 let res = ai::ASIOGetSamplePosition(
1228 &mut time.time_info.sample_position,
1229 &mut time.time_info.system_time,
1230 );
1231 if let Ok(()) = asio_result!(res) {
1232 time.time_info.flags = (ai::AsioTimeInfoFlags::kSystemTimeValid
1233 | ai::AsioTimeInfoFlags::kSamplePositionValid)
1234 // Context about the cast:
1235 //
1236 // Cast was required to successfully compile with MinGW-w64.
1237 //
1238 // The flags defined will not create a value that exceeds the maximum value of an i32.
1239 // The flags are intended to be non-negative, so the sign bit will not be used.
1240 // The c_uint (flags) is being cast to i32 which is safe as long as the actual value fits within the i32 range, which is true in this case.
1241 //
1242 // The actual flags in asio sdk are defined as:
1243 // typedef enum AsioTimeInfoFlags
1244 // {
1245 // kSystemTimeValid = 1, // must always be valid
1246 // kSamplePositionValid = 1 << 1, // must always be valid
1247 // kSampleRateValid = 1 << 2,
1248 // kSpeedValid = 1 << 3,
1249 //
1250 // kSampleRateChanged = 1 << 4,
1251 // kClockSourceChanged = 1 << 5
1252 // } AsioTimeInfoFlags;
1253 .0 as _;
1254 }
1255 time
1256 };
1257
1258 // Actual processing happens within the `buffer_switch_time_info` callback.
1259 let asio_time_ptr = &mut time as *mut AsioTime as *mut ai::ASIOTime;
1260 buffer_switch_time_info(asio_time_ptr, double_buffer_index, direct_process);
1261}
1262
1263#[test]
1264fn check_type_sizes() {
1265 assert_eq!(
1266 std::mem::size_of::<AsioSampleRate>(),
1267 std::mem::size_of::<ai::ASIOSampleRate>()
1268 );
1269 assert_eq!(
1270 std::mem::size_of::<AsioTimeCode>(),
1271 std::mem::size_of::<ai::ASIOTimeCode>()
1272 );
1273 assert_eq!(
1274 std::mem::size_of::<AsioTimeInfo>(),
1275 std::mem::size_of::<ai::AsioTimeInfo>(),
1276 );
1277 assert_eq!(
1278 std::mem::size_of::<AsioTime>(),
1279 std::mem::size_of::<ai::ASIOTime>()
1280 );
1281}