rich_sdl2_rust/
audio.rs

1//! Provides audio device control, configuration, wav format utilities and so on.
2
3use static_assertions::assert_not_impl_all;
4use std::{
5    cell::Cell,
6    ffi::{CStr, CString},
7    marker::PhantomData,
8    mem::MaybeUninit,
9    os::raw::c_int,
10};
11
12use self::{
13    format::AudioFormat,
14    spec::{AudioCallback, AudioSpec, FallbackFlag},
15    status::AudioStatus,
16};
17use crate::{bind, Result, Sdl, SdlError};
18
19pub use driver::*;
20
21pub mod buffer;
22mod driver;
23pub mod event;
24pub mod format;
25pub mod queue;
26pub mod spec;
27pub mod status;
28pub mod stream;
29pub mod wav;
30
31/// A property of an audio device.
32#[derive(Debug, Clone, Copy, PartialEq, Eq)]
33pub struct AudioDeviceProperty {
34    /// The sample frequencies of an audio device..
35    pub sample_freq: u32,
36    /// The format of an audio device.
37    pub format: AudioFormat,
38    /// The numbers of channels of an audio device.
39    pub channels: u8,
40    /// The sample rates of an audio device.
41    pub samples: u16,
42}
43
44/// A lock to prevent an audio device from calling the callback [`spec::AudioCallback`].
45pub struct AudioDeviceLock<'device>(u32, PhantomData<&'device mut dyn AudioDevice>);
46
47impl AudioDeviceLock<'_> {
48    /// Unlocks the lock of an audio device.
49    fn unlock(self) {
50        unsafe { bind::SDL_UnlockAudioDevice(self.0) }
51    }
52}
53
54/// Common methods for an audio device, such as a speaker and a microphone.
55pub trait AudioDevice {
56    /// Returns the id of an audio device.
57    fn id(&self) -> u32;
58
59    /// Returns the status of an audio device.
60    fn status(&self) -> AudioStatus {
61        unsafe { bind::SDL_GetAudioDeviceStatus(self.id()) }.into()
62    }
63
64    /// Obtains the lock preventing from calling the callback [`spec::AudioCallback`].
65    fn lock(&mut self) -> AudioDeviceLock {
66        unsafe { bind::SDL_LockAudioDevice(self.id()) }
67        AudioDeviceLock(self.id(), PhantomData)
68    }
69}
70
71/// An audio device to output sound.
72pub struct SpeakerDevice {
73    id: u32,
74    _phantom: PhantomData<Cell<u8>>,
75}
76
77impl std::fmt::Debug for SpeakerDevice {
78    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
79        f.debug_struct("SpeakerDevice")
80            .field("id", &self.id)
81            .finish()
82    }
83}
84
85assert_not_impl_all!(SpeakerDevice: Send, Sync);
86
87impl SpeakerDevice {
88    /// Returns all of speaker audio device names on now.
89    pub fn all_devices() -> impl Iterator<Item = String> {
90        devices(false)
91    }
92
93    /// Opens the audio device named `device` with the specification and fallback flag.
94    /// If device is `None`, the default audio device is used.
95    ///
96    /// # Errors
97    ///
98    /// Returns `Err` if failed to open a specific speaker device.
99    pub fn open<'callback, T: AudioCallback<'callback>>(
100        device: Option<String>,
101        spec: &'callback AudioSpec<'callback, T>,
102        fallback: FallbackFlag,
103    ) -> Result<(Self, AudioDeviceProperty)> {
104        let (id, prop) = open(false, device, spec, fallback)?;
105        Ok((
106            Self {
107                id,
108                _phantom: PhantomData,
109            },
110            prop,
111        ))
112    }
113}
114
115impl AudioDevice for SpeakerDevice {
116    fn id(&self) -> u32 {
117        self.id
118    }
119}
120
121impl Drop for SpeakerDevice {
122    fn drop(&mut self) {
123        unsafe { bind::SDL_CloseAudioDevice(self.id) }
124    }
125}
126
127/// An audio device to input sound.
128pub struct MicrophoneDevice {
129    id: u32,
130    _phantom: PhantomData<Cell<u8>>,
131}
132
133impl std::fmt::Debug for MicrophoneDevice {
134    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
135        f.debug_struct("MicrophoneDevice")
136            .field("id", &self.id)
137            .finish()
138    }
139}
140
141assert_not_impl_all!(MicrophoneDevice: Send, Sync);
142
143impl MicrophoneDevice {
144    /// Returns all of microphone audio device names on now.
145    pub fn all_devices() -> impl Iterator<Item = String> {
146        devices(true)
147    }
148
149    /// Opens the audio device named `device` with the specification and fallback flag.
150    /// If device is `None`, the default audio device is used.
151    ///
152    /// # Errors
153    ///
154    /// Returns `Err` if failed to open a specific microphone device.
155    pub fn open<'callback, T: AudioCallback<'callback>>(
156        device: Option<String>,
157        spec: &'callback AudioSpec<'callback, T>,
158        fallback: FallbackFlag,
159    ) -> Result<(Self, AudioDeviceProperty)> {
160        let (id, prop) = open(true, device, spec, fallback)?;
161        Ok((
162            Self {
163                id,
164                _phantom: PhantomData,
165            },
166            prop,
167        ))
168    }
169}
170
171impl AudioDevice for MicrophoneDevice {
172    fn id(&self) -> u32 {
173        self.id
174    }
175}
176
177/// Returns the device name of the id `device_id`. Please set `is_microphone` according to what type you want to.
178#[must_use]
179pub fn device_name(device_id: u32, is_microphone: bool) -> Option<String> {
180    let ptr = unsafe {
181        bind::SDL_GetAudioDeviceName(device_id as i32, if is_microphone { 1 } else { 0 })
182    };
183    (!ptr.is_null()).then(|| unsafe { CStr::from_ptr(ptr) }.to_string_lossy().to_string())
184}
185
186fn devices(is_capture: bool) -> impl Iterator<Item = String> {
187    let is_capture_raw = if is_capture { 1 } else { 0 };
188    let num_devices = unsafe {
189        bind::SDL_InitSubSystem(bind::SDL_INIT_AUDIO);
190        bind::SDL_GetNumAudioDevices(is_capture_raw)
191    };
192    (0..num_devices).map(move |index| {
193        let cstr = unsafe { CStr::from_ptr(bind::SDL_GetAudioDeviceName(index, is_capture_raw)) };
194        cstr.to_string_lossy().to_string()
195    })
196}
197
198fn open<'callback, T: AudioCallback<'callback>>(
199    is_capture: bool,
200    device: Option<String>,
201    spec: &'callback AudioSpec<'callback, T>,
202    fallback: FallbackFlag,
203) -> Result<(u32, AudioDeviceProperty)> {
204    let is_capture_raw = if is_capture { 1 } else { 0 };
205    let device = device.map(|s| CString::new(s).expect("device name is invalid"));
206    let mut actual = MaybeUninit::uninit();
207    let id = unsafe {
208        bind::SDL_OpenAudioDevice(
209            device.map_or(std::ptr::null_mut(), |s| s.into_raw()),
210            is_capture_raw,
211            spec.raw() as *const _,
212            actual.as_mut_ptr(),
213            fallback.bits() as c_int,
214        )
215    };
216    if id == 0 {
217        return Err(SdlError::Others { msg: Sdl::error() });
218    }
219    let actual = unsafe { actual.assume_init() };
220    Ok((
221        id,
222        AudioDeviceProperty {
223            sample_freq: actual.freq as u32,
224            format: actual.format.into(),
225            channels: actual.channels,
226            samples: actual.samples,
227        },
228    ))
229}