1//! Platform-specific items.
2//!
3//! This module also contains the implementation of the platform's dynamically dispatched [`Host`]
4//! type and its associated [`Device`], [`Stream`] and other associated types. These
5//! types are useful in the case that users require switching between audio host APIs at runtime.
6
7#[doc(inline)]
8pub use self::platform_impl::*;
9
10/// A macro to assist with implementing a platform's dynamically dispatched [`Host`] type.
11///
12/// These dynamically dispatched types are necessary to allow for users to switch between hosts at
13/// runtime.
14///
15/// For example the invocation `impl_platform_host(Wasapi wasapi "WASAPI", Asio asio "ASIO")`,
16/// this macro should expand to:
17///
18// This sample code block is marked as text because it's not a valid test,
19// it's just illustrative. (see rust issue #96573)
20/// ```text
21/// pub enum HostId {
22/// Wasapi,
23/// Asio,
24/// }
25///
26/// pub enum Host {
27/// Wasapi(crate::host::wasapi::Host),
28/// Asio(crate::host::asio::Host),
29/// }
30/// ```
31///
32/// And so on for Device, Devices, Host, Stream, SupportedInputConfigs,
33/// SupportedOutputConfigs and all their necessary trait implementations.
34///
35macro_rules! impl_platform_host {
36 ($($(#[cfg($feat: meta)])? $HostVariant:ident $host_mod:ident $host_name:literal),*) => {
37 /// All hosts supported by CPAL on this platform.
38 pub const ALL_HOSTS: &'static [HostId] = &[
39 $(
40 $(#[cfg($feat)])?
41 HostId::$HostVariant,
42 )*
43 ];
44
45 /// The platform's dynamically dispatched `Host` type.
46 ///
47 /// An instance of this `Host` type may represent one of the `Host`s available
48 /// on the platform.
49 ///
50 /// Use this type if you require switching between available hosts at runtime.
51 ///
52 /// This type may be constructed via the [`host_from_id`] function. [`HostId`]s may
53 /// be acquired via the [`ALL_HOSTS`] const, and the [`available_hosts`] function.
54 pub struct Host(HostInner);
55
56 /// The `Device` implementation associated with the platform's dynamically dispatched
57 /// [`Host`] type.
58 #[derive(Clone)]
59 pub struct Device(DeviceInner);
60
61 /// The `Devices` iterator associated with the platform's dynamically dispatched [`Host`]
62 /// type.
63 pub struct Devices(DevicesInner);
64
65 /// The `Stream` implementation associated with the platform's dynamically dispatched
66 /// [`Host`] type.
67 // Streams cannot be `Send` or `Sync` if we plan to support Android's AAudio API. This is
68 // because the stream API is not thread-safe, and the API prohibits calling certain
69 // functions within the callback.
70 //
71 // TODO: Confirm this and add more specific detail and references.
72 #[must_use = "If the stream is not stored it will not play."]
73 pub struct Stream(StreamInner, crate::platform::NotSendSyncAcrossAllPlatforms);
74
75 /// The `SupportedInputConfigs` iterator associated with the platform's dynamically
76 /// dispatched [`Host`] type.
77 pub struct SupportedInputConfigs(SupportedInputConfigsInner);
78
79 /// The `SupportedOutputConfigs` iterator associated with the platform's dynamically
80 /// dispatched [`Host`] type.
81 pub struct SupportedOutputConfigs(SupportedOutputConfigsInner);
82
83 /// Unique identifier for available hosts on the platform.
84 #[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
85 pub enum HostId {
86 $(
87 $(#[cfg($feat)])?
88 $HostVariant,
89 )*
90 }
91
92 /// Contains a platform specific [`Device`] implementation.
93 #[derive(Clone)]
94 pub enum DeviceInner {
95 $(
96 $(#[cfg($feat)])?
97 $HostVariant(crate::host::$host_mod::Device),
98 )*
99 }
100
101 /// Contains a platform specific [`Devices`] implementation.
102 pub enum DevicesInner {
103 $(
104 $(#[cfg($feat)])?
105 $HostVariant(crate::host::$host_mod::Devices),
106 )*
107 }
108
109 /// Contains a platform specific [`Host`] implementation.
110 pub enum HostInner {
111 $(
112 $(#[cfg($feat)])?
113 $HostVariant(crate::host::$host_mod::Host),
114 )*
115 }
116
117 /// Contains a platform specific [`Stream`] implementation.
118 pub enum StreamInner {
119 $(
120 $(#[cfg($feat)])?
121 $HostVariant(crate::host::$host_mod::Stream),
122 )*
123 }
124
125 enum SupportedInputConfigsInner {
126 $(
127 $(#[cfg($feat)])?
128 $HostVariant(crate::host::$host_mod::SupportedInputConfigs),
129 )*
130 }
131
132 enum SupportedOutputConfigsInner {
133 $(
134 $(#[cfg($feat)])?
135 $HostVariant(crate::host::$host_mod::SupportedOutputConfigs),
136 )*
137 }
138
139 impl HostId {
140 pub fn name(&self) -> &'static str {
141 match self {
142 $(
143 $(#[cfg($feat)])?
144 HostId::$HostVariant => $host_name,
145 )*
146 }
147 }
148 }
149
150 impl Devices {
151 /// Returns a reference to the underlying platform specific implementation of this
152 /// `Devices`.
153 pub fn as_inner(&self) -> &DevicesInner {
154 &self.0
155 }
156
157 /// Returns a mutable reference to the underlying platform specific implementation of
158 /// this `Devices`.
159 pub fn as_inner_mut(&mut self) -> &mut DevicesInner {
160 &mut self.0
161 }
162
163 /// Returns the underlying platform specific implementation of this `Devices`.
164 pub fn into_inner(self) -> DevicesInner {
165 self.0
166 }
167 }
168
169 impl Device {
170 /// Returns a reference to the underlying platform specific implementation of this
171 /// `Device`.
172 pub fn as_inner(&self) -> &DeviceInner {
173 &self.0
174 }
175
176 /// Returns a mutable reference to the underlying platform specific implementation of
177 /// this `Device`.
178 pub fn as_inner_mut(&mut self) -> &mut DeviceInner {
179 &mut self.0
180 }
181
182 /// Returns the underlying platform specific implementation of this `Device`.
183 pub fn into_inner(self) -> DeviceInner {
184 self.0
185 }
186 }
187
188 impl Host {
189 /// The unique identifier associated with this `Host`.
190 pub fn id(&self) -> HostId {
191 match self.0 {
192 $(
193 $(#[cfg($feat)])?
194 HostInner::$HostVariant(_) => HostId::$HostVariant,
195 )*
196 }
197 }
198
199 /// Returns a reference to the underlying platform specific implementation of this
200 /// `Host`.
201 pub fn as_inner(&self) -> &HostInner {
202 &self.0
203 }
204
205 /// Returns a mutable reference to the underlying platform specific implementation of
206 /// this `Host`.
207 pub fn as_inner_mut(&mut self) -> &mut HostInner {
208 &mut self.0
209 }
210
211 /// Returns the underlying platform specific implementation of this `Host`.
212 pub fn into_inner(self) -> HostInner {
213 self.0
214 }
215 }
216
217 impl Stream {
218 /// Returns a reference to the underlying platform specific implementation of this
219 /// `Stream`.
220 pub fn as_inner(&self) -> &StreamInner {
221 &self.0
222 }
223
224 /// Returns a mutable reference to the underlying platform specific implementation of
225 /// this `Stream`.
226 pub fn as_inner_mut(&mut self) -> &mut StreamInner {
227 &mut self.0
228 }
229
230 /// Returns the underlying platform specific implementation of this `Stream`.
231 pub fn into_inner(self) -> StreamInner {
232 self.0
233 }
234 }
235
236 impl Iterator for Devices {
237 type Item = Device;
238
239 fn next(&mut self) -> Option<Self::Item> {
240 match self.0 {
241 $(
242 $(#[cfg($feat)])?
243 DevicesInner::$HostVariant(ref mut d) => {
244 d.next().map(DeviceInner::$HostVariant).map(Device::from)
245 }
246 )*
247 }
248 }
249
250 fn size_hint(&self) -> (usize, Option<usize>) {
251 match self.0 {
252 $(
253 $(#[cfg($feat)])?
254 DevicesInner::$HostVariant(ref d) => d.size_hint(),
255 )*
256 }
257 }
258 }
259
260 impl Iterator for SupportedInputConfigs {
261 type Item = crate::SupportedStreamConfigRange;
262
263 fn next(&mut self) -> Option<Self::Item> {
264 match self.0 {
265 $(
266 $(#[cfg($feat)])?
267 SupportedInputConfigsInner::$HostVariant(ref mut s) => s.next(),
268 )*
269 }
270 }
271
272 fn size_hint(&self) -> (usize, Option<usize>) {
273 match self.0 {
274 $(
275 $(#[cfg($feat)])?
276 SupportedInputConfigsInner::$HostVariant(ref d) => d.size_hint(),
277 )*
278 }
279 }
280 }
281
282 impl Iterator for SupportedOutputConfigs {
283 type Item = crate::SupportedStreamConfigRange;
284
285 fn next(&mut self) -> Option<Self::Item> {
286 match self.0 {
287 $(
288 $(#[cfg($feat)])?
289 SupportedOutputConfigsInner::$HostVariant(ref mut s) => s.next(),
290 )*
291 }
292 }
293
294 fn size_hint(&self) -> (usize, Option<usize>) {
295 match self.0 {
296 $(
297 $(#[cfg($feat)])?
298 SupportedOutputConfigsInner::$HostVariant(ref d) => d.size_hint(),
299 )*
300 }
301 }
302 }
303
304 impl crate::traits::DeviceTrait for Device {
305 type SupportedInputConfigs = SupportedInputConfigs;
306 type SupportedOutputConfigs = SupportedOutputConfigs;
307 type Stream = Stream;
308
309 fn name(&self) -> Result<String, crate::DeviceNameError> {
310 match self.0 {
311 $(
312 $(#[cfg($feat)])?
313 DeviceInner::$HostVariant(ref d) => d.name(),
314 )*
315 }
316 }
317
318 fn supported_input_configs(&self) -> Result<Self::SupportedInputConfigs, crate::SupportedStreamConfigsError> {
319 match self.0 {
320 $(
321 $(#[cfg($feat)])?
322 DeviceInner::$HostVariant(ref d) => {
323 d.supported_input_configs()
324 .map(SupportedInputConfigsInner::$HostVariant)
325 .map(SupportedInputConfigs)
326 }
327 )*
328 }
329 }
330
331 fn supported_output_configs(&self) -> Result<Self::SupportedOutputConfigs, crate::SupportedStreamConfigsError> {
332 match self.0 {
333 $(
334 $(#[cfg($feat)])?
335 DeviceInner::$HostVariant(ref d) => {
336 d.supported_output_configs()
337 .map(SupportedOutputConfigsInner::$HostVariant)
338 .map(SupportedOutputConfigs)
339 }
340 )*
341 }
342 }
343
344 fn default_input_config(&self) -> Result<crate::SupportedStreamConfig, crate::DefaultStreamConfigError> {
345 match self.0 {
346 $(
347 $(#[cfg($feat)])?
348 DeviceInner::$HostVariant(ref d) => d.default_input_config(),
349 )*
350 }
351 }
352
353 fn default_output_config(&self) -> Result<crate::SupportedStreamConfig, crate::DefaultStreamConfigError> {
354 match self.0 {
355 $(
356 $(#[cfg($feat)])?
357 DeviceInner::$HostVariant(ref d) => d.default_output_config(),
358 )*
359 }
360 }
361
362 fn build_input_stream_raw<D, E>(
363 &self,
364 config: &crate::StreamConfig,
365 sample_format: crate::SampleFormat,
366 data_callback: D,
367 error_callback: E,
368 timeout: Option<std::time::Duration>,
369 ) -> Result<Self::Stream, crate::BuildStreamError>
370 where
371 D: FnMut(&crate::Data, &crate::InputCallbackInfo) + Send + 'static,
372 E: FnMut(crate::StreamError) + Send + 'static,
373 {
374 match self.0 {
375 $(
376 $(#[cfg($feat)])?
377 DeviceInner::$HostVariant(ref d) => d
378 .build_input_stream_raw(
379 config,
380 sample_format,
381 data_callback,
382 error_callback,
383 timeout,
384 )
385 .map(StreamInner::$HostVariant)
386 .map(Stream::from),
387 )*
388 }
389 }
390
391 fn build_output_stream_raw<D, E>(
392 &self,
393 config: &crate::StreamConfig,
394 sample_format: crate::SampleFormat,
395 data_callback: D,
396 error_callback: E,
397 timeout: Option<std::time::Duration>,
398 ) -> Result<Self::Stream, crate::BuildStreamError>
399 where
400 D: FnMut(&mut crate::Data, &crate::OutputCallbackInfo) + Send + 'static,
401 E: FnMut(crate::StreamError) + Send + 'static,
402 {
403 match self.0 {
404 $(
405 $(#[cfg($feat)])?
406 DeviceInner::$HostVariant(ref d) => d
407 .build_output_stream_raw(
408 config,
409 sample_format,
410 data_callback,
411 error_callback,
412 timeout,
413 )
414 .map(StreamInner::$HostVariant)
415 .map(Stream::from),
416 )*
417 }
418 }
419 }
420
421 impl crate::traits::HostTrait for Host {
422 type Devices = Devices;
423 type Device = Device;
424
425 fn is_available() -> bool {
426 $(
427 $(#[cfg($feat)])?
428 if crate::host::$host_mod::Host::is_available() { return true; }
429 )*
430 false
431 }
432
433 fn devices(&self) -> Result<Self::Devices, crate::DevicesError> {
434 match self.0 {
435 $(
436 $(#[cfg($feat)])?
437 HostInner::$HostVariant(ref h) => {
438 h.devices().map(DevicesInner::$HostVariant).map(Devices::from)
439 }
440 )*
441 }
442 }
443
444 fn default_input_device(&self) -> Option<Self::Device> {
445 match self.0 {
446 $(
447 $(#[cfg($feat)])?
448 HostInner::$HostVariant(ref h) => {
449 h.default_input_device().map(DeviceInner::$HostVariant).map(Device::from)
450 }
451 )*
452 }
453 }
454
455 fn default_output_device(&self) -> Option<Self::Device> {
456 match self.0 {
457 $(
458 $(#[cfg($feat)])?
459 HostInner::$HostVariant(ref h) => {
460 h.default_output_device().map(DeviceInner::$HostVariant).map(Device::from)
461 }
462 )*
463 }
464 }
465 }
466
467 impl crate::traits::StreamTrait for Stream {
468 fn play(&self) -> Result<(), crate::PlayStreamError> {
469 match self.0 {
470 $(
471 $(#[cfg($feat)])?
472 StreamInner::$HostVariant(ref s) => {
473 s.play()
474 }
475 )*
476 }
477 }
478
479 fn pause(&self) -> Result<(), crate::PauseStreamError> {
480 match self.0 {
481 $(
482 $(#[cfg($feat)])?
483 StreamInner::$HostVariant(ref s) => {
484 s.pause()
485 }
486 )*
487 }
488 }
489 }
490
491 impl From<DeviceInner> for Device {
492 fn from(d: DeviceInner) -> Self {
493 Device(d)
494 }
495 }
496
497 impl From<DevicesInner> for Devices {
498 fn from(d: DevicesInner) -> Self {
499 Devices(d)
500 }
501 }
502
503 impl From<HostInner> for Host {
504 fn from(h: HostInner) -> Self {
505 Host(h)
506 }
507 }
508
509 impl From<StreamInner> for Stream {
510 fn from(s: StreamInner) -> Self {
511 Stream(s, Default::default())
512 }
513 }
514
515 $(
516 $(#[cfg($feat)])?
517 impl From<crate::host::$host_mod::Device> for Device {
518 fn from(h: crate::host::$host_mod::Device) -> Self {
519 DeviceInner::$HostVariant(h).into()
520 }
521 }
522
523 $(#[cfg($feat)])?
524 impl From<crate::host::$host_mod::Devices> for Devices {
525 fn from(h: crate::host::$host_mod::Devices) -> Self {
526 DevicesInner::$HostVariant(h).into()
527 }
528 }
529
530 $(#[cfg($feat)])?
531 impl From<crate::host::$host_mod::Host> for Host {
532 fn from(h: crate::host::$host_mod::Host) -> Self {
533 HostInner::$HostVariant(h).into()
534 }
535 }
536
537 $(#[cfg($feat)])?
538 impl From<crate::host::$host_mod::Stream> for Stream {
539 fn from(h: crate::host::$host_mod::Stream) -> Self {
540 StreamInner::$HostVariant(h).into()
541 }
542 }
543 )*
544
545 /// Produces a list of hosts that are currently available on the system.
546 pub fn available_hosts() -> Vec<HostId> {
547 let mut host_ids = vec![];
548 $(
549 $(#[cfg($feat)])?
550 if <crate::host::$host_mod::Host as crate::traits::HostTrait>::is_available() {
551 host_ids.push(HostId::$HostVariant);
552 }
553 )*
554 host_ids
555 }
556
557 /// Given a unique host identifier, initialise and produce the host if it is available.
558 pub fn host_from_id(id: HostId) -> Result<Host, crate::HostUnavailable> {
559 match id {
560 $(
561 $(#[cfg($feat)])?
562 HostId::$HostVariant => {
563 crate::host::$host_mod::Host::new()
564 .map(HostInner::$HostVariant)
565 .map(Host::from)
566 }
567 )*
568 }
569 }
570 };
571}
572
573// TODO: Add pulseaudio and jack here eventually.
574#[cfg(any(
575 target_os = "linux",
576 target_os = "dragonfly",
577 target_os = "freebsd",
578 target_os = "netbsd"
579))]
580mod platform_impl {
581 pub use crate::host::alsa::{
582 Device as AlsaDevice, Devices as AlsaDevices, Host as AlsaHost, Stream as AlsaStream,
583 SupportedInputConfigs as AlsaSupportedInputConfigs,
584 SupportedOutputConfigs as AlsaSupportedOutputConfigs,
585 };
586 #[cfg(feature = "jack")]
587 pub use crate::host::jack::{
588 Device as JackDevice, Devices as JackDevices, Host as JackHost, Stream as JackStream,
589 SupportedInputConfigs as JackSupportedInputConfigs,
590 SupportedOutputConfigs as JackSupportedOutputConfigs,
591 };
592
593 impl_platform_host!(#[cfg(feature = "jack")] Jack jack "JACK", Alsa alsa "ALSA");
594
595 /// The default host for the current compilation target platform.
596 pub fn default_host() -> Host {
597 AlsaHostHost::new()
598 .expect(msg:"the default host should always be available")
599 .into()
600 }
601}
602
603#[cfg(any(target_os = "macos", target_os = "ios"))]
604mod platform_impl {
605 pub use crate::host::coreaudio::{
606 Device as CoreAudioDevice, Devices as CoreAudioDevices, Host as CoreAudioHost,
607 Stream as CoreAudioStream, SupportedInputConfigs as CoreAudioSupportedInputConfigs,
608 SupportedOutputConfigs as CoreAudioSupportedOutputConfigs,
609 };
610
611 impl_platform_host!(CoreAudio coreaudio "CoreAudio");
612
613 /// The default host for the current compilation target platform.
614 pub fn default_host() -> Host {
615 CoreAudioHost::new()
616 .expect("the default host should always be available")
617 .into()
618 }
619}
620
621#[cfg(target_os = "emscripten")]
622mod platform_impl {
623 pub use crate::host::emscripten::{
624 Device as EmscriptenDevice, Devices as EmscriptenDevices, Host as EmscriptenHost,
625 Stream as EmscriptenStream, SupportedInputConfigs as EmscriptenSupportedInputConfigs,
626 SupportedOutputConfigs as EmscriptenSupportedOutputConfigs,
627 };
628
629 impl_platform_host!(Emscripten emscripten "Emscripten");
630
631 /// The default host for the current compilation target platform.
632 pub fn default_host() -> Host {
633 EmscriptenHost::new()
634 .expect("the default host should always be available")
635 .into()
636 }
637}
638
639#[cfg(all(target_arch = "wasm32", feature = "wasm-bindgen"))]
640mod platform_impl {
641 pub use crate::host::webaudio::{
642 Device as WebAudioDevice, Devices as WebAudioDevices, Host as WebAudioHost,
643 Stream as WebAudioStream, SupportedInputConfigs as WebAudioSupportedInputConfigs,
644 SupportedOutputConfigs as WebAudioSupportedOutputConfigs,
645 };
646
647 impl_platform_host!(WebAudio webaudio "WebAudio");
648
649 /// The default host for the current compilation target platform.
650 pub fn default_host() -> Host {
651 WebAudioHost::new()
652 .expect("the default host should always be available")
653 .into()
654 }
655}
656
657#[cfg(windows)]
658mod platform_impl {
659 #[cfg(feature = "asio")]
660 pub use crate::host::asio::{
661 Device as AsioDevice, Devices as AsioDevices, Host as AsioHost, Stream as AsioStream,
662 SupportedInputConfigs as AsioSupportedInputConfigs,
663 SupportedOutputConfigs as AsioSupportedOutputConfigs,
664 };
665 pub use crate::host::wasapi::{
666 Device as WasapiDevice, Devices as WasapiDevices, Host as WasapiHost,
667 Stream as WasapiStream, SupportedInputConfigs as WasapiSupportedInputConfigs,
668 SupportedOutputConfigs as WasapiSupportedOutputConfigs,
669 };
670
671 impl_platform_host!(#[cfg(feature = "asio")] Asio asio "ASIO", Wasapi wasapi "WASAPI");
672
673 /// The default host for the current compilation target platform.
674 pub fn default_host() -> Host {
675 WasapiHost::new()
676 .expect("the default host should always be available")
677 .into()
678 }
679}
680
681#[cfg(target_os = "android")]
682mod platform_impl {
683 pub use crate::host::oboe::{
684 Device as OboeDevice, Devices as OboeDevices, Host as OboeHost, Stream as OboeStream,
685 SupportedInputConfigs as OboeSupportedInputConfigs,
686 SupportedOutputConfigs as OboeSupportedOutputConfigs,
687 };
688
689 impl_platform_host!(Oboe oboe "Oboe");
690
691 /// The default host for the current compilation target platform.
692 pub fn default_host() -> Host {
693 OboeHost::new()
694 .expect("the default host should always be available")
695 .into()
696 }
697}
698
699#[cfg(not(any(
700 windows,
701 target_os = "linux",
702 target_os = "dragonfly",
703 target_os = "freebsd",
704 target_os = "netbsd",
705 target_os = "macos",
706 target_os = "ios",
707 target_os = "emscripten",
708 target_os = "android",
709 all(target_arch = "wasm32", feature = "wasm-bindgen"),
710)))]
711mod platform_impl {
712 pub use crate::host::null::{
713 Device as NullDevice, Devices as NullDevices, Host as NullHost,
714 SupportedInputConfigs as NullSupportedInputConfigs,
715 SupportedOutputConfigs as NullSupportedOutputConfigs,
716 };
717
718 impl_platform_host!(Null null "Null");
719
720 /// The default host for the current compilation target platform.
721 pub fn default_host() -> Host {
722 NullHost::new()
723 .expect("the default host should always be available")
724 .into()
725 }
726}
727
728// The following zero-sized types are for applying Send/Sync restrictions to ensure
729// consistent behaviour across different platforms. These verbosely named types are used
730// (rather than using the markers directly) in the hope of making the compile errors
731// slightly more helpful.
732//
733// TODO: Remove these in favour of using negative trait bounds if they stabilise.
734
735// A marker used to remove the `Send` and `Sync` traits.
736struct NotSendSyncAcrossAllPlatforms(std::marker::PhantomData<*mut ()>);
737
738impl Default for NotSendSyncAcrossAllPlatforms {
739 fn default() -> Self {
740 NotSendSyncAcrossAllPlatforms(std::marker::PhantomData)
741 }
742}
743