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}