diff --git a/examples/audio-capture-and-replay.rs b/examples/audio-capture-and-replay.rs new file mode 100644 index 00000000000..67c66dc43bb --- /dev/null +++ b/examples/audio-capture-and-replay.rs @@ -0,0 +1,136 @@ +extern crate sdl2; + +use sdl2::audio::{AudioCallback, AudioSpecDesired}; +use sdl2::AudioSubsystem; +use std::time::Duration; +use std::sync::mpsc; +use std::i16; + +const RECORDING_LENGTH_SECONDS: usize = 3; + + +struct Recording { + record_buffer: Vec, + pos: usize, + done_sender: mpsc::Sender>, + done: bool, +} + +// Append the input of the callback to the record_buffer. +// When the record_buffer is full, send it to the main thread via done_sender. +impl AudioCallback for Recording { + type Channel = i16; + + fn callback(&mut self, input: &mut [i16]) { + if self.done { + return; + } + + for x in input { + self.record_buffer[self.pos] = *x; + self.pos += 1; + if self.pos >= self.record_buffer.len() { + self.done = true; + self.done_sender.send(self.record_buffer.clone()).unwrap(); + break; + } + } + } +} + +fn record(audio_subsystem: &AudioSubsystem, desired_spec: &AudioSpecDesired) -> Vec { + println!("Capturing {:} seconds... Please rock!", RECORDING_LENGTH_SECONDS); + + let (done_sender, done_receiver) = mpsc::channel(); + + let capture_device = audio_subsystem.open_capture(None, desired_spec, |spec| { + println!("Capture Spec = {:?}", spec); + Recording { + record_buffer: vec![0; spec.freq as usize * RECORDING_LENGTH_SECONDS * spec.channels as usize], + pos: 0, + done_sender, + done: false + } + }).unwrap(); + + println!("AudioDriver: {:?}", capture_device.subsystem().current_audio_driver()); + capture_device.resume(); + + // Wait until the recording is done. + let recorded_vec = done_receiver.recv().unwrap(); + + capture_device.pause(); + + // Device is automatically closed when dropped. + // Depending on your system it might be even important that the capture_device is dropped + // before the playback starts. + + recorded_vec +} + + +/// Returns a percent value +fn calculate_average_volume(recorded_vec: &[i16]) -> f32 { + let sum: i64 = recorded_vec.iter().map(|&x| (x as i64).abs()).sum(); + (sum as f32) / (recorded_vec.len() as f32) / (i16::MAX as f32) * 100.0 +} + +/// Returns a percent value +fn calculate_max_volume(recorded_vec: &[i16]) -> f32 { + let max: i64 = recorded_vec.iter().map(|&x| (x as i64).abs()).max().unwrap(); + (max as f32) / (i16::MAX as f32) * 100.0 +} + + +struct SoundPlayback { + data: Vec, + pos: usize, +} + +impl AudioCallback for SoundPlayback { + type Channel = i16; + + fn callback(&mut self, out: &mut [i16]) { + for dst in out.iter_mut() { + *dst = *self.data.get(self.pos).unwrap_or(&0); + self.pos += 1; + } + } +} + +fn replay_recorded_vec(audio_subsystem: &AudioSubsystem, desired_spec: &AudioSpecDesired, recorded_vec: Vec) { + println!("Playing..."); + + let playback_device = audio_subsystem.open_playback(None, desired_spec, |spec| { + println!("Playback Spec = {:?}", spec); + SoundPlayback { + data: recorded_vec, + pos: 0, + } + }).unwrap(); + + // Start playback + playback_device.resume(); + + std::thread::sleep(Duration::from_secs(RECORDING_LENGTH_SECONDS as u64)); + // Device is automatically closed when dropped +} + + +fn main() { + let sdl_context = sdl2::init().unwrap(); + let audio_subsystem = sdl_context.audio().unwrap(); + + let desired_spec = AudioSpecDesired { + freq: None, + channels: None, + samples: None + }; + + let recorded_vec = record(&audio_subsystem, &desired_spec); + + println!("Average Volume of your Recording = {:?}%", calculate_average_volume(&recorded_vec)); + println!("Max Volume of your Recording = {:?}%", calculate_max_volume(&recorded_vec)); + + replay_recorded_vec(&audio_subsystem, &desired_spec, recorded_vec); +} diff --git a/src/sdl2/audio.rs b/src/sdl2/audio.rs index c5fdc01e4c6..caba4f78f1a 100644 --- a/src/sdl2/audio.rs +++ b/src/sdl2/audio.rs @@ -74,6 +74,14 @@ impl AudioSubsystem { AudioDevice::open_playback(self, device, spec, get_callback) } + /// Opens a new audio device for capture (given the desired parameters and callback). + /// Supported since SDL 2.0.5 + pub fn open_capture<'a, CB, F, D>(&self, device: D, spec: &AudioSpecDesired, get_callback: F) -> Result, String> + where CB: AudioCallback, F: FnOnce(AudioSpec) -> CB, D: Into>, + { + AudioDevice::open_capture(self, device, spec, get_callback) + } + /// Opens a new audio device which uses queueing rather than older callback method. #[inline] pub fn open_queue<'a, Channel, D>(&self, device: D, spec: &AudioSpecDesired) -> Result, String> @@ -588,8 +596,8 @@ pub struct AudioDevice { } impl AudioDevice { - /// Opens a new audio device given the desired parameters and callback. - pub fn open_playback<'a, F, D>(a: &AudioSubsystem, device: D, spec: &AudioSpecDesired, get_callback: F) -> Result, String> + /// Opens a new audio device for playback or capture (given the desired parameters and callback). + fn open<'a, F, D>(a: &AudioSubsystem, device: D, spec: &AudioSpecDesired, get_callback: F, capture: bool) -> Result, String> where F: FnOnce(AudioSpec) -> CB, D: Into>, @@ -612,7 +620,7 @@ impl AudioDevice { }; let device_ptr = device.map_or(ptr::null(), |s| s.as_ptr()); - let iscapture_flag = 0; + let iscapture_flag = if capture { 1 } else { 0 }; let device_id = ll::SDL_OpenAudioDevice( device_ptr as *const c_char, iscapture_flag, &desired, &mut obtained, 0 @@ -640,6 +648,25 @@ impl AudioDevice { } } + /// Opens a new audio device for playback (given the desired parameters and callback). + pub fn open_playback<'a, F, D>(a: &AudioSubsystem, device: D, spec: &AudioSpecDesired, get_callback: F) -> Result, String> + where + F: FnOnce(AudioSpec) -> CB, + D: Into>, + { + AudioDevice::open(a, device, spec, get_callback, false) + } + + /// Opens a new audio device for capture (given the desired parameters and callback). + /// Supported since SDL 2.0.5 + pub fn open_capture<'a, F, D>(a: &AudioSubsystem, device: D, spec: &AudioSpecDesired, get_callback: F) -> Result, String> + where + F: FnOnce(AudioSpec) -> CB, + D: Into>, + { + AudioDevice::open(a, device, spec, get_callback, true) + } + #[inline] pub fn subsystem(&self) -> &AudioSubsystem { &self.subsystem }