mirror of
https://github.com/encounter/rust-sdl2.git
synced 2026-07-10 21:18:41 -07:00
Provide memory buffer -> mixer Chunk conversion methods
This basically provides an API to access Mix_QuickLoad_WAV and Mix_QuickLoad_RAW with one caveat – thie API takes ownership of the buffers, while Mix_QuickLoad_* functions don't. Since Rust-SDL2 wraps Mix_Chunks in its own structure with its own buffer ownership flag it's easy to work around that and deallocate the buffer when Chunk is dropped. There are few design considerations here: * Taking ownership of the buffer. I couldn't come up with a way to get a Chunk based on a buffer while still keeping the code safe and making sure that as long as Chunk is alive the buffer is also alive *and* not making the change be terribly involved and disruptive * What's the best way to take an ownership of a buffer passed as a parameter? I came up with Box<[T]> as it seems universal enough, but I expect a more idiomatic Rust way may exist. I modified the mixer demo to show how the API is used, a beep is generated when no sound file is passed to the example.
This commit is contained in:
+18
-3
@@ -70,9 +70,24 @@ fn demo(music_file: &Path, sound_file: Option<&Path>) -> Result<(), String> {
|
||||
println!("music volume => {:?}", sdl2::mixer::Music::get_volume());
|
||||
println!("play => {:?}", music.play(1));
|
||||
|
||||
if let Some(sound_file_path) = sound_file {
|
||||
let sound_chunk = sdl2::mixer::Chunk::from_file(sound_file_path)
|
||||
.map_err(|e| format!("Cannot load sound file: {:?}", e))?;
|
||||
{
|
||||
let sound_chunk = match sound_file {
|
||||
Some(sound_file_path) => sdl2::mixer::Chunk::from_file(sound_file_path)
|
||||
.map_err(|e| format!("Cannot load sound file: {:?}", e))?,
|
||||
None => {
|
||||
// One second of 500Hz sine wave using equation A * sin(2 * PI * f * t)
|
||||
// (played at half the volume to save people's ears).
|
||||
let buffer = (0..frequency)
|
||||
.map(|i| {
|
||||
(0.1 * i16::max_value() as f32
|
||||
* (2.0 * 3.14 * 500.0 * (i as f32 / frequency as f32)).sin())
|
||||
as i16
|
||||
})
|
||||
.collect();
|
||||
sdl2::mixer::Chunk::from_raw_buffer(buffer)
|
||||
.map_err(|e| format!("Cannot get chunk from buffer: {:?}", e))?
|
||||
}
|
||||
};
|
||||
|
||||
println!("chunk volume => {:?}", sound_chunk.get_volume());
|
||||
println!("playing sound twice");
|
||||
|
||||
+35
-1
@@ -21,6 +21,7 @@
|
||||
//! ```
|
||||
|
||||
use std::marker::PhantomData;
|
||||
use std::convert::TryInto;
|
||||
use std::default;
|
||||
use std::fmt;
|
||||
use std::ffi::{CString, CStr};
|
||||
@@ -29,6 +30,7 @@ use std::borrow::ToOwned;
|
||||
use std::path::Path;
|
||||
use libc::c_void;
|
||||
use libc::{c_int, c_double, c_uint};
|
||||
use ::audio::AudioFormatNum;
|
||||
use ::get_error;
|
||||
use ::rwops::RWops;
|
||||
use ::version::Version;
|
||||
@@ -229,7 +231,16 @@ pub struct Chunk {
|
||||
impl Drop for Chunk {
|
||||
fn drop(&mut self) {
|
||||
if self.owned {
|
||||
unsafe { mixer::Mix_FreeChunk(self.raw) }
|
||||
unsafe {
|
||||
// Mix_QuickLoad_* functions don't set the allocated flag, but
|
||||
// from_wav_buffer and from_raw_buffer *do* take ownership of the data,
|
||||
// so we need to deallocate the buffers here, because Mix_FreeChunk won't
|
||||
// and we'd be leaking memory otherwise.
|
||||
if (*self.raw).allocated == 0 {
|
||||
drop(Box::from_raw((*self.raw).abuf));
|
||||
}
|
||||
mixer::Mix_FreeChunk(self.raw);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -238,6 +249,29 @@ impl Chunk {
|
||||
/// Load file for use as a sample.
|
||||
pub fn from_file<P: AsRef<Path>>(path: P) -> Result<Chunk, String> {
|
||||
let raw = unsafe { mixer::Mix_LoadWAV_RW(RWops::from_file(path, "rb")?.raw(), 0) };
|
||||
Self::from_owned_raw(raw)
|
||||
}
|
||||
|
||||
/// Get chunk based on a buffer containing WAV data in the mixer format. The chunk takes
|
||||
/// ownership of the buffer.
|
||||
pub fn from_wav_buffer(buffer: Box<[u8]>) -> Result<Chunk, String> {
|
||||
let raw = unsafe { mixer::Mix_QuickLoad_WAV(Box::into_raw(buffer) as *mut u8) };
|
||||
Self::from_owned_raw(raw)
|
||||
}
|
||||
|
||||
/// Load chunk from a buffer containing raw audio data in the mixer format. The length of the
|
||||
/// buffer has to fit in 32-bit unsigned integer. The chunk takes ownership of the buffer.
|
||||
///
|
||||
/// It's your responsibility to provide the audio data in the right format, as no conversion
|
||||
/// will take place when using this method.
|
||||
pub fn from_raw_buffer<T: AudioFormatNum>(buffer: Box<[T]>) -> Result<Chunk, String> {
|
||||
use std::mem::size_of;
|
||||
let len: u32 = (buffer.len() * size_of::<T>()).try_into().unwrap();
|
||||
let raw = unsafe { mixer::Mix_QuickLoad_RAW(Box::into_raw(buffer) as *mut u8, len) };
|
||||
Self::from_owned_raw(raw)
|
||||
}
|
||||
|
||||
fn from_owned_raw(raw: *mut mixer::Mix_Chunk) -> Result<Chunk, String> {
|
||||
if raw.is_null() {
|
||||
Err(get_error())
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user