pix_engine/renderer/sdl/
audio.rs1use 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
20const WARN_QUEUE_SIZE: u32 = 1 << 22;
22const MAX_QUEUE_SIZE: u32 = 1 << 25;
24
25pub struct AudioDevice<CB: AudioCallback>(SdlAudioDevice<UserCallback<CB>>);
27
28impl<CB: AudioCallback> AudioDeviceDriver for AudioDevice<CB> {
29 #[inline]
31 fn status(&self) -> AudioStatus {
32 self.0.status().into()
33 }
34
35 #[inline]
37 #[must_use]
38 fn driver(&self) -> &'static str {
39 self.0.subsystem().current_audio_driver()
40 }
41
42 #[inline]
44 fn spec(&self) -> AudioSpec {
45 self.0.spec().into()
46 }
47
48 #[inline]
50 fn resume(&self) {
51 self.0.resume();
52 }
53
54 #[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
71pub(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 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 fn from(device: SdlAudioDevice<UserCallback<CB>>) -> Self {
97 Self::new(device)
98 }
99}
100
101impl AudioDriver for Renderer {
102 #[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 #[inline]
121 fn clear_audio(&mut self) {
122 self.audio_device.clear();
123 }
124
125 #[inline]
127 fn audio_status(&self) -> AudioStatus {
128 self.audio_device.status().into()
129 }
130
131 fn audio_driver(&self) -> &'static str {
133 self.audio_device.subsystem().current_audio_driver()
134 }
135
136 fn audio_sample_rate(&self) -> i32 {
138 self.audio_device.spec().freq
139 }
140
141 fn audio_queued_size(&self) -> u32 {
143 self.audio_device.size()
144 }
145
146 fn audio_size(&self) -> u32 {
148 self.audio_device.spec().size
149 }
150
151 #[inline]
153 fn resume_audio(&mut self) {
154 self.audio_device.resume();
155 }
156
157 #[inline]
159 fn pause_audio(&mut self) {
160 self.audio_device.pause();
161 }
162
163 #[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 #[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 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 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 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 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 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 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}