1use 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 #[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 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 #[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 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 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 U8 = sys::AUDIO_U8 as i32,
233 S8 = sys::AUDIO_S8 as i32,
235 U16LSB = sys::AUDIO_U16LSB as i32,
237 U16MSB = sys::AUDIO_U16MSB as i32,
239 S16LSB = sys::AUDIO_S16LSB as i32,
241 S16MSB = sys::AUDIO_S16MSB as i32,
243 S32LSB = sys::AUDIO_S32LSB as i32,
245 S32MSB = sys::AUDIO_S32MSB as i32,
247 F32LSB = sys::AUDIO_F32LSB as i32,
249 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 #[inline]
281 pub const fn u16_sys() -> AudioFormat {
282 AudioFormat::U16LSB
283 }
284 #[inline]
286 pub const fn s16_sys() -> AudioFormat {
287 AudioFormat::S16LSB
288 }
289 #[inline]
291 pub const fn s32_sys() -> AudioFormat {
292 AudioFormat::S32LSB
293 }
294 #[inline]
296 pub const fn f32_sys() -> AudioFormat {
297 AudioFormat::F32LSB
298 }
299}
300
301#[cfg(target_endian = "big")]
302impl AudioFormat {
303 #[inline]
305 pub const fn u16_sys() -> AudioFormat {
306 AudioFormat::U16MSB
307 }
308 #[inline]
310 pub const fn s16_sys() -> AudioFormat {
311 AudioFormat::S16MSB
312 }
313 #[inline]
315 pub const fn s32_sys() -> AudioFormat {
316 AudioFormat::S32MSB
317 }
318 #[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
360fn 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#[doc(alias = "SDL_GetAudioDriver")]
437#[inline]
438pub fn drivers() -> DriverIterator {
439 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 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 #[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
524pub trait AudioFormatNum {
527 fn audio_format() -> AudioFormat;
528
529 const SILENCE: Self;
550}
551
552impl AudioFormatNum for i8 {
554 fn audio_format() -> AudioFormat {
555 AudioFormat::S8
556 }
557 const SILENCE: i8 = 0;
558}
559impl AudioFormatNum for u8 {
561 fn audio_format() -> AudioFormat {
562 AudioFormat::U8
563 }
564 const SILENCE: u8 = 0x80;
565}
566impl AudioFormatNum for i16 {
568 fn audio_format() -> AudioFormat {
569 AudioFormat::s16_sys()
570 }
571 const SILENCE: i16 = 0;
572}
573impl AudioFormatNum for u16 {
575 fn audio_format() -> AudioFormat {
576 AudioFormat::u16_sys()
577 }
578 const SILENCE: u16 = 0x8000;
579}
580impl AudioFormatNum for i32 {
582 fn audio_format() -> AudioFormat {
583 AudioFormat::s32_sys()
584 }
585 const SILENCE: i32 = 0;
586}
587impl 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 pub freq: Option<i32>,
619 pub channels: Option<u8>,
621 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 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 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 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 unsafe { sys::SDL_CloseAudioDevice(self.id()) }
752 }
753}
754
755pub 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 #[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 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 #[doc(alias = "SDL_PauseAudioDevice")]
834 pub fn pause(&self) {
835 unsafe { sys::SDL_PauseAudioDevice(self.device_id.id(), 1) }
836 }
837
838 #[doc(alias = "SDL_PauseAudioDevice")]
840 pub fn resume(&self) {
841 unsafe { sys::SDL_PauseAudioDevice(self.device_id.id(), 0) }
842 }
843
844 #[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 #[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 #[doc(alias = "SDL_ClearQueuedAudio")]
885 pub fn clear(&self) {
886 unsafe {
887 sys::SDL_ClearQueuedAudio(self.device_id.id());
888 }
889 }
890}
891
892pub struct AudioDevice<CB: AudioCallback> {
894 subsystem: AudioSubsystem,
895 device_id: AudioDeviceID,
896 spec: AudioSpec,
897 userdata: Box<Option<CB>>,
899}
900
901impl<CB: AudioCallback> AudioDevice<CB> {
902 #[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 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 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 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 #[doc(alias = "SDL_PauseAudioDevice")]
1012 pub fn pause(&self) {
1013 unsafe { sys::SDL_PauseAudioDevice(self.device_id.id(), 1) }
1014 }
1015
1016 #[doc(alias = "SDL_PauseAudioDevice")]
1018 pub fn resume(&self) {
1019 unsafe { sys::SDL_PauseAudioDevice(self.device_id.id(), 0) }
1020 }
1021
1022 #[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 pub fn close_and_get_callback(self) -> CB {
1041 drop(self.device_id);
1042 self.userdata.expect("Missing callback")
1043 }
1044}
1045
1046pub 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 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 let dst_size = self.capacity(src.len());
1130
1131 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 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 if ret != 0 {
1145 panic!("{}", get_error())
1146 }
1147
1148 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 src
1159 }
1160 }
1161 }
1162
1163 pub fn is_conversion_needed(&self) -> bool {
1166 self.raw.needed != 0
1167 }
1168
1169 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 let buffer: Vec<u8> = (0..255).collect();
1188
1189 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 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 }
1212}