From 313583bc0d34b76d45a444658ba49ff0cefe0c9a Mon Sep 17 00:00:00 2001 From: Andelf Date: Fri, 11 Apr 2014 00:44:17 +0800 Subject: [PATCH 1/4] add a workable SDL_audio implementation --- src/sdl2/audio.rs | 354 ++++++++++++++++++++++++++++++++++++++++++++++ src/sdl2/lib.rs | 1 + 2 files changed, 355 insertions(+) create mode 100644 src/sdl2/audio.rs diff --git a/src/sdl2/audio.rs b/src/sdl2/audio.rs new file mode 100644 index 00000000..f8e78bc0 --- /dev/null +++ b/src/sdl2/audio.rs @@ -0,0 +1,354 @@ +use std::cast; +use std::ptr; +use std::mem; +use std::c_str::CString; +use std::c_vec::CVec; +use libc; +use libc::{c_int, size_t, c_void}; +use libc::{uint8_t, uint16_t, uint32_t}; +use std::raw::Slice; + +use get_error; +use rwops::RWops; + + +#[allow(non_camel_case_types)] +pub mod ll { + use libc::{c_int, c_uint, c_void, uint8_t, uint32_t}; + use libc::{uint16_t, c_double, c_char}; + use rwops::ll::SDL_RWops; + + // assume LSB + pub type SDL_AudioFormat = uint16_t; + pub static AUDIO_U8 : SDL_AudioFormat = 0x0008; + pub static AUDIO_S8 : SDL_AudioFormat = 0x8008; + pub static AUDIO_U16LSB : SDL_AudioFormat = 0x0010; + pub static AUDIO_S16LSB : SDL_AudioFormat = 0x8010; + pub static AUDIO_U16MSB : SDL_AudioFormat = 0x1010; + pub static AUDIO_S16MSB : SDL_AudioFormat = 0x9010; + pub static AUDIO_U16 : SDL_AudioFormat = AUDIO_U16LSB; + pub static AUDIO_S16 : SDL_AudioFormat = AUDIO_S16LSB; + pub static AUDIO_S32LSB : SDL_AudioFormat = 0x8020; + pub static AUDIO_S32MSB : SDL_AudioFormat = 0x9020; + pub static AUDIO_S32 : SDL_AudioFormat = AUDIO_S32LSB; + pub static AUDIO_F32LSB : SDL_AudioFormat = 0x8120; + pub static AUDIO_F32MSB : SDL_AudioFormat = 0x9120; + pub static AUDIO_F32 : SDL_AudioFormat = AUDIO_F32LSB; + pub static AUDIO_U16SYS : SDL_AudioFormat = AUDIO_U16LSB; + pub static AUDIO_S16SYS : SDL_AudioFormat = AUDIO_S16LSB; + pub static AUDIO_S32SYS : SDL_AudioFormat = AUDIO_S32LSB; + pub static AUDIO_F32SYS : SDL_AudioFormat = AUDIO_F32LSB; + + pub type SDL_AudioCallback = + ::std::option::Option; + pub struct SDL_AudioSpec { + pub freq: c_int, + pub format: SDL_AudioFormat, + pub channels: uint8_t, + pub silence: uint8_t, + pub samples: uint16_t, + pub padding: uint16_t, + pub size: uint32_t, + pub callback: SDL_AudioCallback, + pub userdata: *c_void, + } + pub type SDL_AudioFilter = + ::std::option::Option; + pub struct SDL_AudioCVT { + pub needed: c_int, + pub src_format: SDL_AudioFormat, + pub dst_format: SDL_AudioFormat, + pub rate_incr: c_double, + pub buf: *uint8_t, + pub len: c_int, + pub len_cvt: c_int, + pub len_mult: c_int, + pub len_ratio: c_double, + pub filters: [SDL_AudioFilter, ..10u], + pub filter_index: c_int, + } + pub type SDL_AudioDeviceID = uint32_t; + pub type SDL_AudioStatus = c_uint; + pub static SDL_AUDIO_STOPPED: c_uint = 0; + pub static SDL_AUDIO_PLAYING: c_uint = 1; + pub static SDL_AUDIO_PAUSED: c_uint = 2; + extern "C" { + pub fn SDL_GetNumAudioDrivers() -> c_int; + pub fn SDL_GetAudioDriver(index: c_int) -> *c_char; + pub fn SDL_AudioInit(driver_name: *c_char) -> c_int; + pub fn SDL_AudioQuit(); + pub fn SDL_GetCurrentAudioDriver() -> *c_char; + pub fn SDL_OpenAudio(desired: *SDL_AudioSpec, + obtained: *SDL_AudioSpec) -> c_int; + pub fn SDL_GetNumAudioDevices(iscapture: c_int) -> c_int; + pub fn SDL_GetAudioDeviceName(index: c_int, iscapture: c_int) -> *c_char; + pub fn SDL_OpenAudioDevice(device: *c_char, iscapture: c_int, + desired: *SDL_AudioSpec, + obtained: *SDL_AudioSpec, + allowed_changes: c_int) -> SDL_AudioDeviceID; + pub fn SDL_GetAudioStatus() -> SDL_AudioStatus; + pub fn SDL_GetAudioDeviceStatus(dev: SDL_AudioDeviceID) -> + SDL_AudioStatus; + pub fn SDL_PauseAudio(pause_on: c_int); + pub fn SDL_PauseAudioDevice(dev: SDL_AudioDeviceID, pause_on: c_int); + pub fn SDL_LoadWAV_RW(src: *SDL_RWops, freesrc: c_int, + spec: *SDL_AudioSpec, + audio_buf: **uint8_t, audio_len: *uint32_t) -> *SDL_AudioSpec; + pub fn SDL_FreeWAV(audio_buf: *uint8_t); + pub fn SDL_BuildAudioCVT(cvt: *SDL_AudioCVT, + src_format: SDL_AudioFormat, src_channels: uint8_t, + src_rate: c_int, dst_format: SDL_AudioFormat, + dst_channels: uint8_t, dst_rate: c_int) -> c_int; + pub fn SDL_ConvertAudio(cvt: *SDL_AudioCVT) -> c_int; + pub fn SDL_MixAudio(dst: *uint8_t, src: *uint8_t, len: uint32_t, + volume: c_int); + pub fn SDL_MixAudioFormat(dst: *uint8_t, src: *uint8_t, + format: SDL_AudioFormat, len: uint32_t, + volume: c_int); + pub fn SDL_LockAudio(); + pub fn SDL_LockAudioDevice(dev: SDL_AudioDeviceID); + pub fn SDL_UnlockAudio(); + pub fn SDL_UnlockAudioDevice(dev: SDL_AudioDeviceID); + pub fn SDL_CloseAudio(); + pub fn SDL_CloseAudioDevice(dev: SDL_AudioDeviceID); + } + +} + + +pub type AudioFormat = ll::SDL_AudioFormat; + +pub static AudioU8 : AudioFormat = ll::AUDIO_U8; +pub static AudioS8 : AudioFormat = ll::AUDIO_S8; +pub static AudioU16LSB : AudioFormat = ll::AUDIO_U16LSB; +pub static AudioS16LSB : AudioFormat = ll::AUDIO_S16LSB; +pub static AudioU16MSB : AudioFormat = ll::AUDIO_U16MSB; +pub static AudioS16MSB : AudioFormat = ll::AUDIO_S16MSB; +pub static AudioU16 : AudioFormat = ll::AUDIO_U16; +pub static AudioS16 : AudioFormat = ll::AUDIO_S16; +pub static AudioS32LSB : AudioFormat = ll::AUDIO_S32LSB; +pub static AudioS32MSB : AudioFormat = ll::AUDIO_S32MSB; +pub static AudioS32 : AudioFormat = ll::AUDIO_S32; +pub static AudioF32LSB : AudioFormat = ll::AUDIO_F32LSB; +pub static AudioF32MSB : AudioFormat = ll::AUDIO_F32MSB; +pub static AudioF32 : AudioFormat = ll::AUDIO_F32; +pub static AudioU16SYS : AudioFormat = ll::AUDIO_U16SYS; +pub static AudioS16SYS : AudioFormat = ll::AUDIO_S16SYS; +pub static AudioS32SYS : AudioFormat = ll::AUDIO_S32SYS; +pub static AudioF32SYS : AudioFormat = ll::AUDIO_F32SYS; + +#[repr(C)] +#[deriving(Clone, Eq, Hash, Show, FromPrimitive)] +pub enum AudioStatus { + Stopped = ll::SDL_AUDIO_STOPPED as int, + Playing = ll::SDL_AUDIO_PLAYING as int, + Paused = ll::SDL_AUDIO_PAUSED as int, +} + +pub fn get_num_audio_drivers() -> int { + unsafe { ll::SDL_GetNumAudioDrivers() as int } +} + +pub fn get_audio_driver(index: int) -> ~str { + unsafe { + let buf = ll::SDL_GetAudioDriver(index as c_int); + CString::new(buf, false).as_str().unwrap().into_owned() + } +} + +pub fn get_num_audio_devices(iscapture: int) -> int { + unsafe { ll::SDL_GetNumAudioDevices(iscapture as c_int) as int } +} + +pub fn get_audio_device_name(index: int, iscapture: int) -> ~str { + unsafe { + let buf = ll::SDL_GetAudioDeviceName(index as c_int, iscapture as c_int); + CString::new(buf, false).as_str().unwrap().into_owned() + } +} + +pub fn audio_init(name: &str) -> Result<(), ~str> { + let ret = name.with_c_str(|buf| { + unsafe { ll::SDL_AudioInit(buf) } + }); + if ret == 0 { + Ok(()) + } else { + Err(get_error()) + } +} + +pub fn audio_quit() { + unsafe { ll::SDL_AudioQuit() } +} + +pub fn get_current_audio_driver() -> ~str { + unsafe { + let buf = ll::SDL_GetCurrentAudioDriver(); + CString::new(buf, false).as_str().unwrap().into_owned() + } +} + + +// make this same layout as in C +#[repr(C)] +pub struct AudioSpec<'a > { + pub freq: c_int, + pub format: AudioFormat, + pub channels: uint8_t, + pub silence: uint8_t, + pub samples: uint16_t, + pub padding: uint16_t, + pub size: uint32_t, + c_callback: ll::SDL_AudioCallback, + pub callback: &'a |&mut [u8]|:'a, // same size as *c_void +} + + +extern "C" fn c_audio_callback(userdata: *c_void, stream: *uint8_t, len: c_int) { + unsafe { + let f : &|&mut [u8]| = cast::transmute(userdata); + + // FIXME: lifetime error in calling + //slice::raw::mut_buf_as_slice(stream as *mut u8, len as uint, *f) + (*f)(cast::transmute(Slice { + data: stream, + len: len as uint + })) + } +} + + +impl<'a> AudioSpec<'a> { + fn alloc() -> AudioSpec { + unsafe { mem::uninit::() } + } + + pub fn load_wav(path: &Path) -> Result<(~AudioSpec, CVec), ~str> { + AudioSpec::load_wav_rw(try!(RWops::from_file(path, "rb"))) + } + + pub fn load_wav_rw(src: &RWops) -> Result<(~AudioSpec, CVec), ~str> { + assert_eq!(mem::size_of::(), mem::size_of::()); + let mut spec = AudioSpec::alloc(); + let audio_buf = ptr::null::(); + let audio_len = 0u32; + unsafe { + let ret = ll::SDL_LoadWAV_RW(src.raw, 0, cast::transmute(&spec), &audio_buf, &audio_len); + if ret.is_null() { + Err(get_error()) + } else { + let v = CVec::new_with_dtor(audio_buf as *mut u8, audio_len as uint, proc() { + ll::SDL_FreeWAV(audio_buf) + }); + spec.c_callback = Some(c_audio_callback); + Ok((~spec, v)) + } + } + } +} + +pub type AudioDeviceID = ll::SDL_AudioDeviceID; + +// use rust's type system to make it right. +pub enum AudioDevice{ + PlaybackDevice(AudioDeviceID), + RecordingDevice(AudioDeviceID), +} + +impl AudioDevice { + fn to_id(self) -> AudioDeviceID { + match self { + PlaybackDevice(id) => id, + RecordingDevice(id) => id + } + } + + pub fn open(device: &str, iscapture: int, spec: &AudioSpec) -> Result<(AudioDevice, ~AudioSpec), ~str> { + //! SDL_OpenAudioDevice + let obtained = AudioSpec::alloc(); + unsafe { + let ret = ll::SDL_OpenAudioDevice(device.to_c_str().unwrap(), + iscapture as c_int, + cast::transmute(spec), + cast::transmute(&obtained), + 0); + if ret == 0 { + Err(get_error()) + } else { + if iscapture == 0 { // plaback device + Ok((PlaybackDevice(ret as AudioDeviceID), ~obtained)) + } else { + Ok((RecordingDevice(ret as AudioDeviceID), ~obtained)) + } + } + } + } + + pub fn get_status(self) -> AudioStatus { + unsafe { + let status = ll::SDL_GetAudioDeviceStatus(self.to_id()); + FromPrimitive::from_int(status as int).unwrap() + } + } + + pub fn pause(self) { + unsafe { ll::SDL_PauseAudioDevice(self.to_id(), 1) } + } + + pub fn resume(self) { + unsafe { ll::SDL_PauseAudioDevice(self.to_id(), 0) } + } + + pub fn lock(self) { + unsafe { ll::SDL_LockAudioDevice(self.to_id()) } + } + + pub fn unlock(self) { + unsafe { ll::SDL_UnlockAudioDevice(self.to_id()) } + } + + pub fn close(self) { + //! Shut down audio processing and close the audio device. + unsafe { ll::SDL_CloseAudioDevice(self.to_id()) } + } +} + + + +#[deriving(Eq)] #[allow(raw_pointer_deriving)] +pub struct AudioCVT { + pub raw: *ll::SDL_AudioCVT, + pub owned: bool, +} + +impl AudioCVT { + pub fn new(src_format: AudioFormat, src_channels: u8, src_rate: int, + dst_format: AudioFormat, dst_channels: u8, dst_rate: int) -> Result<~AudioCVT, ~str> { + unsafe { + let c_cvt_p = libc::malloc(mem::size_of::() as size_t) as *ll::SDL_AudioCVT; + let ret = ll::SDL_BuildAudioCVT(c_cvt_p, + src_format, src_channels, src_rate as c_int, + dst_format, dst_channels, dst_rate as c_int); + if ret == 1 || ret == 0 { + Ok(~AudioCVT { raw: c_cvt_p, owned: true }) + } else { + Err(get_error()) + } + } + } + + pub fn convert(&self) -> Result<(), ~str> { + //! Convert audio data to a desired audio format. + let ret = unsafe { ll::SDL_ConvertAudio(self.raw) }; + if ret == 0 { + Ok(()) + } else { + Err(get_error()) + } + } +} diff --git a/src/sdl2/lib.rs b/src/sdl2/lib.rs index a0841441..96084215 100644 --- a/src/sdl2/lib.rs +++ b/src/sdl2/lib.rs @@ -30,3 +30,4 @@ pub mod timer; pub mod render; pub mod rwops; pub mod sdl; +pub mod audio; From 469066786c60befacdb0989e5c7f48f89c006063 Mon Sep 17 00:00:00 2001 From: Andelf Date: Fri, 11 Apr 2014 00:47:30 +0800 Subject: [PATCH 2/4] fix unhandled error in calling RWops::from_file --- src/sdl2/surface.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sdl2/surface.rs b/src/sdl2/surface.rs index 4405b1bc..ac78cd60 100644 --- a/src/sdl2/surface.rs +++ b/src/sdl2/surface.rs @@ -166,7 +166,7 @@ impl Surface { pub fn from_bmp(path: &Path) -> Result<~Surface, ~str> { let raw = unsafe { - ll::SDL_LoadBMP_RW(rwops::RWops::from_file(path, "rb").unwrap().raw, 0) + ll::SDL_LoadBMP_RW(try!(rwops::RWops::from_file(path, "rb")).raw, 0) }; if raw.is_null() { Err(get_error()) } @@ -175,7 +175,7 @@ impl Surface { pub fn save_bmp(&self, path: &Path) -> bool { unsafe { - ll::SDL_SaveBMP_RW(self.raw, rwops::RWops::from_file(path, "rb").unwrap().raw, 0) == 0 + ll::SDL_SaveBMP_RW(self.raw, try!(rwops::RWops::from_file(path, "rb")).raw, 0) == 0 } } From 00f48eeaa850ca4cbb50fc366e043899794fe2b3 Mon Sep 17 00:00:00 2001 From: Andelf Date: Fri, 11 Apr 2014 07:42:01 +0800 Subject: [PATCH 3/4] fix return type error --- src/sdl2/surface.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/sdl2/surface.rs b/src/sdl2/surface.rs index ac78cd60..218e9989 100644 --- a/src/sdl2/surface.rs +++ b/src/sdl2/surface.rs @@ -173,10 +173,12 @@ impl Surface { else { Ok(~Surface{raw: raw, owned: true}) } } - pub fn save_bmp(&self, path: &Path) -> bool { - unsafe { - ll::SDL_SaveBMP_RW(self.raw, try!(rwops::RWops::from_file(path, "rb")).raw, 0) == 0 - } + pub fn save_bmp(&self, path: &Path) -> Result<(), ~str> { + let ret = unsafe { + ll::SDL_SaveBMP_RW(self.raw, try!(rwops::RWops::from_file(path, "rb")).raw, 0) + }; + if ret == 0 { Ok(()) } + else { Err(get_error()) } } pub fn set_palette(&self, palette: &pixels::Palette) -> bool { From 13a0f8b5517749ca84341e51d02931f156a1d3c8 Mon Sep 17 00:00:00 2001 From: Andelf Date: Sat, 12 Apr 2014 01:06:33 +0800 Subject: [PATCH 4/4] sdl_audio: implement AudioCVT --- src/sdl2/audio.rs | 64 ++++++++++++++++++++++++++++++----------------- 1 file changed, 41 insertions(+), 23 deletions(-) diff --git a/src/sdl2/audio.rs b/src/sdl2/audio.rs index f8e78bc0..8bd8b63c 100644 --- a/src/sdl2/audio.rs +++ b/src/sdl2/audio.rs @@ -63,13 +63,13 @@ pub mod ll { pub src_format: SDL_AudioFormat, pub dst_format: SDL_AudioFormat, pub rate_incr: c_double, - pub buf: *uint8_t, + pub buf: *mut uint8_t, pub len: c_int, pub len_cvt: c_int, pub len_mult: c_int, pub len_ratio: c_double, - pub filters: [SDL_AudioFilter, ..10u], - pub filter_index: c_int, + filters: [SDL_AudioFilter, ..10u], + filter_index: c_int, } pub type SDL_AudioDeviceID = uint32_t; pub type SDL_AudioStatus = c_uint; @@ -99,11 +99,11 @@ pub mod ll { spec: *SDL_AudioSpec, audio_buf: **uint8_t, audio_len: *uint32_t) -> *SDL_AudioSpec; pub fn SDL_FreeWAV(audio_buf: *uint8_t); - pub fn SDL_BuildAudioCVT(cvt: *SDL_AudioCVT, + pub fn SDL_BuildAudioCVT(cvt: *mut SDL_AudioCVT, src_format: SDL_AudioFormat, src_channels: uint8_t, src_rate: c_int, dst_format: SDL_AudioFormat, dst_channels: uint8_t, dst_rate: c_int) -> c_int; - pub fn SDL_ConvertAudio(cvt: *SDL_AudioCVT) -> c_int; + pub fn SDL_ConvertAudio(cvt: *mut SDL_AudioCVT) -> c_int; pub fn SDL_MixAudio(dst: *uint8_t, src: *uint8_t, len: uint32_t, volume: c_int); pub fn SDL_MixAudioFormat(dst: *uint8_t, src: *uint8_t, @@ -193,7 +193,6 @@ pub fn get_current_audio_driver() -> ~str { } } - // make this same layout as in C #[repr(C)] pub struct AudioSpec<'a > { @@ -208,7 +207,6 @@ pub struct AudioSpec<'a > { pub callback: &'a |&mut [u8]|:'a, // same size as *c_void } - extern "C" fn c_audio_callback(userdata: *c_void, stream: *uint8_t, len: c_int) { unsafe { let f : &|&mut [u8]| = cast::transmute(userdata); @@ -224,17 +222,13 @@ extern "C" fn c_audio_callback(userdata: *c_void, stream: *uint8_t, len: c_int) impl<'a> AudioSpec<'a> { - fn alloc() -> AudioSpec { - unsafe { mem::uninit::() } - } - pub fn load_wav(path: &Path) -> Result<(~AudioSpec, CVec), ~str> { AudioSpec::load_wav_rw(try!(RWops::from_file(path, "rb"))) } pub fn load_wav_rw(src: &RWops) -> Result<(~AudioSpec, CVec), ~str> { assert_eq!(mem::size_of::(), mem::size_of::()); - let mut spec = AudioSpec::alloc(); + let mut spec = unsafe { mem::uninit::() }; let audio_buf = ptr::null::(); let audio_len = 0u32; unsafe { @@ -270,7 +264,7 @@ impl AudioDevice { pub fn open(device: &str, iscapture: int, spec: &AudioSpec) -> Result<(AudioDevice, ~AudioSpec), ~str> { //! SDL_OpenAudioDevice - let obtained = AudioSpec::alloc(); + let obtained = unsafe { mem::uninit::() }; unsafe { let ret = ll::SDL_OpenAudioDevice(device.to_c_str().unwrap(), iscapture as c_int, @@ -318,19 +312,25 @@ impl AudioDevice { } } - - #[deriving(Eq)] #[allow(raw_pointer_deriving)] pub struct AudioCVT { - pub raw: *ll::SDL_AudioCVT, + pub raw: *mut ll::SDL_AudioCVT, pub owned: bool, } +impl Drop for AudioCVT { + fn drop(&mut self) { + if self.owned { + unsafe { libc::free(self.raw as *mut c_void) } + } + } +} + impl AudioCVT { pub fn new(src_format: AudioFormat, src_channels: u8, src_rate: int, dst_format: AudioFormat, dst_channels: u8, dst_rate: int) -> Result<~AudioCVT, ~str> { unsafe { - let c_cvt_p = libc::malloc(mem::size_of::() as size_t) as *ll::SDL_AudioCVT; + let c_cvt_p = libc::malloc(mem::size_of::() as size_t) as *mut ll::SDL_AudioCVT; let ret = ll::SDL_BuildAudioCVT(c_cvt_p, src_format, src_channels, src_rate as c_int, dst_format, dst_channels, dst_rate as c_int); @@ -342,13 +342,31 @@ impl AudioCVT { } } - pub fn convert(&self) -> Result<(), ~str> { + pub fn convert(&self, src: CVec) -> Result, ~str> { //! Convert audio data to a desired audio format. - let ret = unsafe { ll::SDL_ConvertAudio(self.raw) }; - if ret == 0 { - Ok(()) - } else { - Err(get_error()) + + unsafe { + if (*self.raw).needed != 1 { + return Err(~"no convertion needed!") + } + // set len + (*self.raw).len = src.len() as c_int; + // alloc buf + let size = (*self.raw).len * (*self.raw).len_mult; + (*self.raw).buf = libc::malloc(size as size_t) as *mut u8; + // set buf + ptr::copy_memory::((*self.raw).buf, src.as_slice().as_ptr(), src.len()); + // convert + let ret = ll::SDL_ConvertAudio(self.raw); + // return + let p = (*self.raw).buf as *mut c_void; // send to proc() + if ret == 0 { + Ok( CVec::new_with_dtor((*self.raw).buf as *mut u8, (*self.raw).len_cvt as uint, + proc() { libc::free(p) }) + ) + } else { + Err(get_error()) + } } } }