1use 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#[derive(Debug, Clone, Copy, PartialEq, Eq)]
33pub struct AudioDeviceProperty {
34 pub sample_freq: u32,
36 pub format: AudioFormat,
38 pub channels: u8,
40 pub samples: u16,
42}
43
44pub struct AudioDeviceLock<'device>(u32, PhantomData<&'device mut dyn AudioDevice>);
46
47impl AudioDeviceLock<'_> {
48 fn unlock(self) {
50 unsafe { bind::SDL_UnlockAudioDevice(self.0) }
51 }
52}
53
54pub trait AudioDevice {
56 fn id(&self) -> u32;
58
59 fn status(&self) -> AudioStatus {
61 unsafe { bind::SDL_GetAudioDeviceStatus(self.id()) }.into()
62 }
63
64 fn lock(&mut self) -> AudioDeviceLock {
66 unsafe { bind::SDL_LockAudioDevice(self.id()) }
67 AudioDeviceLock(self.id(), PhantomData)
68 }
69}
70
71pub 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 pub fn all_devices() -> impl Iterator<Item = String> {
90 devices(false)
91 }
92
93 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
127pub 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 pub fn all_devices() -> impl Iterator<Item = String> {
146 devices(true)
147 }
148
149 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#[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}