pix_engine/renderer/sdl/
audio.rs

1//! SDL Audio
2
3use super::Renderer;
4use crate::{
5    audio::{AudioDeviceDriver, AudioDriver},
6    error::{Error, Result},
7    prelude::*,
8};
9use anyhow::anyhow;
10use log::warn;
11use sdl2::audio::{
12    AudioCallback as SdlAudioCallback, AudioDevice as SdlAudioDevice,
13    AudioFormat as SdlAudioFormat, AudioSpec as SdlAudioSpec,
14    AudioSpecDesired as SdlAudioSpecDesired, AudioStatus as SdlAudioStatus,
15};
16use std::fmt;
17
18pub use sdl2::audio::AudioFormatNum;
19
20// ~1.5 minutes of audio @ 48,000 HZ.
21const WARN_QUEUE_SIZE: u32 = 1 << 22;
22// ~11.5  minutes of audio @ 48,000 HZ.
23const MAX_QUEUE_SIZE: u32 = 1 << 25;
24
25/// Audio callback or playback device that can be paused and resumed.
26pub struct AudioDevice<CB: AudioCallback>(SdlAudioDevice<UserCallback<CB>>);
27
28impl<CB: AudioCallback> AudioDeviceDriver for AudioDevice<CB> {
29    /// Return the status of this audio callback device.
30    #[inline]
31    fn status(&self) -> AudioStatus {
32        self.0.status().into()
33    }
34
35    /// Return the current driver of this audio callback device.
36    #[inline]
37    #[must_use]
38    fn driver(&self) -> &'static str {
39        self.0.subsystem().current_audio_driver()
40    }
41
42    /// Returns the [`AudioSpec`] for this audio callback device.
43    #[inline]
44    fn spec(&self) -> AudioSpec {
45        self.0.spec().into()
46    }
47
48    /// Resumes playback of this audio callback device.
49    #[inline]
50    fn resume(&self) {
51        self.0.resume();
52    }
53
54    /// Pause playback of this audio callback device.
55    #[inline]
56    fn pause(&self) {
57        self.0.pause();
58    }
59}
60
61impl<CB: AudioCallback> fmt::Debug for AudioDevice<CB> {
62    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
63        f.debug_struct("AudioDevice")
64            .field("status", &self.status())
65            .field("driver", &self.driver())
66            .field("spec", &self.spec())
67            .finish()
68    }
69}
70
71/// A wrapper around `AudioCallback`, which we can implement `SdlAudioCallback` for.
72pub(crate) struct UserCallback<CB>(CB);
73
74impl<CB: AudioCallback> UserCallback<CB> {
75    fn new(callback: CB) -> Self {
76        Self(callback)
77    }
78}
79
80impl<CB: AudioCallback> AudioDevice<CB> {
81    /// Creates a new `AudioDevice` from a renderer-specific device.
82    pub(crate) fn new(device: SdlAudioDevice<UserCallback<CB>>) -> Self {
83        Self(device)
84    }
85}
86
87impl<CB: AudioCallback> SdlAudioCallback for UserCallback<CB> {
88    type Channel = CB::Channel;
89    fn callback(&mut self, out: &mut [Self::Channel]) {
90        self.0.callback(out);
91    }
92}
93
94impl<CB: AudioCallback> From<SdlAudioDevice<UserCallback<CB>>> for AudioDevice<CB> {
95    /// Convert [`SdlAudioDevice<UseCallback<CB>>`] to [`AudioDevice`].
96    fn from(device: SdlAudioDevice<UserCallback<CB>>) -> Self {
97        Self::new(device)
98    }
99}
100
101impl AudioDriver for Renderer {
102    /// Add audio samples to the audio buffer queue.
103    #[inline]
104    fn enqueue_audio(&mut self, samples: &[f32]) -> Result<()> {
105        let size = self.audio_device.size();
106        if size <= MAX_QUEUE_SIZE {
107            if size >= WARN_QUEUE_SIZE {
108                warn!("Audio queue size is increasing: {}. Did you forget to call `PixState::resume_audio`? Audio Device Status: {:?}", size, self.audio_device.status());
109            }
110            self.audio_device
111                .queue_audio(samples)
112                .map_err(Error::Renderer)?;
113            Ok(())
114        } else {
115            Err(anyhow!("Reached max audio queue size: {}. Did you forget to call `PixState::resume_audio`? Audio Device Status: {:?}", MAX_QUEUE_SIZE, self.audio_device.status()))
116        }
117    }
118
119    /// Clear audio samples from the audio buffer queue.
120    #[inline]
121    fn clear_audio(&mut self) {
122        self.audio_device.clear();
123    }
124
125    /// Return the status of the current audio device.
126    #[inline]
127    fn audio_status(&self) -> AudioStatus {
128        self.audio_device.status().into()
129    }
130
131    /// Return the driver of current audio queue device.
132    fn audio_driver(&self) -> &'static str {
133        self.audio_device.subsystem().current_audio_driver()
134    }
135
136    /// Return the sample rate of the current audio device.
137    fn audio_sample_rate(&self) -> i32 {
138        self.audio_device.spec().freq
139    }
140
141    /// Returns the queued buffer size (in bytes) of the current audio queue device.
142    fn audio_queued_size(&self) -> u32 {
143        self.audio_device.size()
144    }
145
146    /// Returns the buffer size (in bytes) of the current audio queue device.
147    fn audio_size(&self) -> u32 {
148        self.audio_device.spec().size
149    }
150
151    /// Resume playback of the current audio device.
152    #[inline]
153    fn resume_audio(&mut self) {
154        self.audio_device.resume();
155    }
156
157    /// Pause playback of the current audio device.
158    #[inline]
159    fn pause_audio(&mut self) {
160        self.audio_device.pause();
161    }
162
163    /// Opens and returns an audio callback device for playback.
164    #[allow(single_use_lifetimes)]
165    #[inline]
166    fn open_playback<'a, CB, F, D>(
167        &self,
168        device: D,
169        desired_spec: &AudioSpecDesired,
170        get_callback: F,
171    ) -> Result<AudioDevice<CB>>
172    where
173        CB: AudioCallback,
174        F: FnOnce(AudioSpec) -> CB,
175        D: Into<Option<&'a str>>,
176    {
177        Ok(self
178            .context
179            .audio()
180            .map_err(Error::Renderer)?
181            .open_playback(device, &desired_spec.into(), |spec| {
182                UserCallback::new(get_callback(spec.into()))
183            })
184            .map_err(Error::Renderer)?
185            .into())
186    }
187
188    /// Opens and returns an audio capture device for recording.
189    #[allow(single_use_lifetimes)]
190    #[inline]
191    fn open_capture<'a, CB, F, D>(
192        &self,
193        device: D,
194        desired_spec: &AudioSpecDesired,
195        get_callback: F,
196    ) -> Result<AudioDevice<CB>>
197    where
198        CB: AudioCallback,
199        F: FnOnce(AudioSpec) -> CB,
200        D: Into<Option<&'a str>>,
201    {
202        Ok(self
203            .context
204            .audio()
205            .map_err(Error::Renderer)?
206            .open_capture(device, &desired_spec.into(), |spec| {
207                UserCallback::new(get_callback(spec.into()))
208            })
209            .map_err(Error::Renderer)?
210            .into())
211    }
212}
213
214#[doc(hidden)]
215impl From<SdlAudioSpecDesired> for AudioSpecDesired {
216    /// Convert [`SdlAudioSpecDesired`] to [`AudioSpecDesired`].
217    fn from(spec: SdlAudioSpecDesired) -> Self {
218        Self {
219            freq: spec.freq,
220            channels: spec.channels,
221            samples: spec.samples,
222        }
223    }
224}
225
226#[doc(hidden)]
227impl From<&AudioSpecDesired> for SdlAudioSpecDesired {
228    /// Convert [`&AudioSpecDesired`] to [`SdlAudioSpecDesired`].
229    fn from(spec: &AudioSpecDesired) -> Self {
230        Self {
231            freq: spec.freq,
232            channels: spec.channels,
233            samples: spec.samples,
234        }
235    }
236}
237
238#[doc(hidden)]
239impl From<SdlAudioFormat> for AudioFormat {
240    /// Convert [`SdlAudioFormat`] to [`AudioFormat`].
241    fn from(format: SdlAudioFormat) -> Self {
242        match format {
243            SdlAudioFormat::U8 => Self::U8,
244            SdlAudioFormat::S8 => Self::S8,
245            SdlAudioFormat::U16LSB => Self::U16LSB,
246            SdlAudioFormat::U16MSB => Self::U16MSB,
247            SdlAudioFormat::S16LSB => Self::S16LSB,
248            SdlAudioFormat::S16MSB => Self::S16MSB,
249            SdlAudioFormat::S32LSB => Self::S32LSB,
250            SdlAudioFormat::S32MSB => Self::S32MSB,
251            SdlAudioFormat::F32LSB => Self::F32LSB,
252            SdlAudioFormat::F32MSB => Self::F32MSB,
253        }
254    }
255}
256
257#[doc(hidden)]
258impl From<SdlAudioSpec> for AudioSpec {
259    /// Convert [`SdlAudioSpec`] to [`AudioSpec`].
260    fn from(spec: SdlAudioSpec) -> Self {
261        Self {
262            freq: spec.freq,
263            format: spec.format.into(),
264            channels: spec.channels,
265            samples: spec.samples,
266            size: spec.size,
267        }
268    }
269}
270
271#[doc(hidden)]
272impl From<&SdlAudioSpec> for AudioSpec {
273    /// Convert [`SdlAudioSpec`] to [`AudioSpec`].
274    fn from(spec: &SdlAudioSpec) -> Self {
275        Self {
276            freq: spec.freq,
277            format: spec.format.into(),
278            channels: spec.channels,
279            samples: spec.samples,
280            size: spec.size,
281        }
282    }
283}
284
285#[doc(hidden)]
286impl From<SdlAudioStatus> for AudioStatus {
287    /// Convert [`SdlAudioStatus`] to [`AudioStatus`].
288    fn from(status: SdlAudioStatus) -> Self {
289        match status {
290            SdlAudioStatus::Stopped => Self::Stopped,
291            SdlAudioStatus::Playing => Self::Playing,
292            SdlAudioStatus::Paused => Self::Paused,
293        }
294    }
295}