cpal/platform/
mod.rs

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        AlsaHost::new()
598            .expect("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}