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}