[go: up one dir, main page]

sdl2/
audio.rs

1//! Audio Functions
2//!
3//! # Example
4//! ```no_run
5//! use sdl2::audio::{AudioCallback, AudioSpecDesired};
6//! use std::time::Duration;
7//!
8//! struct SquareWave {
9//!     phase_inc: f32,
10//!     phase: f32,
11//!     volume: f32
12//! }
13//!
14//! impl AudioCallback for SquareWave {
15//!     type Channel = f32;
16//!
17//!     fn callback(&mut self, out: &mut [f32]) {
18//!         // Generate a square wave
19//!         for x in out.iter_mut() {
20//!             *x = if self.phase <= 0.5 {
21//!                 self.volume
22//!             } else {
23//!                 -self.volume
24//!             };
25//!             self.phase = (self.phase + self.phase_inc) % 1.0;
26//!         }
27//!     }
28//! }
29//!
30//! let sdl_context = sdl2::init().unwrap();
31//! let audio_subsystem = sdl_context.audio().unwrap();
32//!
33//! let desired_spec = AudioSpecDesired {
34//!     freq: Some(44100),
35//!     channels: Some(1),  // mono
36//!     samples: None       // default sample size
37//! };
38//!
39//! let device = audio_subsystem.open_playback(None, &desired_spec, |spec| {
40//!     // initialize the audio callback
41//!     SquareWave {
42//!         phase_inc: 440.0 / spec.freq as f32,
43//!         phase: 0.0,
44//!         volume: 0.25
45//!     }
46//! }).unwrap();
47//!
48//! // Start playback
49//! device.resume();
50//!
51//! // Play for 2 seconds
52//! std::thread::sleep(Duration::from_millis(2000));
53//! ```
54
55use libc::{c_char, c_int, c_void};
56use std::convert::TryFrom;
57use std::ffi::{CStr, CString};
58use std::marker::PhantomData;
59use std::mem;
60use std::ops::{Deref, DerefMut};
61use std::path::Path;
62use std::ptr;
63
64use crate::get_error;
65use crate::rwops::RWops;
66use crate::AudioSubsystem;
67
68use crate::sys;
69use crate::sys::SDL_AudioStatus;
70
71impl AudioSubsystem {
72    /// Opens a new audio device given the desired parameters and callback.
73    ///
74    /// If you want to modify the callback-owned data at a later point (for example to update
75    /// its data buffer) you're likely to be interested in the
76    /// [AudioDevice.lock method](audio/struct.AudioDevice.html#method.lock).
77    #[inline]
78    pub fn open_playback<'a, CB, F, D>(
79        &self,
80        device: D,
81        spec: &AudioSpecDesired,
82        get_callback: F,
83    ) -> Result<AudioDevice<CB>, String>
84    where
85        CB: AudioCallback,
86        F: FnOnce(AudioSpec) -> CB,
87        D: Into<Option<&'a str>>,
88    {
89        AudioDevice::open_playback(self, device, spec, get_callback)
90    }
91
92    /// Opens a new audio device for capture (given the desired parameters and callback).
93    /// Supported since SDL 2.0.5
94    ///
95    /// If you want to modify the callback-owned data at a later point (for example to update
96    /// its data buffer) you're likely to be interested in the
97    /// [AudioDevice.lock method](audio/struct.AudioDevice.html#method.lock).
98    pub fn open_capture<'a, CB, F, D>(
99        &self,
100        device: D,
101        spec: &AudioSpecDesired,
102        get_callback: F,
103    ) -> Result<AudioDevice<CB>, String>
104    where
105        CB: AudioCallback,
106        F: FnOnce(AudioSpec) -> CB,
107        D: Into<Option<&'a str>>,
108    {
109        AudioDevice::open_capture(self, device, spec, get_callback)
110    }
111
112    /// Opens a new audio device which uses queueing rather than older callback method.
113    #[inline]
114    pub fn open_queue<'a, Channel, D>(
115        &self,
116        device: D,
117        spec: &AudioSpecDesired,
118    ) -> Result<AudioQueue<Channel>, String>
119    where
120        Channel: AudioFormatNum,
121        D: Into<Option<&'a str>>,
122    {
123        AudioQueue::open_queue(self, device, spec)
124    }
125
126    #[doc(alias = "SDL_GetCurrentAudioDriver")]
127    pub fn current_audio_driver(&self) -> &'static str {
128        unsafe {
129            let buf = sys::SDL_GetCurrentAudioDriver();
130            assert!(!buf.is_null());
131
132            CStr::from_ptr(buf as *const _).to_str().unwrap()
133        }
134    }
135
136    #[doc(alias = "SDL_GetNumAudioDevices")]
137    pub fn num_audio_playback_devices(&self) -> Option<u32> {
138        let result = unsafe { sys::SDL_GetNumAudioDevices(0) };
139        if result < 0 {
140            // SDL cannot retrieve a list of audio devices. This is not necessarily an error (see the SDL2 docs).
141            None
142        } else {
143            Some(result as u32)
144        }
145    }
146
147    #[doc(alias = "SDL_GetNumAudioDevices")]
148    pub fn num_audio_capture_devices(&self) -> Option<u32> {
149        let result = unsafe { sys::SDL_GetNumAudioDevices(1) };
150        if result < 0 {
151            // SDL cannot retrieve a list of audio devices. This is not necessarily an error (see the SDL2 docs).
152            None
153        } else {
154            Some(result as u32)
155        }
156    }
157
158    #[doc(alias = "SDL_GetAudioDeviceName")]
159    pub fn audio_playback_device_name(&self, index: u32) -> Result<String, String> {
160        unsafe {
161            let dev_name = sys::SDL_GetAudioDeviceName(index as c_int, 0);
162            if dev_name.is_null() {
163                Err(get_error())
164            } else {
165                let cstr = CStr::from_ptr(dev_name as *const _);
166                Ok(cstr.to_str().unwrap().to_owned())
167            }
168        }
169    }
170
171    #[doc(alias = "SDL_GetAudioDeviceName")]
172    pub fn audio_capture_device_name(&self, index: u32) -> Result<String, String> {
173        unsafe {
174            let dev_name = sys::SDL_GetAudioDeviceName(index as c_int, 1);
175            if dev_name.is_null() {
176                Err(get_error())
177            } else {
178                let cstr = CStr::from_ptr(dev_name as *const _);
179                Ok(cstr.to_str().unwrap().to_owned())
180            }
181        }
182    }
183
184    #[doc(alias = "SDL_GetAudioDeviceSpec")]
185    pub fn audio_playback_device_spec(&self, index: u32) -> Result<AudioSpec, String> {
186        let mut spec = sys::SDL_AudioSpec {
187            freq: 0,
188            format: 0,
189            channels: 0,
190            silence: 0,
191            samples: 0,
192            padding: 0,
193            size: 0,
194            callback: None,
195            userdata: ptr::null_mut(),
196        };
197
198        let result = unsafe { sys::SDL_GetAudioDeviceSpec(index as c_int, 0, &mut spec) };
199        if result != 0 {
200            Err(get_error())
201        } else {
202            Ok(AudioSpec::convert_from_ll(spec))
203        }
204    }
205    #[doc(alias = "SDL_GetAudioDeviceSpec")]
206    pub fn audio_capture_device_spec(&self, index: u32) -> Result<AudioSpec, String> {
207        let mut spec = sys::SDL_AudioSpec {
208            freq: 0,
209            format: 0,
210            channels: 0,
211            silence: 0,
212            samples: 0,
213            padding: 0,
214            size: 0,
215            callback: None,
216            userdata: ptr::null_mut(),
217        };
218
219        let result = unsafe { sys::SDL_GetAudioDeviceSpec(index as c_int, 1, &mut spec) };
220        if result != 0 {
221            Err(get_error())
222        } else {
223            Ok(AudioSpec::convert_from_ll(spec))
224        }
225    }
226}
227
228#[repr(i32)]
229#[derive(Copy, Clone, Eq, PartialEq, Debug, Hash)]
230pub enum AudioFormat {
231    /// Unsigned 8-bit samples
232    U8 = sys::AUDIO_U8 as i32,
233    /// Signed 8-bit samples
234    S8 = sys::AUDIO_S8 as i32,
235    /// Unsigned 16-bit samples, little-endian
236    U16LSB = sys::AUDIO_U16LSB as i32,
237    /// Unsigned 16-bit samples, big-endian
238    U16MSB = sys::AUDIO_U16MSB as i32,
239    /// Signed 16-bit samples, little-endian
240    S16LSB = sys::AUDIO_S16LSB as i32,
241    /// Signed 16-bit samples, big-endian
242    S16MSB = sys::AUDIO_S16MSB as i32,
243    /// Signed 32-bit samples, little-endian
244    S32LSB = sys::AUDIO_S32LSB as i32,
245    /// Signed 32-bit samples, big-endian
246    S32MSB = sys::AUDIO_S32MSB as i32,
247    /// 32-bit floating point samples, little-endian
248    F32LSB = sys::AUDIO_F32LSB as i32,
249    /// 32-bit floating point samples, big-endian
250    F32MSB = sys::AUDIO_F32MSB as i32,
251}
252
253impl AudioFormat {
254    fn from_ll(raw: sys::SDL_AudioFormat) -> Option<AudioFormat> {
255        use self::AudioFormat::*;
256        match raw as u32 {
257            sys::AUDIO_U8 => Some(U8),
258            sys::AUDIO_S8 => Some(S8),
259            sys::AUDIO_U16LSB => Some(U16LSB),
260            sys::AUDIO_U16MSB => Some(U16MSB),
261            sys::AUDIO_S16LSB => Some(S16LSB),
262            sys::AUDIO_S16MSB => Some(S16MSB),
263            sys::AUDIO_S32LSB => Some(S32LSB),
264            sys::AUDIO_S32MSB => Some(S32MSB),
265            sys::AUDIO_F32LSB => Some(F32LSB),
266            sys::AUDIO_F32MSB => Some(F32MSB),
267            _ => None,
268        }
269    }
270
271    #[doc(alias = "SDL_AudioFormat")]
272    fn to_ll(self) -> sys::SDL_AudioFormat {
273        self as sys::SDL_AudioFormat
274    }
275}
276
277#[cfg(target_endian = "little")]
278impl AudioFormat {
279    /// Unsigned 16-bit samples, native endian
280    #[inline]
281    pub const fn u16_sys() -> AudioFormat {
282        AudioFormat::U16LSB
283    }
284    /// Signed 16-bit samples, native endian
285    #[inline]
286    pub const fn s16_sys() -> AudioFormat {
287        AudioFormat::S16LSB
288    }
289    /// Signed 32-bit samples, native endian
290    #[inline]
291    pub const fn s32_sys() -> AudioFormat {
292        AudioFormat::S32LSB
293    }
294    /// 32-bit floating point samples, native endian
295    #[inline]
296    pub const fn f32_sys() -> AudioFormat {
297        AudioFormat::F32LSB
298    }
299}
300
301#[cfg(target_endian = "big")]
302impl AudioFormat {
303    /// Unsigned 16-bit samples, native endian
304    #[inline]
305    pub const fn u16_sys() -> AudioFormat {
306        AudioFormat::U16MSB
307    }
308    /// Signed 16-bit samples, native endian
309    #[inline]
310    pub const fn s16_sys() -> AudioFormat {
311        AudioFormat::S16MSB
312    }
313    /// Signed 32-bit samples, native endian
314    #[inline]
315    pub const fn s32_sys() -> AudioFormat {
316        AudioFormat::S32MSB
317    }
318    /// 32-bit floating point samples, native endian
319    #[inline]
320    pub const fn f32_sys() -> AudioFormat {
321        AudioFormat::F32MSB
322    }
323}
324
325#[repr(i32)]
326#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
327pub enum AudioStatus {
328    Stopped = SDL_AudioStatus::SDL_AUDIO_STOPPED as i32,
329    Playing = SDL_AudioStatus::SDL_AUDIO_PLAYING as i32,
330    Paused = SDL_AudioStatus::SDL_AUDIO_PAUSED as i32,
331}
332
333impl TryFrom<u32> for AudioStatus {
334    type Error = ();
335
336    fn try_from(n: u32) -> Result<Self, Self::Error> {
337        use self::AudioStatus::*;
338        use crate::sys::SDL_AudioStatus::*;
339
340        const STOPPED: u32 = SDL_AUDIO_STOPPED as u32;
341        const PLAYING: u32 = SDL_AUDIO_PLAYING as u32;
342        const PAUSED: u32 = SDL_AUDIO_PAUSED as u32;
343
344        Ok(match n {
345            STOPPED => Stopped,
346            PLAYING => Playing,
347            PAUSED => Paused,
348            _ => return Err(()),
349        })
350    }
351}
352
353#[doc(alias = "SDL_GetAudioDriver")]
354#[derive(Copy, Clone)]
355pub struct DriverIterator {
356    length: i32,
357    index: i32,
358}
359
360// panics if SDL_GetAudioDriver returns a null pointer,
361// which only happens if index is outside the range
362// 0..SDL_GetNumAudioDrivers()
363fn get_audio_driver(index: i32) -> &'static str {
364    unsafe {
365        let buf = sys::SDL_GetAudioDriver(index);
366        assert!(!buf.is_null());
367
368        CStr::from_ptr(buf as *const _).to_str().unwrap()
369    }
370}
371
372impl Iterator for DriverIterator {
373    type Item = &'static str;
374
375    #[inline]
376    fn next(&mut self) -> Option<&'static str> {
377        if self.index >= self.length {
378            None
379        } else {
380            let driver = get_audio_driver(self.index);
381            self.index += 1;
382
383            Some(driver)
384        }
385    }
386
387    #[inline]
388    fn size_hint(&self) -> (usize, Option<usize>) {
389        let remaining = (self.length - self.index) as usize;
390        (remaining, Some(remaining))
391    }
392
393    #[inline]
394    fn nth(&mut self, n: usize) -> Option<&'static str> {
395        use std::convert::TryInto;
396
397        self.index = match n.try_into().ok().and_then(|n| self.index.checked_add(n)) {
398            Some(index) if index < self.length => index,
399            _ => self.length,
400        };
401
402        self.next()
403    }
404}
405
406impl DoubleEndedIterator for DriverIterator {
407    #[inline]
408    fn next_back(&mut self) -> Option<&'static str> {
409        if self.index >= self.length {
410            None
411        } else {
412            self.length -= 1;
413
414            Some(get_audio_driver(self.length))
415        }
416    }
417
418    #[inline]
419    fn nth_back(&mut self, n: usize) -> Option<&'static str> {
420        use std::convert::TryInto;
421
422        self.length = match n.try_into().ok().and_then(|n| self.length.checked_sub(n)) {
423            Some(length) if length > self.index => length,
424            _ => self.index,
425        };
426
427        self.next_back()
428    }
429}
430
431impl ExactSizeIterator for DriverIterator {}
432
433impl std::iter::FusedIterator for DriverIterator {}
434
435/// Gets an iterator of all audio drivers compiled into the SDL2 library.
436#[doc(alias = "SDL_GetAudioDriver")]
437#[inline]
438pub fn drivers() -> DriverIterator {
439    // This function is thread-safe and doesn't require the audio subsystem to be initialized.
440    // The list of drivers are read-only and statically compiled into SDL2, varying by platform.
441
442    // SDL_GetNumAudioDrivers can never return a negative value.
443    DriverIterator {
444        length: unsafe { sys::SDL_GetNumAudioDrivers() },
445        index: 0,
446    }
447}
448
449pub struct AudioSpecWAV {
450    pub freq: i32,
451    pub format: AudioFormat,
452    pub channels: u8,
453    audio_buf: *mut u8,
454    audio_len: u32,
455}
456
457impl AudioSpecWAV {
458    /// Loads a WAVE from the file path.
459    pub fn load_wav<P: AsRef<Path>>(path: P) -> Result<AudioSpecWAV, String> {
460        let mut file = RWops::from_file(path, "rb")?;
461        AudioSpecWAV::load_wav_rw(&mut file)
462    }
463
464    /// Loads a WAVE from the data source.
465    #[doc(alias = "SDL_LoadWAV_RW")]
466    pub fn load_wav_rw(src: &mut RWops) -> Result<AudioSpecWAV, String> {
467        use std::mem::MaybeUninit;
468        use std::ptr::null_mut;
469
470        let mut desired = MaybeUninit::uninit();
471        let mut audio_buf: *mut u8 = null_mut();
472        let mut audio_len: u32 = 0;
473        unsafe {
474            let ret = sys::SDL_LoadWAV_RW(
475                src.raw(),
476                0,
477                desired.as_mut_ptr(),
478                &mut audio_buf,
479                &mut audio_len,
480            );
481            if ret.is_null() {
482                Err(get_error())
483            } else {
484                let desired = desired.assume_init();
485                Ok(AudioSpecWAV {
486                    freq: desired.freq,
487                    format: AudioFormat::from_ll(desired.format).unwrap(),
488                    channels: desired.channels,
489                    audio_buf,
490                    audio_len,
491                })
492            }
493        }
494    }
495
496    pub fn buffer(&self) -> &[u8] {
497        use std::slice::from_raw_parts;
498        unsafe {
499            let ptr = self.audio_buf as *const u8;
500            let len = self.audio_len as usize;
501            from_raw_parts(ptr, len)
502        }
503    }
504}
505
506impl Drop for AudioSpecWAV {
507    #[doc(alias = "SDL_FreeWAV")]
508    fn drop(&mut self) {
509        unsafe {
510            sys::SDL_FreeWAV(self.audio_buf);
511        }
512    }
513}
514
515pub trait AudioCallback: Send
516where
517    Self::Channel: AudioFormatNum + 'static,
518{
519    type Channel;
520
521    fn callback(&mut self, _: &mut [Self::Channel]);
522}
523
524/// A phantom type for retrieving the `SDL_AudioFormat` of a given generic type.
525/// All format types are returned as native-endian.
526pub trait AudioFormatNum {
527    fn audio_format() -> AudioFormat;
528
529    /// The appropriately typed silence value for the audio format used.
530    ///
531    /// # Examples
532    ///
533    /// ```
534    /// // The AudioFormatNum trait has to be imported for the Channel::SILENCE part to work.
535    /// use sdl2::audio::{AudioCallback, AudioFormatNum};
536    ///
537    /// struct Silence;
538    ///
539    /// impl AudioCallback for Silence {
540    ///     type Channel = u16;
541    ///
542    ///     fn callback(&mut self, out: &mut [u16]) {
543    ///         for dst in out.iter_mut() {
544    ///             *dst = Self::Channel::SILENCE;
545    ///         }
546    ///     }
547    /// }
548    /// ```
549    const SILENCE: Self;
550}
551
552/// `AUDIO_S8`
553impl AudioFormatNum for i8 {
554    fn audio_format() -> AudioFormat {
555        AudioFormat::S8
556    }
557    const SILENCE: i8 = 0;
558}
559/// `AUDIO_U8`
560impl AudioFormatNum for u8 {
561    fn audio_format() -> AudioFormat {
562        AudioFormat::U8
563    }
564    const SILENCE: u8 = 0x80;
565}
566/// `AUDIO_S16`
567impl AudioFormatNum for i16 {
568    fn audio_format() -> AudioFormat {
569        AudioFormat::s16_sys()
570    }
571    const SILENCE: i16 = 0;
572}
573/// `AUDIO_U16`
574impl AudioFormatNum for u16 {
575    fn audio_format() -> AudioFormat {
576        AudioFormat::u16_sys()
577    }
578    const SILENCE: u16 = 0x8000;
579}
580/// `AUDIO_S32`
581impl AudioFormatNum for i32 {
582    fn audio_format() -> AudioFormat {
583        AudioFormat::s32_sys()
584    }
585    const SILENCE: i32 = 0;
586}
587/// `AUDIO_F32`
588impl AudioFormatNum for f32 {
589    fn audio_format() -> AudioFormat {
590        AudioFormat::f32_sys()
591    }
592    const SILENCE: f32 = 0.0;
593}
594
595extern "C" fn audio_callback_marshall<CB: AudioCallback>(
596    userdata: *mut c_void,
597    stream: *mut u8,
598    len: c_int,
599) {
600    use std::mem::size_of;
601    use std::slice::from_raw_parts_mut;
602    unsafe {
603        let cb_userdata: &mut Option<CB> = &mut *(userdata as *mut _);
604        let buf: &mut [CB::Channel] = from_raw_parts_mut(
605            stream as *mut CB::Channel,
606            len as usize / size_of::<CB::Channel>(),
607        );
608
609        if let Some(cb) = cb_userdata {
610            cb.callback(buf);
611        }
612    }
613}
614
615#[derive(Clone)]
616pub struct AudioSpecDesired {
617    /// DSP frequency (samples per second). Set to None for the device's fallback frequency.
618    pub freq: Option<i32>,
619    /// Number of separate audio channels. Set to None for the device's fallback number of channels.
620    pub channels: Option<u8>,
621    /// Audio buffer size in samples (power of 2). Set to None for the device's fallback sample size.
622    pub samples: Option<u16>,
623}
624
625impl AudioSpecDesired {
626    fn convert_to_ll<CB, F, C, S>(
627        freq: F,
628        channels: C,
629        samples: S,
630        userdata: *mut Option<CB>,
631    ) -> sys::SDL_AudioSpec
632    where
633        CB: AudioCallback,
634        F: Into<Option<i32>>,
635        C: Into<Option<u8>>,
636        S: Into<Option<u16>>,
637    {
638        let freq = freq.into();
639        let channels = channels.into();
640        let samples = samples.into();
641
642        if let Some(freq) = freq {
643            assert!(freq > 0);
644        }
645        if let Some(channels) = channels {
646            assert!(channels > 0);
647        }
648        if let Some(samples) = samples {
649            assert!(samples > 0);
650        }
651
652        // A value of 0 means "fallback" or "default".
653
654        sys::SDL_AudioSpec {
655            freq: freq.unwrap_or(0),
656            format: <CB::Channel as AudioFormatNum>::audio_format().to_ll(),
657            channels: channels.unwrap_or(0),
658            silence: 0,
659            samples: samples.unwrap_or(0),
660            padding: 0,
661            size: 0,
662            callback: Some(
663                audio_callback_marshall::<CB>
664                    as extern "C" fn(arg1: *mut c_void, arg2: *mut u8, arg3: c_int),
665            ),
666            userdata: userdata as *mut _,
667        }
668    }
669
670    fn convert_queue_to_ll<Channel, F, C, S>(freq: F, channels: C, samples: S) -> sys::SDL_AudioSpec
671    where
672        Channel: AudioFormatNum,
673        F: Into<Option<i32>>,
674        C: Into<Option<u8>>,
675        S: Into<Option<u16>>,
676    {
677        let freq = freq.into();
678        let channels = channels.into();
679        let samples = samples.into();
680
681        if let Some(freq) = freq {
682            assert!(freq > 0);
683        }
684        if let Some(channels) = channels {
685            assert!(channels > 0);
686        }
687        if let Some(samples) = samples {
688            assert!(samples > 0);
689        }
690
691        // A value of 0 means "fallback" or "default".
692
693        sys::SDL_AudioSpec {
694            freq: freq.unwrap_or(0),
695            format: <Channel as AudioFormatNum>::audio_format().to_ll(),
696            channels: channels.unwrap_or(0),
697            silence: 0,
698            samples: samples.unwrap_or(0),
699            padding: 0,
700            size: 0,
701            callback: None,
702            userdata: ptr::null_mut(),
703        }
704    }
705}
706
707#[allow(missing_copy_implementations)]
708#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
709pub struct AudioSpec {
710    pub freq: i32,
711    pub format: AudioFormat,
712    pub channels: u8,
713    /// The silence value calculated by SDL2. Note that it's inconvenient to use if your channel
714    /// type is not u8 and [incorrect in case of u16](https://bugzilla.libsdl.org/show_bug.cgi?id=4805).
715    /// You're likely to find [the `AudioFormatNum.SILENCE` associated constant](
716    /// trait.AudioFormatNum.html#associatedconstant.SILENCE) more useful.
717    pub silence: u8,
718    pub samples: u16,
719    pub size: u32,
720}
721
722impl AudioSpec {
723    fn convert_from_ll(spec: sys::SDL_AudioSpec) -> AudioSpec {
724        AudioSpec {
725            freq: spec.freq,
726            format: AudioFormat::from_ll(spec.format).unwrap(),
727            channels: spec.channels,
728            silence: spec.silence,
729            samples: spec.samples,
730            size: spec.size,
731        }
732    }
733}
734
735enum AudioDeviceID {
736    PlaybackDevice(sys::SDL_AudioDeviceID),
737}
738
739impl AudioDeviceID {
740    fn id(&self) -> sys::SDL_AudioDeviceID {
741        match *self {
742            AudioDeviceID::PlaybackDevice(id) => id,
743        }
744    }
745}
746
747impl Drop for AudioDeviceID {
748    #[doc(alias = "SDL_CloseAudioDevice")]
749    fn drop(&mut self) {
750        //! Shut down audio processing and close the audio device.
751        unsafe { sys::SDL_CloseAudioDevice(self.id()) }
752    }
753}
754
755/// Wraps `SDL_AudioDeviceID` and owns the callback data used by the audio device.
756pub struct AudioQueue<Channel: AudioFormatNum> {
757    subsystem: AudioSubsystem,
758    device_id: AudioDeviceID,
759    phantom: PhantomData<Channel>,
760    spec: AudioSpec,
761}
762
763impl<'a, Channel: AudioFormatNum> AudioQueue<Channel> {
764    /// Opens a new audio device given the desired parameters and callback.
765    #[doc(alias = "SDL_OpenAudioDevice")]
766    pub fn open_queue<D: Into<Option<&'a str>>>(
767        a: &AudioSubsystem,
768        device: D,
769        spec: &AudioSpecDesired,
770    ) -> Result<AudioQueue<Channel>, String> {
771        use std::mem::MaybeUninit;
772
773        let desired = AudioSpecDesired::convert_queue_to_ll::<
774            Channel,
775            Option<i32>,
776            Option<u8>,
777            Option<u16>,
778        >(spec.freq, spec.channels, spec.samples);
779
780        let mut obtained = MaybeUninit::uninit();
781        unsafe {
782            let device = device.into().map(|device| CString::new(device).unwrap());
783            // Warning: map_or consumes its argument; `device.map_or()` would therefore consume the
784            // CString and drop it, making device_ptr a dangling pointer! To avoid that we downgrade
785            // device to an Option<&_> first.
786            let device_ptr = device.as_ref().map_or(ptr::null(), |s| s.as_ptr());
787
788            let iscapture_flag = 0;
789            let device_id = sys::SDL_OpenAudioDevice(
790                device_ptr as *const c_char,
791                iscapture_flag,
792                &desired,
793                obtained.as_mut_ptr(),
794                0,
795            );
796            match device_id {
797                0 => Err(get_error()),
798                id => {
799                    let obtained = obtained.assume_init();
800                    let device_id = AudioDeviceID::PlaybackDevice(id);
801                    let spec = AudioSpec::convert_from_ll(obtained);
802
803                    Ok(AudioQueue {
804                        subsystem: a.clone(),
805                        device_id,
806                        phantom: PhantomData,
807                        spec,
808                    })
809                }
810            }
811        }
812    }
813
814    #[inline]
815    #[doc(alias = "SDL_GetAudioDeviceStatus")]
816    pub fn subsystem(&self) -> &AudioSubsystem {
817        &self.subsystem
818    }
819
820    #[inline]
821    pub fn spec(&self) -> &AudioSpec {
822        &self.spec
823    }
824
825    pub fn status(&self) -> AudioStatus {
826        unsafe {
827            let status = sys::SDL_GetAudioDeviceStatus(self.device_id.id());
828            AudioStatus::try_from(status as u32).unwrap()
829        }
830    }
831
832    /// Pauses playback of the audio device.
833    #[doc(alias = "SDL_PauseAudioDevice")]
834    pub fn pause(&self) {
835        unsafe { sys::SDL_PauseAudioDevice(self.device_id.id(), 1) }
836    }
837
838    /// Starts playback of the audio device.
839    #[doc(alias = "SDL_PauseAudioDevice")]
840    pub fn resume(&self) {
841        unsafe { sys::SDL_PauseAudioDevice(self.device_id.id(), 0) }
842    }
843
844    /// Adds data to the audio queue.
845    #[doc(alias = "SDL_QueueAudio")]
846    #[deprecated(
847        since = "0.35.2",
848        note = "Users should instead use AudioQueue::queue_audio"
849    )]
850    pub fn queue(&self, data: &[Channel]) -> bool {
851        let result = unsafe {
852            sys::SDL_QueueAudio(
853                self.device_id.id(),
854                data.as_ptr() as *const c_void,
855                mem::size_of_val(data) as u32,
856            )
857        };
858        result == 0
859    }
860
861    /// Adds data to the audio queue.
862    #[doc(alias = "SDL_QueueAudio")]
863    pub fn queue_audio(&self, data: &[Channel]) -> Result<(), String> {
864        let result = unsafe {
865            sys::SDL_QueueAudio(
866                self.device_id.id(),
867                data.as_ptr() as *const c_void,
868                mem::size_of_val(data) as u32,
869            )
870        };
871        if result == 0 {
872            Ok(())
873        } else {
874            Err(get_error())
875        }
876    }
877
878    #[doc(alias = "SDL_GetQueuedAudioSize")]
879    pub fn size(&self) -> u32 {
880        unsafe { sys::SDL_GetQueuedAudioSize(self.device_id.id()) }
881    }
882
883    /// Clears all data from the current audio queue.
884    #[doc(alias = "SDL_ClearQueuedAudio")]
885    pub fn clear(&self) {
886        unsafe {
887            sys::SDL_ClearQueuedAudio(self.device_id.id());
888        }
889    }
890}
891
892/// Wraps `SDL_AudioDeviceID` and owns the callback data used by the audio device.
893pub struct AudioDevice<CB: AudioCallback> {
894    subsystem: AudioSubsystem,
895    device_id: AudioDeviceID,
896    spec: AudioSpec,
897    /// Store the callback to keep it alive for the entire duration of `AudioDevice`.
898    userdata: Box<Option<CB>>,
899}
900
901impl<CB: AudioCallback> AudioDevice<CB> {
902    /// Opens a new audio device for playback or capture (given the desired parameters and callback).
903    #[doc(alias = "SDL_OpenAudioDevice")]
904    fn open<'a, F, D>(
905        a: &AudioSubsystem,
906        device: D,
907        spec: &AudioSpecDesired,
908        get_callback: F,
909        capture: bool,
910    ) -> Result<AudioDevice<CB>, String>
911    where
912        F: FnOnce(AudioSpec) -> CB,
913        D: Into<Option<&'a str>>,
914    {
915        use std::mem::MaybeUninit;
916
917        let mut userdata: Box<Option<CB>> = Box::new(None);
918        let desired =
919            AudioSpecDesired::convert_to_ll(spec.freq, spec.channels, spec.samples, &mut *userdata);
920
921        let mut obtained = MaybeUninit::uninit();
922        unsafe {
923            let device = device.into().map(|device| CString::new(device).unwrap());
924            // Warning: map_or consumes its argument; `device.map_or()` would therefore consume the
925            // CString and drop it, making device_ptr a dangling pointer! To avoid that we downgrade
926            // device to an Option<&_> first.
927            let device_ptr = device.as_ref().map_or(ptr::null(), |s| s.as_ptr());
928
929            let iscapture_flag = if capture { 1 } else { 0 };
930            let device_id = sys::SDL_OpenAudioDevice(
931                device_ptr as *const c_char,
932                iscapture_flag,
933                &desired,
934                obtained.as_mut_ptr(),
935                0,
936            );
937            match device_id {
938                0 => Err(get_error()),
939                id => {
940                    let obtained = obtained.assume_init();
941                    let device_id = AudioDeviceID::PlaybackDevice(id);
942                    let spec = AudioSpec::convert_from_ll(obtained);
943
944                    *userdata = Some(get_callback(spec));
945
946                    Ok(AudioDevice {
947                        subsystem: a.clone(),
948                        device_id,
949                        userdata,
950                        spec,
951                    })
952                }
953            }
954        }
955    }
956
957    /// Opens a new audio device for playback (given the desired parameters and callback).
958    ///
959    /// If you want to modify the callback-owned data at a later point (for example to update
960    /// its data buffer) you're likely to be interested in the [lock method](#method.lock).
961    pub fn open_playback<'a, F, D>(
962        a: &AudioSubsystem,
963        device: D,
964        spec: &AudioSpecDesired,
965        get_callback: F,
966    ) -> Result<AudioDevice<CB>, String>
967    where
968        F: FnOnce(AudioSpec) -> CB,
969        D: Into<Option<&'a str>>,
970    {
971        AudioDevice::open(a, device, spec, get_callback, false)
972    }
973
974    /// Opens a new audio device for capture (given the desired parameters and callback).
975    /// Supported since SDL 2.0.5
976    ///
977    /// If you want to modify the callback-owned data at a later point (for example to update
978    /// its data buffer) you're likely to be interested in the [lock method](#method.lock).
979    pub fn open_capture<'a, F, D>(
980        a: &AudioSubsystem,
981        device: D,
982        spec: &AudioSpecDesired,
983        get_callback: F,
984    ) -> Result<AudioDevice<CB>, String>
985    where
986        F: FnOnce(AudioSpec) -> CB,
987        D: Into<Option<&'a str>>,
988    {
989        AudioDevice::open(a, device, spec, get_callback, true)
990    }
991
992    #[inline]
993    #[doc(alias = "SDL_GetAudioDeviceStatus")]
994    pub fn subsystem(&self) -> &AudioSubsystem {
995        &self.subsystem
996    }
997
998    #[inline]
999    pub fn spec(&self) -> &AudioSpec {
1000        &self.spec
1001    }
1002
1003    pub fn status(&self) -> AudioStatus {
1004        unsafe {
1005            let status = sys::SDL_GetAudioDeviceStatus(self.device_id.id());
1006            AudioStatus::try_from(status as u32).unwrap()
1007        }
1008    }
1009
1010    /// Pauses playback of the audio device.
1011    #[doc(alias = "SDL_PauseAudioDevice")]
1012    pub fn pause(&self) {
1013        unsafe { sys::SDL_PauseAudioDevice(self.device_id.id(), 1) }
1014    }
1015
1016    /// Starts playback of the audio device.
1017    #[doc(alias = "SDL_PauseAudioDevice")]
1018    pub fn resume(&self) {
1019        unsafe { sys::SDL_PauseAudioDevice(self.device_id.id(), 0) }
1020    }
1021
1022    /// Locks the audio device using `SDL_LockAudioDevice`.
1023    ///
1024    /// When the returned lock guard is dropped, `SDL_UnlockAudioDevice` is
1025    /// called.
1026    /// Use this method to read and mutate callback data.
1027    #[doc(alias = "SDL_LockAudioDevice")]
1028    pub fn lock(&mut self) -> AudioDeviceLockGuard<CB> {
1029        unsafe { sys::SDL_LockAudioDevice(self.device_id.id()) };
1030        AudioDeviceLockGuard {
1031            device: self,
1032            _nosend: PhantomData,
1033        }
1034    }
1035
1036    /// Closes the audio device and saves the callback data from being dropped.
1037    ///
1038    /// Note that simply dropping `AudioDevice` will close the audio device,
1039    /// but the callback data will be dropped.
1040    pub fn close_and_get_callback(self) -> CB {
1041        drop(self.device_id);
1042        self.userdata.expect("Missing callback")
1043    }
1044}
1045
1046/// Similar to `std::sync::MutexGuard`, but for use with `AudioDevice::lock()`.
1047pub struct AudioDeviceLockGuard<'a, CB>
1048where
1049    CB: AudioCallback,
1050    CB: 'a,
1051{
1052    device: &'a mut AudioDevice<CB>,
1053    _nosend: PhantomData<*mut ()>,
1054}
1055
1056impl<'a, CB: AudioCallback> Deref for AudioDeviceLockGuard<'a, CB> {
1057    type Target = CB;
1058    #[doc(alias = "SDL_UnlockAudioDevice")]
1059    fn deref(&self) -> &CB {
1060        (*self.device.userdata).as_ref().expect("Missing callback")
1061    }
1062}
1063
1064impl<'a, CB: AudioCallback> DerefMut for AudioDeviceLockGuard<'a, CB> {
1065    fn deref_mut(&mut self) -> &mut CB {
1066        (*self.device.userdata).as_mut().expect("Missing callback")
1067    }
1068}
1069
1070impl<'a, CB: AudioCallback> Drop for AudioDeviceLockGuard<'a, CB> {
1071    fn drop(&mut self) {
1072        unsafe { sys::SDL_UnlockAudioDevice(self.device.device_id.id()) }
1073    }
1074}
1075
1076#[derive(Copy, Clone)]
1077pub struct AudioCVT {
1078    raw: sys::SDL_AudioCVT,
1079}
1080
1081impl AudioCVT {
1082    #[doc(alias = "SDL_BuildAudioCVT")]
1083    pub fn new(
1084        src_format: AudioFormat,
1085        src_channels: u8,
1086        src_rate: i32,
1087        dst_format: AudioFormat,
1088        dst_channels: u8,
1089        dst_rate: i32,
1090    ) -> Result<AudioCVT, String> {
1091        use std::mem::MaybeUninit;
1092
1093        let mut raw: MaybeUninit<sys::SDL_AudioCVT> = mem::MaybeUninit::uninit();
1094
1095        unsafe {
1096            let ret = sys::SDL_BuildAudioCVT(
1097                raw.as_mut_ptr(),
1098                src_format.to_ll(),
1099                src_channels,
1100                src_rate as c_int,
1101                dst_format.to_ll(),
1102                dst_channels,
1103                dst_rate as c_int,
1104            );
1105            if ret == 1 || ret == 0 {
1106                let raw = raw.assume_init();
1107                Ok(AudioCVT { raw })
1108            } else {
1109                Err(get_error())
1110            }
1111        }
1112    }
1113
1114    #[doc(alias = "SDL_ConvertAudio")]
1115    pub fn convert(&self, mut src: Vec<u8>) -> Vec<u8> {
1116        //! Convert audio data to a desired audio format.
1117        //!
1118        //! Passes raw audio data from src to the SDL library for conversion, returning the result
1119        //! of the conversion.
1120        unsafe {
1121            if self.raw.needed != 0 {
1122                use std::convert::TryInto;
1123                use std::slice::from_raw_parts_mut;
1124
1125                let mut raw = self.raw;
1126
1127                // Calculate the size of the buffer we're handing to SDL.
1128                // This is more a suggestion, and not really a guarantee...
1129                let dst_size = self.capacity(src.len());
1130
1131                // Bounce into SDL2 heap allocation as SDL_ConvertAudio may rewrite the pointer.
1132                raw.len = src.len().try_into().expect("Buffer length overflow");
1133                raw.buf = sys::SDL_malloc(dst_size as _) as *mut _;
1134                if raw.buf.is_null() {
1135                    panic!("Failed SDL_malloc needed for SDL_ConvertAudio");
1136                }
1137                // raw.buf is dst_size long, but we want to copy into only the first src.len bytes.
1138                assert!(src.len() <= dst_size);
1139                from_raw_parts_mut(raw.buf, src.len()).copy_from_slice(src.as_ref());
1140
1141                let ret = sys::SDL_ConvertAudio(&mut raw);
1142                // There's no reason for SDL_ConvertAudio to fail.
1143                // The only time it can fail is if buf is NULL, which it never is.
1144                if ret != 0 {
1145                    panic!("{}", get_error())
1146                }
1147
1148                // Bounce back into src, trying to re-use the same buffer.
1149                let outlen: usize = raw.len_cvt.try_into().expect("Buffer size rollover");
1150                debug_assert!(outlen <= dst_size);
1151                src.resize(outlen, 0);
1152                src.copy_from_slice(from_raw_parts_mut(raw.buf, outlen));
1153                sys::SDL_free(raw.buf as *mut _);
1154
1155                src
1156            } else {
1157                // The buffer remains unmodified
1158                src
1159            }
1160        }
1161    }
1162
1163    /// Checks if any conversion is needed. i.e. if the buffer that goes
1164    /// into `convert()` is unchanged from the result.
1165    pub fn is_conversion_needed(&self) -> bool {
1166        self.raw.needed != 0
1167    }
1168
1169    /// Gets the buffer capacity that can contain both the original and
1170    /// converted data.
1171    pub fn capacity(&self, src_len: usize) -> usize {
1172        src_len
1173            .checked_mul(self.raw.len_mult as usize)
1174            .expect("Integer overflow")
1175    }
1176}
1177
1178#[cfg(test)]
1179mod test {
1180    use super::{AudioCVT, AudioFormat};
1181
1182    #[test]
1183    fn test_audio_cvt() {
1184        use std::iter::repeat;
1185
1186        // 0,1,2,3, ...
1187        let buffer: Vec<u8> = (0..255).collect();
1188
1189        // 0,0,1,1,2,2,3,3, ...
1190        let new_buffer_expected: Vec<u8> = (0..255).flat_map(|v| repeat(v).take(2)).collect();
1191
1192        let cvt = AudioCVT::new(AudioFormat::U8, 1, 44100, AudioFormat::U8, 2, 44100).unwrap();
1193        assert!(cvt.is_conversion_needed());
1194
1195        // since we're going from mono to stereo, our capacity must be at least twice the original (255) vec size
1196        assert!(
1197            cvt.capacity(255) >= 255 * 2,
1198            "capacity must be able to hold the converted audio sample"
1199        );
1200
1201        let new_buffer = cvt.convert(buffer);
1202        assert_eq!(
1203            new_buffer.len(),
1204            new_buffer_expected.len(),
1205            "capacity must be exactly equal to twice the original vec size"
1206        );
1207
1208        // // this has been commented, see https://discourse.libsdl.org/t/change-of-behavior-in-audiocvt-sdl-convertaudio-from-2-0-5-to-2-0-6/24682
1209        // // to maybe re-enable it someday
1210        // assert_eq!(new_buffer, new_buffer_expected);
1211    }
1212}