diff --git a/.travis.yml b/.travis.yml index 4fc4950e..fb608709 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,9 +5,9 @@ rust: - nightly - stable install: -- sudo add-apt-repository ppa:team-xbmc/ppa -y -- sudo apt-get update -q -- sudo apt-get install libsdl2-dev +- wget https://www.libsdl.org/release/SDL2-2.0.4.tar.gz -O sdl2.tar.gz +- tar xzf sdl2.tar.gz +- pushd SDL2-2.0.4 && ./configure && make && sudo make install && popd before_script: - | pip install 'travis-cargo<0.2' --user && diff --git a/examples/audio-queue-squarewave.rs b/examples/audio-queue-squarewave.rs new file mode 100644 index 00000000..904cd226 --- /dev/null +++ b/examples/audio-queue-squarewave.rs @@ -0,0 +1,51 @@ +extern crate sdl2; + +use sdl2::audio::{AudioCallback, AudioSpecDesired}; + +use std::time::Duration; + +fn gen_wave(bytes_to_write: i32) -> Vec { + // Generate a square wave + let tone_volume = 1000i16; + let period = 48000 / 256; + let sample_count = bytes_to_write; + let mut result = Vec::new(); + + for x in 0..sample_count { + result.push( + if (x / period) % 2 == 0 { + tone_volume + } + else { + -tone_volume + } + ); + } + result +} + +fn main() { + let sdl_context = sdl2::init().unwrap(); + let audio_subsystem = sdl_context.audio().unwrap(); + + let desired_spec = AudioSpecDesired { + freq: Some(48000), + channels: Some(2), + // mono - + samples: Some(4) + // default sample size + }; + + let device = audio_subsystem.open_queue::(None, &desired_spec).unwrap(); + + let target_bytes = 48000 * 4; + let wave = gen_wave(target_bytes); + device.queue(&wave); + // Start playback + device.resume(); + + // Play for 2 seconds + std::thread::sleep(Duration::from_millis(2000)); + + // Device is automatically closed when dropped +} diff --git a/sdl2-sys/src/audio.rs b/sdl2-sys/src/audio.rs index 74068ad3..b8545fe5 100644 --- a/sdl2-sys/src/audio.rs +++ b/sdl2-sys/src/audio.rs @@ -96,6 +96,9 @@ extern "C" { pub fn SDL_MixAudioFormat(dst: *mut uint8_t, src: *const uint8_t, format: SDL_AudioFormat, len: uint32_t, volume: c_int); + pub fn SDL_QueueAudio(dev: SDL_AudioDeviceID, data: *const c_void, len: uint32_t) -> c_int; + pub fn SDL_GetQueuedAudioSize(dev: SDL_AudioDeviceID) -> uint32_t; + pub fn SDL_ClearQueuedAudio(dev: SDL_AudioDeviceID); pub fn SDL_LockAudio(); pub fn SDL_LockAudioDevice(dev: SDL_AudioDeviceID); pub fn SDL_UnlockAudio(); diff --git a/src/sdl2/audio.rs b/src/sdl2/audio.rs index 1d91e5c1..fdfb9ef1 100644 --- a/src/sdl2/audio.rs +++ b/src/sdl2/audio.rs @@ -74,6 +74,13 @@ impl AudioSubsystem { AudioDevice::open_playback(self, device, spec, get_callback) } + /// Opens a new audio device which uses queueing rather than older callback method. + #[inline] + pub fn open_queue(&self, device: Option<&str>, spec: &AudioSpecDesired) -> Result, String> where Channel: AudioFormatNum + { + AudioQueue::open_queue(self, device, spec) + } + pub fn current_audio_driver(&self) -> &'static str { unsafe { let buf = ll::SDL_GetCurrentAudioDriver(); @@ -392,14 +399,34 @@ impl AudioSpecDesired { padding: 0, size: 0, callback: Some(audio_callback_marshall:: - as extern "C" fn - (arg1: *mut c_void, - arg2: *mut uint8_t, - arg3: c_int)), + as extern "C" fn + (arg1: *mut c_void, + arg2: *mut uint8_t, + arg3: c_int)), userdata: transmute(userdata) } } } + + fn convert_queue_to_ll(freq: Option, channels: Option, samples: Option) -> ll::SDL_AudioSpec { + if let Some(freq) = freq { assert!(freq > 0); } + if let Some(channels) = channels { assert!(channels > 0); } + if let Some(samples) = samples { assert!(samples > 0); } + + // A value of 0 means "fallback" or "default". + + ll::SDL_AudioSpec { + freq: freq.unwrap_or(0), + format: ::audio_format().to_ll(), + channels: channels.unwrap_or(0), + silence: 0, + samples: samples.unwrap_or(0), + padding: 0, + size: 0, + callback: None, + userdata: 0 as *mut c_void + } + } } #[allow(missing_copy_implementations)] @@ -445,6 +472,84 @@ impl Drop for AudioDeviceID { } } +/// Wraps SDL_AudioDeviceID and owns the callback data used by the audio device. +pub struct AudioQueue { + subsystem: AudioSubsystem, + device_id: AudioDeviceID, + phantom: PhantomData +} + +impl AudioQueue { + /// Opens a new audio device given the desired parameters and callback. + pub fn open_queue(a: &AudioSubsystem, device: Option<&str>, spec: &AudioSpecDesired) -> Result, String> { + let desired = AudioSpecDesired::convert_queue_to_ll::(spec.freq, spec.channels, spec.samples); + + let mut obtained = unsafe { mem::uninitialized::() }; + unsafe { + let device = match device { + Some(device) => Some(CString::new(device).unwrap()), + None => None + }; + let device_ptr = device.map_or(ptr::null(), |s| s.as_ptr()); + + let iscapture_flag = 0; + let device_id = ll::SDL_OpenAudioDevice( + device_ptr as *const c_char, iscapture_flag, &desired, + &mut obtained, 0 + ); + match device_id { + 0 => { + Err(get_error()) + }, + id => { + let device_id = AudioDeviceID::PlaybackDevice(id); + + Ok(AudioQueue { + subsystem: a.clone(), + device_id: device_id, + phantom: PhantomData::default() + }) + } + } + } + } + + #[inline] + pub fn subsystem(&self) -> &AudioSubsystem { &self.subsystem } + + pub fn status(&self) -> AudioStatus { + unsafe { + let status = ll::SDL_GetAudioDeviceStatus(self.device_id.id()); + FromPrimitive::from_i32(status as i32).unwrap() + } + } + + /// Pauses playback of the audio device. + pub fn pause(&self) { + unsafe { ll::SDL_PauseAudioDevice(self.device_id.id(), 1) } + } + + /// Starts playback of the audio device. + pub fn resume(&self) { + unsafe { ll::SDL_PauseAudioDevice(self.device_id.id(), 0) } + } + + /// Adds data to the audio queue. + pub fn queue(&self, data: &[Channel]) -> bool { + let result = unsafe {ll::SDL_QueueAudio(self.device_id.id(), data.as_ptr() as *const c_void, data.len() as u32)}; + result == 0 + } + + pub fn size(&self) -> u32 { + unsafe {ll::SDL_GetQueuedAudioSize(self.device_id.id())} + } + + /// Clears all data from the current audio queue. + pub fn clear(&self) { + unsafe {ll::SDL_ClearQueuedAudio(self.device_id.id());} + } +} + /// Wraps SDL_AudioDeviceID and owns the callback data used by the audio device. pub struct AudioDevice { subsystem: AudioSubsystem,