Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support Audio Capture in AudioCallbacks #673

Merged
merged 3 commits into from
Jul 9, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
136 changes: 136 additions & 0 deletions examples/audio-capture-and-replay.rs
Original file line number Diff line number Diff line change
@@ -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<i16>,
pos: usize,
done_sender: mpsc::Sender<Vec<i16>>,
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<i16> {
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<i16>,
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<i16>) {
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);
}
33 changes: 30 additions & 3 deletions src/sdl2/audio.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<AudioDevice <CB>, String>
where CB: AudioCallback, F: FnOnce(AudioSpec) -> CB, D: Into<Option<&'a str>>,
{
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<AudioQueue<Channel>, String>
Expand Down Expand Up @@ -588,8 +596,8 @@ pub struct AudioDevice<CB: AudioCallback> {
}

impl<CB: AudioCallback> AudioDevice<CB> {
/// 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<AudioDevice <CB>, 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<AudioDevice <CB>, String>
where
F: FnOnce(AudioSpec) -> CB,
D: Into<Option<&'a str>>,
Expand All @@ -612,7 +620,7 @@ impl<CB: AudioCallback> AudioDevice<CB> {
};
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
Expand Down Expand Up @@ -640,6 +648,25 @@ impl<CB: AudioCallback> AudioDevice<CB> {
}
}

/// 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<AudioDevice <CB>, String>
where
F: FnOnce(AudioSpec) -> CB,
D: Into<Option<&'a str>>,
{
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<AudioDevice <CB>, String>
where
F: FnOnce(AudioSpec) -> CB,
D: Into<Option<&'a str>>,
{
AudioDevice::open(a, device, spec, get_callback, true)
}

#[inline]
pub fn subsystem(&self) -> &AudioSubsystem { &self.subsystem }

Expand Down