sdl3/
audio.rs

1//! Audio Functions
2//!
3//! # Example
4//! ```no_run
5//! use sdl3::audio::{AudioCallback, AudioFormat, AudioSpec};
6//! use std::time::Duration;
7//!
8//! struct SquareWave {
9//!     phase_inc: f32,
10//!     phase: f32,
11//!     volume: f32
12//! }
13//!
14//! impl AudioCallback<f32> for SquareWave {
15//!     fn callback(&mut self, out: &mut [f32]) {
16//!         // Generate a square wave
17//!         for x in out.iter_mut() {
18//!             *x = if self.phase <= 0.5 {
19//!                 self.volume
20//!             } else {
21//!                 -self.volume
22//!             };
23//!             self.phase = (self.phase + self.phase_inc) % 1.0;
24//!         }
25//!     }
26//! }
27//!
28//! let sdl_context = sdl3::init().unwrap();
29//! let audio_subsystem = sdl_context.audio().unwrap();
30//!
31//! let source_freq = 44100;
32//! let source_spec = AudioSpec {
33//!     freq: Some(source_freq),
34//!     channels: Some(1),                      // mono
35//!     format: Some(AudioFormat::f32_sys())    // floating 32 bit samples
36//! };
37//!
38//! // Initialize the audio callback
39//! let device = audio_subsystem.open_playback_stream(&source_spec, SquareWave {
40//!     phase_inc: 440.0 / source_freq as f32,
41//!     phase: 0.0,
42//!     volume: 0.25
43//! }).unwrap();
44//!
45//! // Start playback
46//! device.resume().expect("Failed to start playback");
47//!
48//! // Play for 2 seconds
49//! std::thread::sleep(Duration::from_millis(2000));
50//! ```
51
52use crate::get_error;
53use crate::iostream::IOStream;
54use crate::sys;
55use crate::AudioSubsystem;
56use crate::Error;
57use libc::c_void;
58use std::convert::TryInto;
59use std::ffi::{c_int, CStr};
60use std::fmt;
61use std::fmt::{Debug, Display};
62use std::io::{self, Read};
63use std::marker::PhantomData;
64use std::ops::Deref;
65use std::ops::DerefMut;
66use std::path::Path;
67use sys::audio::{SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, SDL_AUDIO_DEVICE_DEFAULT_RECORDING};
68use sys::stdinc::SDL_free;
69
70impl AudioSubsystem {
71    /// Enumerate audio playback devices.
72    #[doc(alias = "SDL_GetAudioPlaybackDevices")]
73    pub fn audio_playback_device_ids(&self) -> Result<Vec<AudioDeviceID>, Error> {
74        unsafe {
75            self.audio_device_ids(|num_devices| {
76                sys::audio::SDL_GetAudioPlaybackDevices(num_devices)
77            })
78        }
79    }
80
81    /// Enumerate audio recording devices.
82    #[doc(alias = "SDL_GetAudioRecordingDevices")]
83    pub fn audio_recording_device_ids(&self) -> Result<Vec<AudioDeviceID>, Error> {
84        self.audio_device_ids(|num_devices| unsafe {
85            sys::audio::SDL_GetAudioRecordingDevices(num_devices)
86        })
87    }
88
89    fn audio_device_ids<F>(&self, get_devices: F) -> Result<Vec<AudioDeviceID>, Error>
90    where
91        F: FnOnce(&mut i32) -> *mut sys::audio::SDL_AudioDeviceID,
92    {
93        let mut num_devices: i32 = 0;
94        let devices = get_devices(&mut num_devices);
95        if devices.is_null() {
96            return Err(get_error());
97        }
98
99        let mut ret = Vec::new();
100        for i in 0..num_devices {
101            let instance_id = unsafe { *devices.offset(i as isize) };
102            ret.push(AudioDeviceID::Device(instance_id));
103        }
104
105        unsafe { SDL_free(devices as *mut c_void) };
106        Ok(ret)
107    }
108    /// Open a default playback device with the specified audio spec.
109    pub fn open_playback_device(&self, spec: &AudioSpec) -> Result<AudioDevice, Error> {
110        self.open_device(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, spec)
111    }
112
113    /// Open a default recording device with the specified audio spec.
114    pub fn open_recording_device(&self, spec: &AudioSpec) -> Result<AudioDevice, Error> {
115        self.open_device(SDL_AUDIO_DEVICE_DEFAULT_RECORDING, spec)
116    }
117
118    pub fn default_playback_device(&self) -> AudioDevice {
119        AudioDevice::new(
120            AudioDeviceID::Device(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK),
121            self.clone(),
122        )
123    }
124
125    pub fn default_recording_device(&self) -> AudioDevice {
126        AudioDevice::new(
127            AudioDeviceID::Device(SDL_AUDIO_DEVICE_DEFAULT_RECORDING),
128            self.clone(),
129        )
130    }
131
132    /// General method to open a device by ID.
133    fn open_device(
134        &self,
135        device_id: sys::audio::SDL_AudioDeviceID,
136        spec: &AudioSpec,
137    ) -> Result<AudioDevice, Error> {
138        let sdl_spec: sys::audio::SDL_AudioSpec = spec.clone().into();
139        let device = unsafe { sys::audio::SDL_OpenAudioDevice(device_id, &sdl_spec) };
140        if device == 0 {
141            Err(get_error())
142        } else {
143            Ok(AudioDevice::new(
144                AudioDeviceID::Device(device),
145                self.clone(),
146            ))
147        }
148    }
149
150    pub fn open_playback_stream_with_callback<CB, Channel>(
151        &self,
152        device: &AudioDevice,
153        spec: &AudioSpec,
154        callback: CB,
155    ) -> Result<AudioStreamWithCallback<CB>, Error>
156    where
157        CB: AudioCallback<Channel>,
158        Channel: AudioFormatNum + 'static,
159    {
160        device.open_playback_stream_with_callback(spec, callback)
161    }
162
163    pub fn open_playback_stream<CB, Channel>(
164        &self,
165        spec: &AudioSpec,
166        callback: CB,
167    ) -> Result<AudioStreamWithCallback<CB>, Error>
168    where
169        CB: AudioCallback<Channel>,
170        Channel: AudioFormatNum + 'static,
171    {
172        let device = AudioDevice::open_playback(self, None, spec)?;
173        device.open_playback_stream_with_callback(spec, callback)
174    }
175
176    pub fn open_recording_stream<CB, Channel>(
177        &self,
178        spec: &AudioSpec,
179        callback: CB,
180    ) -> Result<AudioStreamWithCallback<CB>, Error>
181    where
182        CB: AudioRecordingCallback<Channel>,
183        Channel: AudioFormatNum + 'static,
184    {
185        let device = AudioDevice::open_recording(self, None, spec)?;
186        device.open_recording_stream_with_callback(spec, callback)
187    }
188
189    #[doc(alias = "SDL_GetCurrentAudioDriver")]
190    pub fn current_audio_driver(&self) -> &'static str {
191        unsafe {
192            let buf = sys::audio::SDL_GetCurrentAudioDriver();
193            assert!(!buf.is_null());
194
195            CStr::from_ptr(buf as *const _).to_str().unwrap()
196        }
197    }
198
199    #[doc(alias = "SDL_GetAudioDeviceName")]
200    pub fn audio_playback_device_name(&self, index: u32) -> Result<String, Error> {
201        unsafe {
202            let dev_name = sys::audio::SDL_GetAudioDeviceName(index);
203            if dev_name.is_null() {
204                Err(get_error())
205            } else {
206                let cstr = CStr::from_ptr(dev_name as *const _);
207                Ok(cstr.to_str().unwrap().to_owned())
208            }
209        }
210    }
211
212    #[doc(alias = "SDL_GetAudioDeviceName")]
213    pub fn audio_recording_device_name(&self, index: u32) -> Result<String, Error> {
214        unsafe {
215            let dev_name = sys::audio::SDL_GetAudioDeviceName(index);
216            if dev_name.is_null() {
217                Err(get_error())
218            } else {
219                let cstr = CStr::from_ptr(dev_name as *const _);
220                Ok(cstr.to_str().unwrap().to_owned())
221            }
222        }
223    }
224
225    /// Creates a new audio stream that converts audio data from the source format (`src_spec`)
226    /// to the destination format (`dst_spec`).
227    ///
228    /// # Arguments
229    ///
230    /// * `src_spec` - The format details of the input audio.
231    /// * `dst_spec` - The format details of the output audio.
232    ///
233    /// # Returns
234    ///
235    /// Returns `Ok(AudioStream)` on success or an error message on failure.
236    ///
237    /// # Safety
238    ///
239    /// This function is safe to call from any thread.
240    pub fn new_stream(
241        &self,
242        src_spec: Option<&AudioSpec>,
243        dst_spec: Option<&AudioSpec>,
244    ) -> Result<AudioStreamOwner, Error> {
245        let sdl_src_spec = src_spec.map(sys::audio::SDL_AudioSpec::from);
246        let sdl_dst_spec = dst_spec.map(sys::audio::SDL_AudioSpec::from);
247
248        let sdl_src_spec_ptr = sdl_src_spec
249            .as_ref()
250            .map_or(std::ptr::null(), |spec| spec as *const _);
251        let sdl_dst_spec_ptr = sdl_dst_spec
252            .as_ref()
253            .map_or(std::ptr::null(), |spec| spec as *const _);
254
255        let stream =
256            unsafe { sys::audio::SDL_CreateAudioStream(sdl_src_spec_ptr, sdl_dst_spec_ptr) };
257        if stream.is_null() {
258            Err(get_error())
259        } else {
260            Ok(AudioStreamOwner {
261                inner: AudioStream { stream },
262                audio_subsystem: self.clone().into(),
263            })
264        }
265    }
266
267    /// Creates a new audio stream for playback.
268    ///
269    /// # Arguments
270    ///
271    /// * `app_spec` - The format of audio data the application will provide.
272    /// * `device_spec` - The format of audio data the audio device expects.
273    ///                   If `None`, SDL will choose an appropriate format.
274    pub fn new_playback_stream(
275        &self,
276        app_spec: &AudioSpec,
277        device_spec: Option<&AudioSpec>,
278    ) -> Result<AudioStreamOwner, Error> {
279        self.new_stream(Some(app_spec), device_spec)
280    }
281
282    /// Creates a new audio stream for recording.
283    ///
284    /// # Arguments
285    ///
286    /// * `device_spec` - The format of audio data the audio device provides.
287    ///                   If `None`, SDL will choose an appropriate format.
288    /// * `app_spec` - The format of audio data the application wants to receive.
289    pub fn new_recording_stream(
290        &self,
291        device_spec: Option<&AudioSpec>,
292        app_spec: &AudioSpec,
293    ) -> Result<AudioStreamOwner, Error> {
294        self.new_stream(device_spec, Some(app_spec))
295    }
296}
297
298#[repr(u32)]
299#[derive(Copy, Clone, Eq, PartialEq, Debug, Hash)]
300pub enum AudioFormat {
301    UNKNOWN = sys::audio::SDL_AUDIO_UNKNOWN.0,
302
303    /// Unsigned 8-bit samples
304    U8 = sys::audio::SDL_AUDIO_U8.0,
305    /// Signed 8-bit samples
306    S8 = sys::audio::SDL_AUDIO_S8.0,
307    /// Signed 16-bit samples, little-endian
308    S16LE = sys::audio::SDL_AUDIO_S16LE.0,
309    /// Signed 16-bit samples, big-endian
310    S16BE = sys::audio::SDL_AUDIO_S16BE.0,
311    /// Signed 32-bit samples, little-endian
312    S32LE = sys::audio::SDL_AUDIO_S32LE.0,
313    /// Signed 32-bit samples, big-endian
314    S32BE = sys::audio::SDL_AUDIO_S32BE.0,
315    /// 32-bit floating point samples, little-endian
316    F32LE = sys::audio::SDL_AUDIO_F32LE.0,
317    /// 32-bit floating point samples, big-endian
318    F32BE = sys::audio::SDL_AUDIO_F32BE.0,
319}
320
321impl AudioFormat {
322    fn from_ll(raw: sys::audio::SDL_AudioFormat) -> Option<AudioFormat> {
323        match raw {
324            sys::audio::SDL_AUDIO_UNKNOWN => Some(AudioFormat::UNKNOWN),
325            sys::audio::SDL_AUDIO_U8 => Some(AudioFormat::U8),
326            sys::audio::SDL_AUDIO_S8 => Some(AudioFormat::S8),
327            sys::audio::SDL_AUDIO_S16LE => Some(AudioFormat::S16LE),
328            sys::audio::SDL_AUDIO_S16BE => Some(AudioFormat::S16BE),
329            sys::audio::SDL_AUDIO_S32LE => Some(AudioFormat::S32LE),
330            sys::audio::SDL_AUDIO_S32BE => Some(AudioFormat::S32BE),
331            sys::audio::SDL_AUDIO_F32LE => Some(AudioFormat::F32LE),
332            sys::audio::SDL_AUDIO_F32BE => Some(AudioFormat::F32BE),
333            _ => None,
334        }
335    }
336
337    #[doc(alias = "SDL_AudioFormat")]
338    fn to_ll(self) -> sys::audio::SDL_AudioFormat {
339        self.into()
340    }
341}
342
343impl From<AudioFormat> for sys::audio::SDL_AudioFormat {
344    fn from(format: AudioFormat) -> sys::audio::SDL_AudioFormat {
345        match format {
346            AudioFormat::UNKNOWN => sys::audio::SDL_AUDIO_UNKNOWN,
347            AudioFormat::U8 => sys::audio::SDL_AUDIO_U8,
348            AudioFormat::S8 => sys::audio::SDL_AUDIO_S8,
349            AudioFormat::S16LE => sys::audio::SDL_AUDIO_S16LE,
350            AudioFormat::S16BE => sys::audio::SDL_AUDIO_S16BE,
351            AudioFormat::S32LE => sys::audio::SDL_AUDIO_S32LE,
352            AudioFormat::S32BE => sys::audio::SDL_AUDIO_S32BE,
353            AudioFormat::F32LE => sys::audio::SDL_AUDIO_F32LE,
354            AudioFormat::F32BE => sys::audio::SDL_AUDIO_F32BE,
355        }
356    }
357}
358
359#[cfg(target_endian = "little")]
360impl AudioFormat {
361    /// Signed 16-bit samples, native endian
362    #[inline]
363    pub const fn s16_sys() -> AudioFormat {
364        AudioFormat::S16LE
365    }
366    /// Signed 32-bit samples, native endian
367    #[inline]
368    pub const fn s32_sys() -> AudioFormat {
369        AudioFormat::S32LE
370    }
371    /// 32-bit floating point samples, native endian
372    #[inline]
373    pub const fn f32_sys() -> AudioFormat {
374        AudioFormat::F32LE
375    }
376}
377
378#[cfg(target_endian = "big")]
379impl AudioFormat {
380    /// Signed 16-bit samples, native endian
381    #[inline]
382    pub const fn s16_sys() -> AudioFormat {
383        AudioFormat::S16MSB
384    }
385    /// Signed 32-bit samples, native endian
386    #[inline]
387    pub const fn s32_sys() -> AudioFormat {
388        AudioFormat::S32MSB
389    }
390    /// 32-bit floating point samples, native endian
391    #[inline]
392    pub const fn f32_sys() -> AudioFormat {
393        AudioFormat::F32MSB
394    }
395}
396
397#[doc(alias = "SDL_GetAudioDriver")]
398#[derive(Copy, Clone)]
399pub struct DriverIterator {
400    length: i32,
401    index: i32,
402}
403
404impl Iterator for DriverIterator {
405    type Item = &'static str;
406
407    #[inline]
408    fn next(&mut self) -> Option<&'static str> {
409        if self.index >= self.length {
410            None
411        } else {
412            unsafe {
413                let buf = sys::audio::SDL_GetAudioDriver(self.index);
414                assert!(!buf.is_null());
415                self.index += 1;
416
417                Some(CStr::from_ptr(buf as *const _).to_str().unwrap())
418            }
419        }
420    }
421
422    #[inline]
423    fn size_hint(&self) -> (usize, Option<usize>) {
424        let l = self.length as usize;
425        (l, Some(l))
426    }
427}
428
429impl ExactSizeIterator for DriverIterator {}
430
431/// Gets an iterator of all audio drivers compiled into the SDL2 library.
432#[doc(alias = "SDL_GetAudioDriver")]
433#[inline]
434pub fn drivers() -> DriverIterator {
435    // This function is thread-safe and doesn't require the audio subsystem to be initialized.
436    // The list of drivers are read-only and statically compiled into SDL2, varying by platform.
437
438    // SDL_GetNumAudioDrivers can never return a negative value.
439    DriverIterator {
440        length: unsafe { sys::audio::SDL_GetNumAudioDrivers() },
441        index: 0,
442    }
443}
444
445pub struct AudioSpecWAV {
446    pub freq: i32,
447    pub format: AudioFormat,
448    pub channels: u8,
449    audio_buf: *mut u8,
450    audio_len: u32,
451}
452
453impl AudioSpecWAV {
454    /// Loads a WAVE from the file path.
455    pub fn load_wav<P: AsRef<Path>>(path: P) -> Result<AudioSpecWAV, Error> {
456        let mut file = IOStream::from_file(path, "rb")?;
457        AudioSpecWAV::load_wav_rw(&mut file)
458    }
459
460    /// Loads a WAVE from the data source.
461    #[doc(alias = "SDL_LoadWAV_RW")]
462    pub fn load_wav_rw(src: &mut IOStream) -> Result<AudioSpecWAV, Error> {
463        use std::mem::MaybeUninit;
464        use std::ptr::null_mut;
465
466        let mut desired = MaybeUninit::uninit();
467        let mut audio_buf: *mut u8 = null_mut();
468        let mut audio_len: u32 = 0;
469        unsafe {
470            let ret = sys::audio::SDL_LoadWAV_IO(
471                src.raw(),
472                false,
473                desired.as_mut_ptr(),
474                &mut audio_buf,
475                &mut audio_len,
476            );
477            if !ret {
478                Err(get_error())
479            } else {
480                let desired = desired.assume_init();
481                Ok(AudioSpecWAV {
482                    freq: desired.freq,
483                    format: AudioFormat::from_ll(desired.format).unwrap(),
484                    channels: desired.channels.try_into().unwrap(),
485                    audio_buf,
486                    audio_len,
487                })
488            }
489        }
490    }
491
492    pub fn buffer(&self) -> &[u8] {
493        use std::slice::from_raw_parts;
494        unsafe {
495            let ptr = self.audio_buf as *const u8;
496            let len = self.audio_len as usize;
497            from_raw_parts(ptr, len)
498        }
499    }
500}
501
502impl Drop for AudioSpecWAV {
503    #[doc(alias = "SDL_free")]
504    fn drop(&mut self) {
505        unsafe {
506            SDL_free(self.audio_buf as *mut _);
507        }
508    }
509}
510
511pub trait AudioCallback<Channel>: Send + 'static
512where
513    Channel: AudioFormatNum + 'static,
514{
515    fn callback(&mut self, stream: &mut AudioStream, requested: i32);
516}
517
518/// A phantom type for retrieving the `SDL_AudioFormat` of a given generic type.
519/// All format types are returned as native-endian.
520pub trait AudioFormatNum: Copy + 'static {
521    fn audio_format() -> AudioFormat;
522
523    /// The appropriately typed silence value for the audio format used.
524    ///
525    /// # Examples
526    ///
527    /// ```
528    /// // The AudioFormatNum trait has to be imported for the Channel::SILENCE part to work.
529    /// use sdl3::audio::{AudioCallback, AudioFormatNum};
530    ///
531    /// struct Silence;
532    ///
533    /// impl<Channel> AudioCallback<Channel> for Silence
534    /// where
535    ///     Channel: AudioFormatNum,
536    /// {
537    ///     fn callback(&mut self, out: &mut [Channel]) {
538    ///         for dst in out.iter_mut() {
539    ///             *dst = Channel::SILENCE;
540    ///        }
541    ///     }
542    /// }
543    /// ```
544    const SILENCE: Self;
545}
546
547/// `AUDIO_S8`
548impl AudioFormatNum for i8 {
549    fn audio_format() -> AudioFormat {
550        AudioFormat::S8
551    }
552    const SILENCE: i8 = 0;
553}
554/// `AUDIO_U8`
555impl AudioFormatNum for u8 {
556    fn audio_format() -> AudioFormat {
557        AudioFormat::U8
558    }
559    const SILENCE: u8 = 0x80;
560}
561/// `AUDIO_S16`
562impl AudioFormatNum for i16 {
563    fn audio_format() -> AudioFormat {
564        AudioFormat::s16_sys()
565    }
566    const SILENCE: i16 = 0;
567}
568/// `AUDIO_S32`
569impl AudioFormatNum for i32 {
570    fn audio_format() -> AudioFormat {
571        AudioFormat::s32_sys()
572    }
573    const SILENCE: i32 = 0;
574}
575/// `AUDIO_F32`
576impl AudioFormatNum for f32 {
577    fn audio_format() -> AudioFormat {
578        AudioFormat::f32_sys()
579    }
580    const SILENCE: f32 = 0.0;
581}
582
583#[derive(Clone, Debug)]
584pub struct AudioSpec {
585    /// DSP frequency (samples per second). Set to None for the device's fallback frequency.
586    pub freq: Option<i32>,
587    /// Number of separate audio channels. Set to None for the device's fallback number of channels.
588    pub channels: Option<i32>,
589    /// Audio format. Set to None for the device's fallback audio format.
590    pub format: Option<AudioFormat>,
591}
592
593impl From<AudioSpec> for sys::audio::SDL_AudioSpec {
594    fn from(val: AudioSpec) -> Self {
595        AudioSpec::convert_to_ll(val.freq, val.channels, val.format)
596    }
597}
598
599impl From<&AudioSpec> for sys::audio::SDL_AudioSpec {
600    fn from(spec: &AudioSpec) -> Self {
601        sys::audio::SDL_AudioSpec {
602            freq: spec.freq.unwrap_or(0), // SDL uses 0 to indicate default frequency
603            format: spec.format.unwrap_or(AudioFormat::UNKNOWN).to_ll(), // Use AudioFormat::Unknown for default
604            channels: spec.channels.unwrap_or(0), // SDL uses 0 to indicate default channels
605        }
606    }
607}
608
609impl AudioSpec {
610    fn convert_to_ll<R, C, F>(rate: R, channels: C, format: F) -> sys::audio::SDL_AudioSpec
611    where
612        R: Into<Option<i32>>,
613        C: Into<Option<i32>>,
614        F: Into<Option<AudioFormat>>,
615    {
616        let channels = channels.into();
617        let freq = rate.into();
618        let format = format.into();
619
620        sys::audio::SDL_AudioSpec {
621            freq: freq.unwrap_or(0),
622            format: format.unwrap_or(AudioFormat::UNKNOWN).to_ll(),
623            channels: channels.unwrap_or(0),
624        }
625    }
626}
627
628impl From<&sys::audio::SDL_AudioSpec> for AudioSpec {
629    fn from(sdl_spec: &sys::audio::SDL_AudioSpec) -> Self {
630        Self {
631            freq: if sdl_spec.freq != 0 {
632                Some(sdl_spec.freq)
633            } else {
634                None // SDL used default frequency
635            },
636            format: if sdl_spec.format != sys::audio::SDL_AUDIO_UNKNOWN {
637                Some(AudioFormat::from_ll(sdl_spec.format).expect("Unknown audio format"))
638            } else {
639                None // SDL used default format
640            },
641            channels: if sdl_spec.channels != 0 {
642                Some(sdl_spec.channels)
643            } else {
644                None // SDL used default channels
645            },
646        }
647    }
648}
649impl AudioSpec {
650    /// Creates a new `AudioSpec` with specified values.
651    /// Use `None` for any parameter to indicate the device's default value.
652    pub fn new(freq: Option<i32>, channels: Option<i32>, format: Option<AudioFormat>) -> Self {
653        Self {
654            freq,
655            channels,
656            format,
657        }
658    }
659
660    // fn convert_from_ll(spec: sys::audio::SDL_AudioSpec) -> AudioSpec {
661    //     AudioSpec {
662    //         freq: Some(spec.freq.into()),
663    //         format: AudioFormat::from_ll(spec.format),
664    //         channels: Some(spec.channels),
665    //     }
666    // }
667}
668
669impl Default for AudioSpec {
670    /// Creates an `AudioSpec` with all fields set to `None` (use device defaults).
671    fn default() -> Self {
672        Self {
673            freq: None,
674            channels: None,
675            format: None,
676        }
677    }
678}
679
680#[derive(Clone, Debug)]
681pub enum AudioDeviceID {
682    Device(sys::audio::SDL_AudioDeviceID),
683}
684
685impl Copy for AudioDeviceID {}
686
687impl AudioDeviceID {
688    pub fn id(&self) -> sys::audio::SDL_AudioDeviceID {
689        match *self {
690            AudioDeviceID::Device(id) => id,
691        }
692    }
693
694    pub fn name(&self) -> Result<String, Error> {
695        unsafe {
696            let name_ptr = sys::audio::SDL_GetAudioDeviceName(self.id());
697            if name_ptr.is_null() {
698                return Err(get_error());
699            }
700            Ok(CStr::from_ptr(name_ptr).to_str().unwrap().to_owned())
701        }
702    }
703}
704
705impl PartialEq for AudioDeviceID {
706    fn eq(&self, other: &Self) -> bool {
707        self.id() == other.id()
708    }
709}
710impl Eq for AudioDeviceID {}
711
712/// Represents an open audio device (playback or recording).
713#[derive(Clone)]
714pub struct AudioDevice {
715    device_id: AudioDeviceID,
716    // keep the audio subsystem alive
717    audio_subsystem: AudioSubsystem,
718}
719
720impl PartialEq for AudioDevice {
721    fn eq(&self, other: &Self) -> bool {
722        self.device_id == other.device_id
723    }
724}
725
726impl Eq for AudioDevice {}
727
728impl Drop for AudioDevice {
729    fn drop(&mut self) {
730        unsafe {
731            sys::audio::SDL_CloseAudioDevice(self.device_id.id());
732        }
733    }
734}
735
736impl AudioDevice {
737    pub fn id(&self) -> AudioDeviceID {
738        self.device_id
739    }
740
741    pub fn new(device_id: AudioDeviceID, audio_subsystem: AudioSubsystem) -> Self {
742        AudioDevice {
743            device_id,
744            audio_subsystem,
745        }
746    }
747
748    /// Get the name of the audio device.
749    #[doc(alias = "SDL_GetAudioDeviceName")]
750    pub fn name(&self) -> Result<String, Error> {
751        unsafe {
752            let name_ptr = sys::audio::SDL_GetAudioDeviceName(self.device_id.id());
753            if name_ptr.is_null() {
754                return Err(get_error());
755            }
756            Ok(CStr::from_ptr(name_ptr).to_str().unwrap().to_owned())
757        }
758    }
759
760    /// Create an `AudioStream` for this device with the specified spec.
761    /// This device will be closed when the stream is dropped.
762    /// The device begins paused, so you must call `stream.resume()` to start playback.
763    #[doc(alias = "SDL_OpenAudioDeviceStream")]
764    pub fn open_device_stream(self, spec: Option<&AudioSpec>) -> Result<AudioStreamOwner, Error> {
765        let sdl_spec = spec.map(|spec| spec.into());
766        let sdl_spec_ptr = crate::util::option_to_ptr(sdl_spec.as_ref());
767
768        let stream = unsafe {
769            sys::audio::SDL_OpenAudioDeviceStream(
770                self.device_id.id(),
771                sdl_spec_ptr,
772                // not using callbacks here
773                None,
774                std::ptr::null_mut(),
775            )
776        };
777        if stream.is_null() {
778            Err(get_error())
779        } else {
780            // SDL will close the device when the stream is closed
781            core::mem::forget(self);
782            let audio_subsystem = unsafe { AudioSubsystem::new_unchecked() };
783
784            Ok(AudioStreamOwner {
785                inner: AudioStream { stream },
786                audio_subsystem: audio_subsystem.into(),
787            })
788        }
789    }
790
791    /// Binds an audio stream to this device.
792    #[doc(alias = "SDL_BindAudioStream")]
793    pub fn bind_stream(&self, stream: &AudioStream) -> Result<(), Error> {
794        let result = unsafe { sys::audio::SDL_BindAudioStream(self.device_id.id(), stream.stream) };
795        if result {
796            Ok(())
797        } else {
798            Err(get_error())
799        }
800    }
801
802    /// Binds multiple audio streams to this device.
803    #[doc(alias = "SDL_BindAudioStreams")]
804    pub fn bind_streams(&self, streams: &[&AudioStream]) -> Result<(), Error> {
805        let streams_ptrs: Vec<*mut sys::audio::SDL_AudioStream> =
806            streams.iter().map(|s| s.stream).collect();
807        let result = unsafe {
808            sys::audio::SDL_BindAudioStreams(
809                self.device_id.id(),
810                streams_ptrs.as_ptr() as *mut _,
811                streams.len() as i32,
812            )
813        };
814        if result {
815            Ok(())
816        } else {
817            Err(get_error())
818        }
819    }
820
821    /// Opens a new audio device for playback or recording (given the desired parameters).
822    #[doc(alias = "SDL_OpenAudioDevice")]
823    fn open<'a, D>(
824        device: D,
825        spec: &AudioSpec,
826        recording: bool,
827        audio_subsystem: &AudioSubsystem,
828    ) -> Result<AudioDevice, Error>
829    where
830        D: Into<Option<&'a AudioDeviceID>>,
831    {
832        let desired = AudioSpec::convert_to_ll(spec.freq, spec.channels, spec.format);
833
834        unsafe {
835            let sdl_device = match device.into() {
836                Some(device) => device.id(),
837                // use default device if no device is specified
838                None => {
839                    if recording {
840                        SDL_AUDIO_DEVICE_DEFAULT_RECORDING
841                    } else {
842                        SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK
843                    }
844                }
845            };
846            let device_id = sys::audio::SDL_OpenAudioDevice(sdl_device, &desired);
847            match device_id {
848                0 => Err(get_error()),
849                id => {
850                    let device_id = AudioDeviceID::Device(id);
851
852                    Ok(AudioDevice::new(device_id, audio_subsystem.clone()))
853                }
854            }
855        }
856    }
857
858    /// Opens a new audio device for playback (given the desired parameters).
859    pub fn open_playback<'a, D>(
860        _a: &AudioSubsystem,
861        device: D,
862        spec: &AudioSpec,
863    ) -> Result<AudioDevice, Error>
864    where
865        D: Into<Option<&'a AudioDeviceID>>,
866    {
867        AudioDevice::open(device, spec, false, _a)
868    }
869
870    /// Opens a new audio device for recording (given the desired parameters).
871    pub fn open_recording<'a, D>(
872        _a: &AudioSubsystem,
873        device: D,
874        spec: &AudioSpec,
875    ) -> Result<AudioDevice, Error>
876    where
877        D: Into<Option<&'a AudioDeviceID>>,
878    {
879        AudioDevice::open(device, spec, true, _a)
880    }
881
882    /// Pauses playback of the audio device.
883    #[doc(alias = "SDL_PauseAudioDevice")]
884    pub fn pause(&self) -> bool {
885        unsafe { sys::audio::SDL_PauseAudioDevice(self.device_id.id()) }
886    }
887
888    /// Starts playback of the audio device.
889    #[doc(alias = "SDL_ResumeAudioDevice")]
890    pub fn resume(&self) -> bool {
891        unsafe { sys::audio::SDL_ResumeAudioDevice(self.device_id.id()) }
892    }
893
894    /// Opens a new audio stream for this device with the specified spec.
895    /// The device begins paused, so you must call `stream.resume()` to start playback.
896    #[doc(alias = "SDL_OpenAudioDeviceStream")]
897    pub fn open_playback_stream_with_callback<CB, Channel>(
898        &self,
899        spec: &AudioSpec,
900        callback: CB,
901    ) -> Result<AudioStreamWithCallback<CB>, Error>
902    where
903        CB: AudioCallback<Channel>,
904        Channel: AudioFormatNum + 'static,
905    {
906        let sdl_audiospec: sys::audio::SDL_AudioSpec = spec.clone().into();
907
908        if sdl_audiospec.format != Channel::audio_format().to_ll() {
909            return Err(Error(
910                "AudioSpec format does not match AudioCallback Channel type".to_string(),
911            ));
912        }
913
914        let callback_box = Box::new(callback);
915        let c_userdata = Box::into_raw(callback_box) as *mut c_void;
916
917        unsafe extern "C" fn audio_stream_callback<CB, Channel>(
918            userdata: *mut c_void,
919            sdl_stream: *mut sys::audio::SDL_AudioStream,
920            len: c_int,
921            _bytes: c_int,
922        ) where
923            CB: AudioCallback<Channel>,
924            Channel: AudioFormatNum + 'static,
925        {
926            let callback = &mut *(userdata as *mut CB);
927
928            let mut stream = AudioStream { stream: sdl_stream };
929
930            callback.callback(&mut stream, len / size_of::<Channel>() as i32);
931        }
932
933        unsafe {
934            let stream = sys::audio::SDL_OpenAudioDeviceStream(
935                self.device_id.id(),
936                &sdl_audiospec,
937                Some(audio_stream_callback::<CB, Channel>),
938                c_userdata,
939            );
940
941            if stream.is_null() {
942                // Drop the callback box
943                let _ = Box::from_raw(c_userdata as *mut CB);
944                Err(get_error())
945            } else {
946                Ok(AudioStreamWithCallback {
947                    base_stream: AudioStreamOwner {
948                        inner: AudioStream { stream },
949                        audio_subsystem: self.audio_subsystem.clone().into(),
950                    },
951                    _marker: PhantomData,
952                    c_userdata,
953                })
954            }
955        }
956    }
957
958    /// Opens a new audio stream for recording with the specified spec.
959    /// The device begins paused, so you must call `stream.resume()` to start recording.
960    #[doc(alias = "SDL_OpenAudioDeviceStream")]
961    pub fn open_recording_stream_with_callback<CB, Channel>(
962        &self,
963        spec: &AudioSpec,
964        callback: CB,
965    ) -> Result<AudioStreamWithCallback<CB>, Error>
966    where
967        CB: AudioRecordingCallback<Channel>,
968        Channel: AudioFormatNum + 'static,
969    {
970        // Convert Rust AudioSpec to SDL_AudioSpec
971        let sdl_audiospec: sys::audio::SDL_AudioSpec = spec.clone().into();
972
973        if sdl_audiospec.format != Channel::audio_format().to_ll() {
974            return Err(Error(
975                "AudioSpec format does not match AudioCallback Channel type".to_string(),
976            ));
977        }
978
979        let callback_box = Box::new(callback);
980        let c_userdata = Box::into_raw(callback_box) as *mut c_void;
981
982        unsafe {
983            let stream = sys::audio::SDL_OpenAudioDeviceStream(
984                self.device_id.id(),
985                &sdl_audiospec,
986                Some(audio_recording_stream_callback::<CB, Channel>),
987                c_userdata,
988            );
989
990            if stream.is_null() {
991                // Drop the callback box
992                let _ = Box::from_raw(c_userdata as *mut CB);
993                Err(get_error())
994            } else {
995                Ok(AudioStreamWithCallback {
996                    base_stream: AudioStreamOwner {
997                        inner: AudioStream { stream },
998                        audio_subsystem: self.audio_subsystem.clone().into(),
999                    },
1000                    _marker: PhantomData,
1001                    c_userdata,
1002                })
1003            }
1004        }
1005    }
1006}
1007
1008pub struct AudioStreamOwner {
1009    inner: AudioStream,
1010    #[expect(dead_code, reason = "keep the audio subsystem alive")]
1011    audio_subsystem: Option<AudioSubsystem>,
1012}
1013
1014pub struct AudioStream {
1015    stream: *mut sys::audio::SDL_AudioStream,
1016}
1017
1018impl Deref for AudioStreamOwner {
1019    type Target = AudioStream;
1020
1021    fn deref(&self) -> &Self::Target {
1022        &self.inner
1023    }
1024}
1025
1026impl DerefMut for AudioStreamOwner {
1027    fn deref_mut(&mut self) -> &mut Self::Target {
1028        &mut self.inner
1029    }
1030}
1031
1032impl Drop for AudioStreamOwner {
1033    /// Destroys the audio stream, unbinding it automatically from the device.
1034    /// If this stream was created with SDL_OpenAudioDeviceStream, the audio device that was opened alongside this stream’s creation will be closed, too.
1035    fn drop(&mut self) {
1036        if !self.inner.stream.is_null() {
1037            unsafe {
1038                sys::audio::SDL_DestroyAudioStream(self.inner.stream);
1039            }
1040            self.inner.stream = std::ptr::null_mut();
1041        }
1042    }
1043}
1044
1045impl Debug for AudioStream {
1046    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1047        // Get the device "name [ID]"
1048        let device_name = self
1049            .device_id()
1050            .and_then(|id| id.name().ok())
1051            .unwrap_or("Unknown".to_string());
1052        let device_name = format!(
1053            "{} [{}]",
1054            device_name,
1055            self.device_id().map(|id| id.id()).unwrap_or(0)
1056        );
1057
1058        // Get the audio specs
1059        let (src_spec, dst_spec) = match self.get_format() {
1060            Ok((src, dst)) => (Some(src), Some(dst)),
1061            Err(_) => (None, None),
1062        };
1063
1064        // Get the gain
1065        let gain = self.get_gain().ok();
1066
1067        // Begin building the debug struct
1068        let mut ds = f.debug_struct("AudioStream");
1069
1070        ds.field("device", &device_name);
1071
1072        if let Some(src_spec) = src_spec {
1073            ds.field("src_spec", &src_spec);
1074        } else {
1075            ds.field("src_spec", &"Unknown");
1076        }
1077
1078        if let Some(dst_spec) = dst_spec {
1079            ds.field("dst_spec", &dst_spec);
1080        } else {
1081            ds.field("dst_spec", &"Unknown");
1082        }
1083
1084        if let Some(gain) = gain {
1085            ds.field("gain", &gain);
1086        } else {
1087            ds.field("gain", &"Unknown");
1088        }
1089
1090        ds.finish()
1091    }
1092}
1093impl Display for AudioStream {
1094    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1095        if let Some(name) = self.device_id().and_then(|id| id.name().ok()) {
1096            write!(f, "AudioStream({})", name)
1097        } else {
1098            write!(f, "AudioStream")
1099        }
1100    }
1101}
1102
1103impl AudioStream {
1104    /// Get the SDL_AudioStream pointer.
1105    #[doc(alias = "SDL_AudioStream")]
1106    pub fn stream(&mut self) -> *mut sys::audio::SDL_AudioStream {
1107        self.stream
1108    }
1109
1110    /// Get the device ID bound to the stream.
1111    /// If the stream is not bound to a device, this will return `None`.
1112    #[doc(alias = "SDL_GetAudioStreamDevice")]
1113    pub fn device_id(&self) -> Option<AudioDeviceID> {
1114        let device_id = unsafe { sys::audio::SDL_GetAudioStreamDevice(self.stream) };
1115        // If not bound, or invalid, this returns zero, which is not a valid device ID.
1116        if device_id != 0 {
1117            Some(AudioDeviceID::Device(device_id))
1118        } else {
1119            None
1120        }
1121    }
1122
1123    pub fn device_name(&self) -> Option<String> {
1124        self.device_id().and_then(|id| id.name().ok())
1125    }
1126
1127    /// Retrieves the source and destination formats of the audio stream.
1128    ///
1129    /// Returns a tuple `(src_spec, dst_spec)` where each is an `Option<AudioSpec>`.
1130    #[doc(alias = "SDL_GetAudioStreamFormat")]
1131    pub fn get_format(&self) -> Result<(Option<AudioSpec>, Option<AudioSpec>), Error> {
1132        let mut sdl_src_spec = AudioSpec::default().into();
1133        let mut sdl_dst_spec = AudioSpec::default().into();
1134        let result = unsafe {
1135            sys::audio::SDL_GetAudioStreamFormat(self.stream, &mut sdl_src_spec, &mut sdl_dst_spec)
1136        };
1137        if result {
1138            let src_spec = if sdl_src_spec.format != sys::audio::SDL_AUDIO_UNKNOWN {
1139                Some(AudioSpec::from(&sdl_src_spec))
1140            } else {
1141                None
1142            };
1143            let dst_spec = if sdl_dst_spec.format != sys::audio::SDL_AUDIO_UNKNOWN {
1144                Some(AudioSpec::from(&sdl_dst_spec))
1145            } else {
1146                None
1147            };
1148            Ok((src_spec, dst_spec))
1149        } else {
1150            Err(get_error())
1151        }
1152    }
1153
1154    /// Retrieves the gain of the audio stream.
1155    ///
1156    /// Returns the gain as a `f32` on success, or an error message on failure.
1157    #[doc(alias = "SDL_GetAudioStreamGain")]
1158    pub fn get_gain(&self) -> Result<f32, Error> {
1159        let gain = unsafe { sys::audio::SDL_GetAudioStreamGain(self.stream) };
1160        if gain >= 0.0 {
1161            Ok(gain)
1162        } else {
1163            Err(get_error())
1164        }
1165    }
1166
1167    /// Pauses playback of the audio stream.
1168    #[doc(alias = "SDL_PauseAudioStream")]
1169    pub fn pause(&self) -> Result<(), Error> {
1170        let result = unsafe { sys::audio::SDL_PauseAudioStreamDevice(self.stream) };
1171        if result {
1172            Ok(())
1173        } else {
1174            Err(get_error())
1175        }
1176    }
1177
1178    /// Resumes playback of the audio stream.
1179    #[doc(alias = "SDL_ResumeAudioStream")]
1180    pub fn resume(&self) -> Result<(), Error> {
1181        let result = unsafe { sys::audio::SDL_ResumeAudioStreamDevice(self.stream) };
1182        if result {
1183            Ok(())
1184        } else {
1185            Err(get_error())
1186        }
1187    }
1188
1189    /// Gets the number of converted/resampled bytes available.
1190    #[doc(alias = "SDL_GetAudioStreamAvailable")]
1191    pub fn available_bytes(&self) -> Result<i32, Error> {
1192        let available = unsafe { sys::audio::SDL_GetAudioStreamAvailable(self.stream) };
1193        if available == -1 {
1194            Err(get_error())
1195        } else {
1196            Ok(available)
1197        }
1198    }
1199
1200    /// Converts a slice of bytes to a f32 sample based on AudioFormat.
1201    /// Returns a Result containing the converted f32 or an error message.
1202    fn read_bytes_to_f32(&self, chunk: &[u8]) -> Result<f32, Error> {
1203        // TODO: store specs so we don't have to call get_format every time
1204        let (_, output_spec) = self.get_format()?;
1205        match output_spec.unwrap().format {
1206            Some(AudioFormat::F32LE) => {
1207                Ok(f32::from_le_bytes(chunk.try_into().map_err(|_| {
1208                    Error("Invalid byte slice length for f32 LE".to_owned())
1209                })?))
1210            }
1211            Some(AudioFormat::F32BE) => {
1212                Ok(f32::from_be_bytes(chunk.try_into().map_err(|_| {
1213                    Error("Invalid byte slice length for f32 BE".to_owned())
1214                })?))
1215            }
1216            _ => Err(Error(
1217                "Unsupported AudioFormat for f32 conversion".to_string(),
1218            )),
1219        }
1220    }
1221
1222    /// Converts a slice of bytes to an i16 sample based on AudioFormat.
1223    /// Returns a Result containing the converted i16 or an error message.
1224    fn read_bytes_to_i16(&self, chunk: &[u8]) -> Result<i16, Error> {
1225        // TODO: store specs so we don't have to call get_format every time
1226        let (_, output_spec) = self.get_format()?;
1227        match output_spec.unwrap().format {
1228            Some(AudioFormat::S16LE) => {
1229                Ok(i16::from_le_bytes(chunk.try_into().map_err(|_| {
1230                    Error("Invalid byte slice length for i16 LE".to_owned())
1231                })?))
1232            }
1233            Some(AudioFormat::S16BE) => {
1234                Ok(i16::from_be_bytes(chunk.try_into().map_err(|_| {
1235                    Error("Invalid byte slice length for i16 BE".to_owned())
1236                })?))
1237            }
1238            _ => Err(Error(
1239                "Unsupported AudioFormat for i16 conversion".to_string(),
1240            )),
1241        }
1242    }
1243
1244    /// Reads samples as f32 into the provided buffer.
1245    /// Returns the number of samples read.
1246    pub fn read_f32_samples(&mut self, buf: &mut [f32]) -> io::Result<usize> {
1247        let byte_len = std::mem::size_of_val(buf);
1248        let mut byte_buf = vec![0u8; byte_len];
1249
1250        // Read bytes from the stream and capture the number of bytes read
1251        let bytes_read = self.read(&mut byte_buf)?;
1252
1253        // Calculate the number of complete samples read
1254        let samples_read = bytes_read / size_of::<f32>();
1255
1256        // Iterate over each complete sample
1257        for (i, v) in buf.iter_mut().enumerate().take(samples_read) {
1258            let start = i * size_of::<f32>();
1259            let end = start + size_of::<f32>();
1260            let chunk = &byte_buf[start..end];
1261
1262            // Convert bytes to f32 and handle potential errors
1263            *v = self
1264                .read_bytes_to_f32(chunk)
1265                .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
1266        }
1267
1268        Ok(samples_read)
1269    }
1270
1271    /// Reads samples as i16 into the provided buffer.
1272    /// Returns the number of samples read.
1273    pub fn read_i16_samples(&mut self, buf: &mut [i16]) -> io::Result<usize> {
1274        let byte_len = std::mem::size_of_val(buf);
1275        let mut byte_buf = vec![0u8; byte_len];
1276
1277        // Read bytes from the stream and capture the number of bytes read
1278        let bytes_read = self.read(&mut byte_buf)?;
1279
1280        // Calculate the number of complete samples read
1281        let samples_read = bytes_read / size_of::<i16>();
1282
1283        // Iterate over each complete sample
1284        for (i, v) in buf.iter_mut().enumerate().take(samples_read) {
1285            let start = i * size_of::<i16>();
1286            let end = start + size_of::<i16>();
1287            let chunk = &byte_buf[start..end];
1288
1289            // Convert bytes to i16 and handle potential errors
1290            *v = self
1291                .read_bytes_to_i16(chunk)
1292                .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
1293        }
1294
1295        Ok(samples_read)
1296    }
1297
1298    /// Adds data to the stream.
1299    pub fn put_data(&self, buf: &[u8]) -> Result<(), Error> {
1300        let result = unsafe {
1301            sys::audio::SDL_PutAudioStreamData(self.stream, buf.as_ptr().cast(), buf.len() as i32)
1302        };
1303        if result {
1304            Ok(())
1305        } else {
1306            Err(get_error())
1307        }
1308    }
1309
1310    /// Adds data to the stream (16-bit signed).
1311    pub fn put_data_i16(&self, buf: &[i16]) -> Result<(), Error> {
1312        let result = unsafe {
1313            sys::audio::SDL_PutAudioStreamData(
1314                self.stream,
1315                buf.as_ptr().cast(),
1316                buf.len() as i32 * size_of::<i16>() as i32,
1317            )
1318        };
1319        if result {
1320            Ok(())
1321        } else {
1322            Err(get_error())
1323        }
1324    }
1325
1326    /// Adds data to the stream (32-bit float).
1327    pub fn put_data_f32(&self, buf: &[f32]) -> Result<(), Error> {
1328        let result = unsafe {
1329            sys::audio::SDL_PutAudioStreamData(
1330                self.stream,
1331                buf.as_ptr().cast(),
1332                buf.len() as i32 * size_of::<f32>() as i32,
1333            )
1334        };
1335        if result {
1336            Ok(())
1337        } else {
1338            Err(get_error())
1339        }
1340    }
1341}
1342
1343impl Read for AudioStream {
1344    /// Reads audio data from the stream.
1345    /// Note that this reads bytes from the stream, not samples.
1346    /// You must convert the bytes to samples based on the format of the stream.
1347    /// `read_f32_samples` and `read_i16_samples` are provided for convenience.
1348    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
1349        let ret = unsafe {
1350            sys::audio::SDL_GetAudioStreamData(
1351                self.stream,
1352                buf.as_mut_ptr().cast(),
1353                buf.len() as c_int,
1354            )
1355        };
1356        if ret == -1 {
1357            Err(io::Error::new(io::ErrorKind::Other, get_error()))
1358        } else {
1359            Ok(ret as usize)
1360        }
1361    }
1362}
1363
1364// Streams with callbacks
1365pub struct AudioStreamWithCallback<CB> {
1366    base_stream: AudioStreamOwner,
1367    c_userdata: *mut c_void,
1368    _marker: PhantomData<CB>,
1369}
1370
1371impl<CB> Drop for AudioStreamWithCallback<CB> {
1372    fn drop(&mut self) {
1373        // `base_stream` will be dropped automatically.
1374        if !self.c_userdata.is_null() {
1375            unsafe {
1376                // Drop the callback box
1377                let _ = Box::from_raw(self.c_userdata as *mut CB);
1378            }
1379            self.c_userdata = std::ptr::null_mut();
1380        }
1381    }
1382}
1383
1384impl<CB> AudioStreamWithCallback<CB> {
1385    /// Pauses the audio stream.
1386    pub fn pause(&self) -> Result<(), Error> {
1387        self.base_stream.pause()
1388    }
1389
1390    /// Resumes the audio stream.
1391    pub fn resume(&self) -> Result<(), Error> {
1392        self.base_stream.resume()
1393    }
1394
1395    pub fn lock(&mut self) -> Option<AudioStreamLockGuard<CB>> {
1396        let raw_stream = self.base_stream.stream;
1397        let result = unsafe { sys::audio::SDL_LockAudioStream(raw_stream) };
1398
1399        if result {
1400            Some(AudioStreamLockGuard {
1401                stream: self,
1402                _nosend: PhantomData,
1403            })
1404        } else {
1405            None
1406        }
1407    }
1408}
1409
1410pub trait AudioRecordingCallback<Channel>: Send + 'static
1411where
1412    Channel: AudioFormatNum + 'static,
1413{
1414    fn callback(&mut self, stream: &mut AudioStream, available: i32);
1415}
1416
1417unsafe extern "C" fn audio_recording_stream_callback<CB, Channel>(
1418    userdata: *mut c_void,
1419    sdl_stream: *mut sys::audio::SDL_AudioStream,
1420    len: c_int,
1421    _bytes: c_int,
1422) where
1423    CB: AudioRecordingCallback<Channel>,
1424    Channel: AudioFormatNum + 'static,
1425{
1426    let callback = &mut *(userdata as *mut CB);
1427
1428    let mut stream = AudioStream { stream: sdl_stream };
1429
1430    // Call the user's callback with the captured audio data
1431    callback.callback(&mut stream, len / size_of::<Channel>() as c_int);
1432}
1433
1434/// Similar to `std::sync::MutexGuard`, but for use with `AudioStream::lock()`.
1435pub struct AudioStreamLockGuard<'a, CB>
1436where
1437    CB: 'a,
1438{
1439    stream: &'a mut AudioStreamWithCallback<CB>,
1440    _nosend: PhantomData<*mut ()>,
1441}
1442
1443impl<'a, CB> Deref for AudioStreamLockGuard<'a, CB>
1444where
1445    CB: 'a,
1446{
1447    type Target = CB;
1448    #[doc(alias = "SDL_UnlockAudioStream")]
1449    fn deref(&self) -> &CB {
1450        unsafe {
1451            (self.stream.c_userdata as *const CB)
1452                .as_ref()
1453                .expect("Missing callback")
1454        }
1455    }
1456}
1457
1458impl<'a, CB> DerefMut for AudioStreamLockGuard<'a, CB> {
1459    fn deref_mut(&mut self) -> &mut CB {
1460        unsafe {
1461            (self.stream.c_userdata as *mut CB)
1462                .as_mut()
1463                .expect("Missing callback")
1464        }
1465    }
1466}
1467
1468impl<'a, CB> Drop for AudioStreamLockGuard<'a, CB> {
1469    fn drop(&mut self) {
1470        unsafe {
1471            sys::audio::SDL_UnlockAudioStream(self.stream.base_stream.stream);
1472        }
1473    }
1474}
1475
1476#[cfg(test)]
1477mod test {}