timbre/drivers/sdl2_output.rs
1use crate::{
2 core::{AudioBuffer, SharedAudioSource},
3 AudioFormat, Error, StreamState,
4};
5
6use sdl2::audio::{AudioCallback, AudioFormatNum, AudioSpecDesired};
7use tracing::{info, instrument, warn};
8
9struct Callback {
10 pub format: AudioFormat,
11 pub source: Option<SharedAudioSource>,
12}
13
14impl AudioCallback for Callback {
15 type Channel = f32;
16 #[instrument(name = "Sdl2Output::callback", skip(self, samples))]
17 fn callback(&mut self, samples: &mut [Self::Channel]) {
18 if let Some(source) = &self.source {
19 let mut source = source.lock().unwrap();
20
21 let mut buffer = AudioBuffer::new(self.format, samples);
22 let result = source.read(&mut buffer);
23
24 if result.state == StreamState::Underrun {
25 warn!("Underrun detected.");
26 }
27
28 samples
29 .iter_mut()
30 .skip(result.read)
31 .for_each(|s| *s = AudioFormatNum::SILENCE);
32 } else {
33 for sample in samples.iter_mut() {
34 *sample = AudioFormatNum::SILENCE;
35 }
36 }
37 }
38}
39
40/// A sink that outputs audio data to speakers, etc.
41///
42/// # Examples
43/// ```
44/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
45/// # use timbre::drivers::{Sdl2Input, Sdl2Output};
46/// # std::env::set_var("SDL_AUDIODRIVER", "dummy");
47/// let sdl = sdl2::init()?;
48/// let audio = sdl.audio()?;
49///
50/// let mut microphone = Sdl2Input::new(&audio)?;
51/// let mut speaker = Sdl2Output::new(&audio)?;
52/// microphone.resume();
53/// speaker.set_source(microphone.source());
54/// speaker.resume();
55/// # Ok(())
56/// # }
57/// ```
58pub struct Sdl2Output {
59 device: sdl2::audio::AudioDevice<Callback>,
60}
61
62impl Sdl2Output {
63 /// Construct a new `Sdl2Output` with the default format.
64 ///
65 /// The default format is stereo at 44.1 kHz.
66 ///
67 /// # Arguments
68 ///
69 /// * `subsystem` -- An SDL [`AudioSubystem`](sdl2::AudioSubsystem) used to create an output device.
70 ///
71 /// # Errors
72 ///
73 /// If SDL fails to open the device.
74 ///
75 /// # Examples
76 /// ```
77 /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
78 /// # use timbre::drivers::Sdl2Output;
79 /// # std::env::set_var("SDL_AUDIODRIVER", "dummy");
80 /// let sdl = sdl2::init()?;
81 /// let audio = sdl.audio()?;
82 ///
83 /// let speaker = Sdl2Output::new(&audio)?;
84 /// # Ok(())
85 /// # }
86 /// ```
87 pub fn new(subsystem: &sdl2::AudioSubsystem) -> Result<Self, Error> {
88 Sdl2Output::with_format(
89 subsystem,
90 AudioFormat {
91 channels: 2,
92 sample_rate: 44100,
93 },
94 )
95 }
96
97 /// Construct a new `Sdl2Output` with the specified format.
98 ///
99 /// This constructor will request the specified format, but the driver may choose something else.
100 ///
101 /// # Arguments
102 ///
103 /// * `subsystem` -- An SDL [`AudioSubystem`](sdl2::AudioSubsystem) used to create an output device.
104 /// * `format` -- The format to request for this output device.
105 ///
106 /// # Errors
107 ///
108 /// If SDL fails to open the device.
109 ///
110 /// # Examples
111 /// ```
112 /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
113 /// # use timbre::drivers::Sdl2Output;
114 /// # std::env::set_var("SDL_AUDIODRIVER", "dummy");
115 /// let sdl = sdl2::init()?;
116 /// let audio = sdl.audio()?;
117 ///
118 /// let speaker = Sdl2Output::new(&audio)?;
119 /// # Ok(())
120 /// # }
121 /// ```
122 pub fn with_format(
123 subsystem: &sdl2::AudioSubsystem,
124 format: AudioFormat,
125 ) -> Result<Self, Error> {
126 let desired_spec = AudioSpecDesired {
127 freq: Some(format.sample_rate as i32),
128 channels: Some(format.channels),
129 samples: Some(1024),
130 };
131
132 let device = subsystem
133 .open_playback(None, &desired_spec, |spec| {
134 info!("Output Spec: {:?}", spec);
135
136 Callback {
137 format: spec.into(),
138 source: None,
139 }
140 })
141 .map_err(Error::from_sdl)?;
142
143 Ok(Sdl2Output { device })
144 }
145
146 /// Set the source of audio to output.
147 pub fn set_source(&mut self, source: SharedAudioSource) {
148 self.device.lock().source = Some(source);
149 }
150
151 /// Get the driver's chosen audio format.
152 pub fn format(&mut self) -> AudioFormat {
153 self.device.lock().format
154 }
155
156 /// Pause playback for this device.
157 ///
158 /// While paused, this device will not consume data from its source.
159 pub fn pause(&mut self) {
160 self.device.pause();
161 }
162
163 /// Start/resume playback for this device.
164 ///
165 /// The device starts in the paused state, and must be resumed for
166 /// playback from an audio source to begin.
167 pub fn resume(&mut self) {
168 self.device.resume();
169 }
170}