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