Skip to main content

asio_sys/bindings/
mod.rs

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