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