[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        Ok(match unsafe { mem::transmute(n) } {
341            SDL_AUDIO_STOPPED => Stopped,
342            SDL_AUDIO_PLAYING => Playing,
343            SDL_AUDIO_PAUSED => Paused,
344        })
345    }
346}
347
348#[doc(alias = "SDL_GetAudioDriver")]
349#[derive(Copy, Clone)]
350pub struct DriverIterator {
351    length: i32,
352    index: i32,
353}
354
355// panics if SDL_GetAudioDriver returns a null pointer,
356// which only happens if index is outside the range
357// 0..SDL_GetNumAudioDrivers()
358fn get_audio_driver(index: i32) -> &'static str {
359    unsafe {
360        let buf = sys::SDL_GetAudioDriver(index);
361        assert!(!buf.is_null());
362
363        CStr::from_ptr(buf as *const _).to_str().unwrap()
364    }
365}
366
367impl Iterator for DriverIterator {
368    type Item = &'static str;
369
370    #[inline]
371    fn next(&mut self) -> Option<&'static str> {
372        if self.index >= self.length {
373            None
374        } else {
375            let driver = get_audio_driver(self.index);
376            self.index += 1;
377
378            Some(driver)
379        }
380    }
381
382    #[inline]
383    fn size_hint(&self) -> (usize, Option<usize>) {
384        let remaining = (self.length - self.index) as usize;
385        (remaining, Some(remaining))
386    }
387
388    #[inline]
389    fn nth(&mut self, n: usize) -> Option<&'static str> {
390        use std::convert::TryInto;
391
392        self.index = match n.try_into().ok().and_then(|n| self.index.checked_add(n)) {
393            Some(index) if index < self.length => index,
394            _ => self.length,
395        };
396
397        self.next()
398    }
399}
400
401impl DoubleEndedIterator for DriverIterator {
402    #[inline]
403    fn next_back(&mut self) -> Option<&'static str> {
404        if self.index >= self.length {
405            None
406        } else {
407            self.length -= 1;
408
409            Some(get_audio_driver(self.length))
410        }
411    }
412
413    #[inline]
414    fn nth_back(&mut self, n: usize) -> Option<&'static str> {
415        use std::convert::TryInto;
416
417        self.length = match n.try_into().ok().and_then(|n| self.length.checked_sub(n)) {
418            Some(length) if length > self.index => length,
419            _ => self.index,
420        };
421
422        self.next_back()
423    }
424}
425
426impl ExactSizeIterator for DriverIterator {}
427
428impl std::iter::FusedIterator for DriverIterator {}
429
430/// Gets an iterator of all audio drivers compiled into the SDL2 library.
431#[doc(alias = "SDL_GetAudioDriver")]
432#[inline]
433pub fn drivers() -> DriverIterator {
434    // This function is thread-safe and doesn't require the audio subsystem to be initialized.
435    // The list of drivers are read-only and statically compiled into SDL2, varying by platform.
436
437    // SDL_GetNumAudioDrivers can never return a negative value.
438    DriverIterator {
439        length: unsafe { sys::SDL_GetNumAudioDrivers() },
440        index: 0,
441    }
442}
443
444pub struct AudioSpecWAV {
445    pub freq: i32,
446    pub format: AudioFormat,
447    pub channels: u8,
448    audio_buf: *mut u8,
449    audio_len: u32,
450}
451
452impl AudioSpecWAV {
453    /// Loads a WAVE from the file path.
454    pub fn load_wav<P: AsRef<Path>>(path: P) -> Result<AudioSpecWAV, String> {
455        let mut file = RWops::from_file(path, "rb")?;
456        AudioSpecWAV::load_wav_rw(&mut file)
457    }
458
459    /// Loads a WAVE from the data source.
460    #[doc(alias = "SDL_LoadWAV_RW")]
461    pub fn load_wav_rw(src: &mut RWops) -> Result<AudioSpecWAV, String> {
462        use std::mem::MaybeUninit;
463        use std::ptr::null_mut;
464
465        let mut desired = MaybeUninit::uninit();
466        let mut audio_buf: *mut u8 = null_mut();
467        let mut audio_len: u32 = 0;
468        unsafe {
469            let ret = sys::SDL_LoadWAV_RW(
470                src.raw(),
471                0,
472                desired.as_mut_ptr(),
473                &mut audio_buf,
474                &mut audio_len,
475            );
476            if ret.is_null() {
477                Err(get_error())
478            } else {
479                let desired = desired.assume_init();
480                Ok(AudioSpecWAV {
481                    freq: desired.freq,
482                    format: AudioFormat::from_ll(desired.format).unwrap(),
483                    channels: desired.channels,
484                    audio_buf,
485                    audio_len,
486                })
487            }
488        }
489    }
490
491    pub fn buffer(&self) -> &[u8] {
492        use std::slice::from_raw_parts;
493        unsafe {
494            let ptr = self.audio_buf as *const u8;
495            let len = self.audio_len as usize;
496            from_raw_parts(ptr, len)
497        }
498    }
499}
500
501impl Drop for AudioSpecWAV {
502    #[doc(alias = "SDL_FreeWAV")]
503    fn drop(&mut self) {
504        unsafe {
505            sys::SDL_FreeWAV(self.audio_buf);
506        }
507    }
508}
509
510pub trait AudioCallback: Send
511where
512    Self::Channel: AudioFormatNum + 'static,
513{
514    type Channel;
515
516    fn callback(&mut self, _: &mut [Self::Channel]);
517}
518
519/// A phantom type for retrieving the `SDL_AudioFormat` of a given generic type.
520/// All format types are returned as native-endian.
521pub trait AudioFormatNum {
522    fn audio_format() -> AudioFormat;
523
524    /// The appropriately typed silence value for the audio format used.
525    ///
526    /// # Examples
527    ///
528    /// ```
529    /// // The AudioFormatNum trait has to be imported for the Channel::SILENCE part to work.
530    /// use sdl2::audio::{AudioCallback, AudioFormatNum};
531    ///
532    /// struct Silence;
533    ///
534    /// impl AudioCallback for Silence {
535    ///     type Channel = u16;
536    ///
537    ///     fn callback(&mut self, out: &mut [u16]) {
538    ///         for dst in out.iter_mut() {
539    ///             *dst = Self::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_U16`
569impl AudioFormatNum for u16 {
570    fn audio_format() -> AudioFormat {
571        AudioFormat::u16_sys()
572    }
573    const SILENCE: u16 = 0x8000;
574}
575/// `AUDIO_S32`
576impl AudioFormatNum for i32 {
577    fn audio_format() -> AudioFormat {
578        AudioFormat::s32_sys()
579    }
580    const SILENCE: i32 = 0;
581}
582/// `AUDIO_F32`
583impl AudioFormatNum for f32 {
584    fn audio_format() -> AudioFormat {
585        AudioFormat::f32_sys()
586    }
587    const SILENCE: f32 = 0.0;
588}
589
590extern "C" fn audio_callback_marshall<CB: AudioCallback>(
591    userdata: *mut c_void,
592    stream: *mut u8,
593    len: c_int,
594) {
595    use std::mem::size_of;
596    use std::slice::from_raw_parts_mut;
597    unsafe {
598        let cb_userdata: &mut Option<CB> = &mut *(userdata as *mut _);
599        let buf: &mut [CB::Channel] = from_raw_parts_mut(
600            stream as *mut CB::Channel,
601            len as usize / size_of::<CB::Channel>(),
602        );
603
604        if let Some(cb) = cb_userdata {
605            cb.callback(buf);
606        }
607    }
608}
609
610#[derive(Clone)]
611pub struct AudioSpecDesired {
612    /// DSP frequency (samples per second). Set to None for the device's fallback frequency.
613    pub freq: Option<i32>,
614    /// Number of separate audio channels. Set to None for the device's fallback number of channels.
615    pub channels: Option<u8>,
616    /// Audio buffer size in samples (power of 2). Set to None for the device's fallback sample size.
617    pub samples: Option<u16>,
618}
619
620impl AudioSpecDesired {
621    fn convert_to_ll<CB, F, C, S>(
622        freq: F,
623        channels: C,
624        samples: S,
625        userdata: *mut Option<CB>,
626    ) -> sys::SDL_AudioSpec
627    where
628        CB: AudioCallback,
629        F: Into<Option<i32>>,
630        C: Into<Option<u8>>,
631        S: Into<Option<u16>>,
632    {
633        let freq = freq.into();
634        let channels = channels.into();
635        let samples = samples.into();
636
637        if let Some(freq) = freq {
638            assert!(freq > 0);
639        }
640        if let Some(channels) = channels {
641            assert!(channels > 0);
642        }
643        if let Some(samples) = samples {
644            assert!(samples > 0);
645        }
646
647        // A value of 0 means "fallback" or "default".
648
649        sys::SDL_AudioSpec {
650            freq: freq.unwrap_or(0),
651            format: <CB::Channel as AudioFormatNum>::audio_format().to_ll(),
652            channels: channels.unwrap_or(0),
653            silence: 0,
654            samples: samples.unwrap_or(0),
655            padding: 0,
656            size: 0,
657            callback: Some(
658                audio_callback_marshall::<CB>
659                    as extern "C" fn(arg1: *mut c_void, arg2: *mut u8, arg3: c_int),
660            ),
661            userdata: userdata as *mut _,
662        }
663    }
664
665    fn convert_queue_to_ll<Channel, F, C, S>(freq: F, channels: C, samples: S) -> sys::SDL_AudioSpec
666    where
667        Channel: AudioFormatNum,
668        F: Into<Option<i32>>,
669        C: Into<Option<u8>>,
670        S: Into<Option<u16>>,
671    {
672        let freq = freq.into();
673        let channels = channels.into();
674        let samples = samples.into();
675
676        if let Some(freq) = freq {
677            assert!(freq > 0);
678        }
679        if let Some(channels) = channels {
680            assert!(channels > 0);
681        }
682        if let Some(samples) = samples {
683            assert!(samples > 0);
684        }
685
686        // A value of 0 means "fallback" or "default".
687
688        sys::SDL_AudioSpec {
689            freq: freq.unwrap_or(0),
690            format: <Channel as AudioFormatNum>::audio_format().to_ll(),
691            channels: channels.unwrap_or(0),
692            silence: 0,
693            samples: samples.unwrap_or(0),
694            padding: 0,
695            size: 0,
696            callback: None,
697            userdata: ptr::null_mut(),
698        }
699    }
700}
701
702#[allow(missing_copy_implementations)]
703#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
704pub struct AudioSpec {
705    pub freq: i32,
706    pub format: AudioFormat,
707    pub channels: u8,
708    /// The silence value calculated by SDL2. Note that it's inconvenient to use if your channel
709    /// type is not u8 and [incorrect in case of u16](https://bugzilla.libsdl.org/show_bug.cgi?id=4805).
710    /// You're likely to find [the `AudioFormatNum.SILENCE` associated constant](
711    /// trait.AudioFormatNum.html#associatedconstant.SILENCE) more useful.
712    pub silence: u8,
713    pub samples: u16,
714    pub size: u32,
715}
716
717impl AudioSpec {
718    fn convert_from_ll(spec: sys::SDL_AudioSpec) -> AudioSpec {
719        AudioSpec {
720            freq: spec.freq,
721            format: AudioFormat::from_ll(spec.format).unwrap(),
722            channels: spec.channels,
723            silence: spec.silence,
724            samples: spec.samples,
725            size: spec.size,
726        }
727    }
728}
729
730enum AudioDeviceID {
731    PlaybackDevice(sys::SDL_AudioDeviceID),
732}
733
734impl AudioDeviceID {
735    fn id(&self) -> sys::SDL_AudioDeviceID {
736        match *self {
737            AudioDeviceID::PlaybackDevice(id) => id,
738        }
739    }
740}
741
742impl Drop for AudioDeviceID {
743    #[doc(alias = "SDL_CloseAudioDevice")]
744    fn drop(&mut self) {
745        //! Shut down audio processing and close the audio device.
746        unsafe { sys::SDL_CloseAudioDevice(self.id()) }
747    }
748}
749
750/// Wraps `SDL_AudioDeviceID` and owns the callback data used by the audio device.
751pub struct AudioQueue<Channel: AudioFormatNum> {
752    subsystem: AudioSubsystem,
753    device_id: AudioDeviceID,
754    phantom: PhantomData<Channel>,
755    spec: AudioSpec,
756}
757
758impl<'a, Channel: AudioFormatNum> AudioQueue<Channel> {
759    /// Opens a new audio device given the desired parameters and callback.
760    #[doc(alias = "SDL_OpenAudioDevice")]
761    pub fn open_queue<D: Into<Option<&'a str>>>(
762        a: &AudioSubsystem,
763        device: D,
764        spec: &AudioSpecDesired,
765    ) -> Result<AudioQueue<Channel>, String> {
766        use std::mem::MaybeUninit;
767
768        let desired = AudioSpecDesired::convert_queue_to_ll::<
769            Channel,
770            Option<i32>,
771            Option<u8>,
772            Option<u16>,
773        >(spec.freq, spec.channels, spec.samples);
774
775        let mut obtained = MaybeUninit::uninit();
776        unsafe {
777            let device = device.into().map(|device| CString::new(device).unwrap());
778            // Warning: map_or consumes its argument; `device.map_or()` would therefore consume the
779            // CString and drop it, making device_ptr a dangling pointer! To avoid that we downgrade
780            // device to an Option<&_> first.
781            let device_ptr = device.as_ref().map_or(ptr::null(), |s| s.as_ptr());
782
783            let iscapture_flag = 0;
784            let device_id = sys::SDL_OpenAudioDevice(
785                device_ptr as *const c_char,
786                iscapture_flag,
787                &desired,
788                obtained.as_mut_ptr(),
789                0,
790            );
791            match device_id {
792                0 => Err(get_error()),
793                id => {
794                    let obtained = obtained.assume_init();
795                    let device_id = AudioDeviceID::PlaybackDevice(id);
796                    let spec = AudioSpec::convert_from_ll(obtained);
797
798                    Ok(AudioQueue {
799                        subsystem: a.clone(),
800                        device_id,
801                        phantom: PhantomData,
802                        spec,
803                    })
804                }
805            }
806        }
807    }
808
809    #[inline]
810    #[doc(alias = "SDL_GetAudioDeviceStatus")]
811    pub fn subsystem(&self) -> &AudioSubsystem {
812        &self.subsystem
813    }
814
815    #[inline]
816    pub fn spec(&self) -> &AudioSpec {
817        &self.spec
818    }
819
820    pub fn status(&self) -> AudioStatus {
821        unsafe {
822            let status = sys::SDL_GetAudioDeviceStatus(self.device_id.id());
823            AudioStatus::try_from(status as u32).unwrap()
824        }
825    }
826
827    /// Pauses playback of the audio device.
828    #[doc(alias = "SDL_PauseAudioDevice")]
829    pub fn pause(&self) {
830        unsafe { sys::SDL_PauseAudioDevice(self.device_id.id(), 1) }
831    }
832
833    /// Starts playback of the audio device.
834    #[doc(alias = "SDL_PauseAudioDevice")]
835    pub fn resume(&self) {
836        unsafe { sys::SDL_PauseAudioDevice(self.device_id.id(), 0) }
837    }
838
839    /// Adds data to the audio queue.
840    #[doc(alias = "SDL_QueueAudio")]
841    #[deprecated(
842        since = "0.35.2",
843        note = "Users should instead use AudioQueue::queue_audio"
844    )]
845    pub fn queue(&self, data: &[Channel]) -> bool {
846        let result = unsafe {
847            sys::SDL_QueueAudio(
848                self.device_id.id(),
849                data.as_ptr() as *const c_void,
850                mem::size_of_val(data) as u32,
851            )
852        };
853        result == 0
854    }
855
856    /// Adds data to the audio queue.
857    #[doc(alias = "SDL_QueueAudio")]
858    pub fn queue_audio(&self, data: &[Channel]) -> Result<(), String> {
859        let result = unsafe {
860            sys::SDL_QueueAudio(
861                self.device_id.id(),
862                data.as_ptr() as *const c_void,
863                mem::size_of_val(data) as u32,
864            )
865        };
866        if result == 0 {
867            Ok(())
868        } else {
869            Err(get_error())
870        }
871    }
872
873    #[doc(alias = "SDL_GetQueuedAudioSize")]
874    pub fn size(&self) -> u32 {
875        unsafe { sys::SDL_GetQueuedAudioSize(self.device_id.id()) }
876    }
877
878    /// Clears all data from the current audio queue.
879    #[doc(alias = "SDL_ClearQueuedAudio")]
880    pub fn clear(&self) {
881        unsafe {
882            sys::SDL_ClearQueuedAudio(self.device_id.id());
883        }
884    }
885}
886
887/// Wraps `SDL_AudioDeviceID` and owns the callback data used by the audio device.
888pub struct AudioDevice<CB: AudioCallback> {
889    subsystem: AudioSubsystem,
890    device_id: AudioDeviceID,
891    spec: AudioSpec,
892    /// Store the callback to keep it alive for the entire duration of `AudioDevice`.
893    userdata: Box<Option<CB>>,
894}
895
896impl<CB: AudioCallback> AudioDevice<CB> {
897    /// Opens a new audio device for playback or capture (given the desired parameters and callback).
898    #[doc(alias = "SDL_OpenAudioDevice")]
899    fn open<'a, F, D>(
900        a: &AudioSubsystem,
901        device: D,
902        spec: &AudioSpecDesired,
903        get_callback: F,
904        capture: bool,
905    ) -> Result<AudioDevice<CB>, String>
906    where
907        F: FnOnce(AudioSpec) -> CB,
908        D: Into<Option<&'a str>>,
909    {
910        use std::mem::MaybeUninit;
911
912        let mut userdata: Box<Option<CB>> = Box::new(None);
913        let desired =
914            AudioSpecDesired::convert_to_ll(spec.freq, spec.channels, spec.samples, &mut *userdata);
915
916        let mut obtained = MaybeUninit::uninit();
917        unsafe {
918            let device = device.into().map(|device| CString::new(device).unwrap());
919            // Warning: map_or consumes its argument; `device.map_or()` would therefore consume the
920            // CString and drop it, making device_ptr a dangling pointer! To avoid that we downgrade
921            // device to an Option<&_> first.
922            let device_ptr = device.as_ref().map_or(ptr::null(), |s| s.as_ptr());
923
924            let iscapture_flag = if capture { 1 } else { 0 };
925            let device_id = sys::SDL_OpenAudioDevice(
926                device_ptr as *const c_char,
927                iscapture_flag,
928                &desired,
929                obtained.as_mut_ptr(),
930                0,
931            );
932            match device_id {
933                0 => Err(get_error()),
934                id => {
935                    let obtained = obtained.assume_init();
936                    let device_id = AudioDeviceID::PlaybackDevice(id);
937                    let spec = AudioSpec::convert_from_ll(obtained);
938
939                    *userdata = Some(get_callback(spec));
940
941                    Ok(AudioDevice {
942                        subsystem: a.clone(),
943                        device_id,
944                        userdata,
945                        spec,
946                    })
947                }
948            }
949        }
950    }
951
952    /// Opens a new audio device for playback (given the desired parameters and callback).
953    ///
954    /// If you want to modify the callback-owned data at a later point (for example to update
955    /// its data buffer) you're likely to be interested in the [lock method](#method.lock).
956    pub fn open_playback<'a, F, D>(
957        a: &AudioSubsystem,
958        device: D,
959        spec: &AudioSpecDesired,
960        get_callback: F,
961    ) -> Result<AudioDevice<CB>, String>
962    where
963        F: FnOnce(AudioSpec) -> CB,
964        D: Into<Option<&'a str>>,
965    {
966        AudioDevice::open(a, device, spec, get_callback, false)
967    }
968
969    /// Opens a new audio device for capture (given the desired parameters and callback).
970    /// Supported since SDL 2.0.5
971    ///
972    /// If you want to modify the callback-owned data at a later point (for example to update
973    /// its data buffer) you're likely to be interested in the [lock method](#method.lock).
974    pub fn open_capture<'a, F, D>(
975        a: &AudioSubsystem,
976        device: D,
977        spec: &AudioSpecDesired,
978        get_callback: F,
979    ) -> Result<AudioDevice<CB>, String>
980    where
981        F: FnOnce(AudioSpec) -> CB,
982        D: Into<Option<&'a str>>,
983    {
984        AudioDevice::open(a, device, spec, get_callback, true)
985    }
986
987    #[inline]
988    #[doc(alias = "SDL_GetAudioDeviceStatus")]
989    pub fn subsystem(&self) -> &AudioSubsystem {
990        &self.subsystem
991    }
992
993    #[inline]
994    pub fn spec(&self) -> &AudioSpec {
995        &self.spec
996    }
997
998    pub fn status(&self) -> AudioStatus {
999        unsafe {
1000            let status = sys::SDL_GetAudioDeviceStatus(self.device_id.id());
1001            AudioStatus::try_from(status as u32).unwrap()
1002        }
1003    }
1004
1005    /// Pauses playback of the audio device.
1006    #[doc(alias = "SDL_PauseAudioDevice")]
1007    pub fn pause(&self) {
1008        unsafe { sys::SDL_PauseAudioDevice(self.device_id.id(), 1) }
1009    }
1010
1011    /// Starts playback of the audio device.
1012    #[doc(alias = "SDL_PauseAudioDevice")]
1013    pub fn resume(&self) {
1014        unsafe { sys::SDL_PauseAudioDevice(self.device_id.id(), 0) }
1015    }
1016
1017    /// Locks the audio device using `SDL_LockAudioDevice`.
1018    ///
1019    /// When the returned lock guard is dropped, `SDL_UnlockAudioDevice` is
1020    /// called.
1021    /// Use this method to read and mutate callback data.
1022    #[doc(alias = "SDL_LockAudioDevice")]
1023    pub fn lock(&mut self) -> AudioDeviceLockGuard<CB> {
1024        unsafe { sys::SDL_LockAudioDevice(self.device_id.id()) };
1025        AudioDeviceLockGuard {
1026            device: self,
1027            _nosend: PhantomData,
1028        }
1029    }
1030
1031    /// Closes the audio device and saves the callback data from being dropped.
1032    ///
1033    /// Note that simply dropping `AudioDevice` will close the audio device,
1034    /// but the callback data will be dropped.
1035    pub fn close_and_get_callback(self) -> CB {
1036        drop(self.device_id);
1037        self.userdata.expect("Missing callback")
1038    }
1039}
1040
1041/// Similar to `std::sync::MutexGuard`, but for use with `AudioDevice::lock()`.
1042pub struct AudioDeviceLockGuard<'a, CB>
1043where
1044    CB: AudioCallback,
1045    CB: 'a,
1046{
1047    device: &'a mut AudioDevice<CB>,
1048    _nosend: PhantomData<*mut ()>,
1049}
1050
1051impl<'a, CB: AudioCallback> Deref for AudioDeviceLockGuard<'a, CB> {
1052    type Target = CB;
1053    #[doc(alias = "SDL_UnlockAudioDevice")]
1054    fn deref(&self) -> &CB {
1055        (*self.device.userdata).as_ref().expect("Missing callback")
1056    }
1057}
1058
1059impl<'a, CB: AudioCallback> DerefMut for AudioDeviceLockGuard<'a, CB> {
1060    fn deref_mut(&mut self) -> &mut CB {
1061        (*self.device.userdata).as_mut().expect("Missing callback")
1062    }
1063}
1064
1065impl<'a, CB: AudioCallback> Drop for AudioDeviceLockGuard<'a, CB> {
1066    fn drop(&mut self) {
1067        unsafe { sys::SDL_UnlockAudioDevice(self.device.device_id.id()) }
1068    }
1069}
1070
1071#[derive(Copy, Clone)]
1072pub struct AudioCVT {
1073    raw: sys::SDL_AudioCVT,
1074}
1075
1076impl AudioCVT {
1077    #[doc(alias = "SDL_BuildAudioCVT")]
1078    pub fn new(
1079        src_format: AudioFormat,
1080        src_channels: u8,
1081        src_rate: i32,
1082        dst_format: AudioFormat,
1083        dst_channels: u8,
1084        dst_rate: i32,
1085    ) -> Result<AudioCVT, String> {
1086        use std::mem::MaybeUninit;
1087
1088        let mut raw: MaybeUninit<sys::SDL_AudioCVT> = mem::MaybeUninit::uninit();
1089
1090        unsafe {
1091            let ret = sys::SDL_BuildAudioCVT(
1092                raw.as_mut_ptr(),
1093                src_format.to_ll(),
1094                src_channels,
1095                src_rate as c_int,
1096                dst_format.to_ll(),
1097                dst_channels,
1098                dst_rate as c_int,
1099            );
1100            if ret == 1 || ret == 0 {
1101                let raw = raw.assume_init();
1102                Ok(AudioCVT { raw })
1103            } else {
1104                Err(get_error())
1105            }
1106        }
1107    }
1108
1109    #[doc(alias = "SDL_ConvertAudio")]
1110    pub fn convert(&self, mut src: Vec<u8>) -> Vec<u8> {
1111        //! Convert audio data to a desired audio format.
1112        //!
1113        //! Passes raw audio data from src to the SDL library for conversion, returning the result
1114        //! of the conversion.
1115        unsafe {
1116            if self.raw.needed != 0 {
1117                use std::convert::TryInto;
1118                use std::slice::from_raw_parts_mut;
1119
1120                let mut raw = self.raw;
1121
1122                // Calculate the size of the buffer we're handing to SDL.
1123                // This is more a suggestion, and not really a guarantee...
1124                let dst_size = self.capacity(src.len());
1125
1126                // Bounce into SDL2 heap allocation as SDL_ConvertAudio may rewrite the pointer.
1127                raw.len = src.len().try_into().expect("Buffer length overflow");
1128                raw.buf = sys::SDL_malloc(dst_size as _) as *mut _;
1129                if raw.buf.is_null() {
1130                    panic!("Failed SDL_malloc needed for SDL_ConvertAudio");
1131                }
1132                // raw.buf is dst_size long, but we want to copy into only the first src.len bytes.
1133                assert!(src.len() <= dst_size);
1134                from_raw_parts_mut(raw.buf, src.len()).copy_from_slice(src.as_ref());
1135
1136                let ret = sys::SDL_ConvertAudio(&mut raw);
1137                // There's no reason for SDL_ConvertAudio to fail.
1138                // The only time it can fail is if buf is NULL, which it never is.
1139                if ret != 0 {
1140                    panic!("{}", get_error())
1141                }
1142
1143                // Bounce back into src, trying to re-use the same buffer.
1144                let outlen: usize = raw.len_cvt.try_into().expect("Buffer size rollover");
1145                debug_assert!(outlen <= dst_size);
1146                src.resize(outlen, 0);
1147                src.copy_from_slice(from_raw_parts_mut(raw.buf, outlen));
1148                sys::SDL_free(raw.buf as *mut _);
1149
1150                src
1151            } else {
1152                // The buffer remains unmodified
1153                src
1154            }
1155        }
1156    }
1157
1158    /// Checks if any conversion is needed. i.e. if the buffer that goes
1159    /// into `convert()` is unchanged from the result.
1160    pub fn is_conversion_needed(&self) -> bool {
1161        self.raw.needed != 0
1162    }
1163
1164    /// Gets the buffer capacity that can contain both the original and
1165    /// converted data.
1166    pub fn capacity(&self, src_len: usize) -> usize {
1167        src_len
1168            .checked_mul(self.raw.len_mult as usize)
1169            .expect("Integer overflow")
1170    }
1171}
1172
1173#[cfg(test)]
1174mod test {
1175    use super::{AudioCVT, AudioFormat};
1176
1177    #[test]
1178    fn test_audio_cvt() {
1179        use std::iter::repeat;
1180
1181        // 0,1,2,3, ...
1182        let buffer: Vec<u8> = (0..255).collect();
1183
1184        // 0,0,1,1,2,2,3,3, ...
1185        let new_buffer_expected: Vec<u8> = (0..255).flat_map(|v| repeat(v).take(2)).collect();
1186
1187        let cvt = AudioCVT::new(AudioFormat::U8, 1, 44100, AudioFormat::U8, 2, 44100).unwrap();
1188        assert!(cvt.is_conversion_needed());
1189
1190        // since we're going from mono to stereo, our capacity must be at least twice the original (255) vec size
1191        assert!(
1192            cvt.capacity(255) >= 255 * 2,
1193            "capacity must be able to hold the converted audio sample"
1194        );
1195
1196        let new_buffer = cvt.convert(buffer);
1197        assert_eq!(
1198            new_buffer.len(),
1199            new_buffer_expected.len(),
1200            "capacity must be exactly equal to twice the original vec size"
1201        );
1202
1203        // // 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
1204        // // to maybe re-enable it someday
1205        // assert_eq!(new_buffer, new_buffer_expected);
1206    }
1207}