extern crate tsf_sys;
use tsf_sys::*;
use std::ffi::{CString, c_void};
use std::os::raw::c_int;
pub struct Tsf {
tsf_ptr: *mut tsf_sys::tsf,
output_mode: Option<OutputMode>
}
unsafe impl Send for Tsf {}
unsafe impl Sync for Tsf {}
impl Drop for Tsf {
fn drop(&mut self) {
unsafe { tsf_close(self.tsf_ptr) };
}
}
impl Tsf {
fn new(tsf_ptr: *mut tsf) -> Self {
Tsf {
tsf_ptr,
output_mode : None
}
}
pub fn load_filename<T: Into<Vec<u8>>>(filename: T) -> Option<Tsf> {
let filename_cstring = CString::new(filename).expect("filename wasn't a valid C string - did it have an internal 0 byte?");
let tsf_ptr: *mut tsf = unsafe { tsf_load_filename(filename_cstring.as_ptr()) };
if tsf_ptr.is_null() { None } else { Some(Tsf::new(tsf_ptr)) }
}
pub fn load_memory<T: Into<Vec<u8>>>(buffer: T) -> Option<Tsf> {
let vec = buffer.into();
let tsf_ptr: *mut tsf = unsafe {
tsf_load_memory(vec.as_ptr() as *const c_void, vec.len() as c_int)
};
if tsf_ptr.is_null() { None } else { Some(Tsf::new(tsf_ptr)) }
}
pub fn close(self) {
}
pub fn set_output(&mut self, mode: OutputMode, sample_rate: u16, global_gain_db: f32) {
let converted_mode = match mode {
OutputMode::StereoInterleaved => TSFOutputMode_TSF_STEREO_INTERLEAVED,
OutputMode::StereoUnweaved => TSFOutputMode_TSF_STEREO_UNWEAVED,
OutputMode::Mono => TSFOutputMode_TSF_MONO,
};
unsafe { tsf_set_output(self.tsf_ptr, converted_mode, sample_rate as c_int, global_gain_db) };
self.output_mode = Some(mode);
}
pub fn render_float(&mut self, samples: usize) -> Vec<f32> {
let output_channels = match self.output_mode.as_ref().expect("set_output not yet called") {
OutputMode::StereoInterleaved => 2,
OutputMode::StereoUnweaved => 2,
OutputMode::Mono => 1,
};
let mut dst: Vec<f32> = Vec::with_capacity(samples * output_channels);
let dst_ptr: *mut f32 = dst.as_mut_ptr();
unsafe {
tsf_render_float(self.tsf_ptr, dst_ptr, samples as c_int, 0);
dst.set_len(samples*output_channels);
}
dst
}
pub fn channel_note_on(&mut self, channel: u16, key: u8, vel: f32) {
assert!(key <= 127u8, "key must be between 0 and 127");
assert!(vel >= 0f32 && vel <= 1f32, "vel must be between 0.0 and 1.0");
unsafe { tsf_channel_note_on(self.tsf_ptr, channel as c_int, key as c_int, vel) };
}
pub fn channel_note_off(&mut self, channel: u16, key: u8) {
assert!(key <= 127u8, "key must be between 0 and 127");
unsafe { tsf_channel_note_off(self.tsf_ptr, channel as c_int, key as c_int) };
}
pub fn channel_note_off_all(&mut self, channel: u16) {
unsafe { tsf_channel_note_off_all(self.tsf_ptr, channel as c_int) };
}
pub fn note_off_all(&mut self) {
unsafe { tsf_note_off_all(self.tsf_ptr) };
}
pub fn channel_set_preset_number(&mut self, channel: u16, preset_number: u16, mididrums: bool) {
unsafe { tsf_channel_set_presetnumber(self.tsf_ptr, channel as c_int, preset_number as c_int, if mididrums { 1 } else { 0 }) };
}
pub fn get_preset_count(&mut self) -> u16 {
unsafe { tsf_get_presetcount(self.tsf_ptr) as u16 }
}
}
pub enum OutputMode {
StereoInterleaved,
StereoUnweaved,
Mono
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn load_filename_and_render_c3() {
let mut tsf = Tsf::load_filename("test_resources/sinewave.sf2").unwrap();
assert_eq!(1, tsf.get_preset_count());
let sample_rate = 48000;
let samples = (sample_rate / 10) as usize;
tsf.set_output(OutputMode::Mono, sample_rate, 0f32);
let silence_samples = tsf.render_float(samples);
assert!(silence_samples.into_iter().all(|x| x==0f32), "Didn't get silence");
tsf.channel_set_preset_number(0, 0, false);
tsf.channel_note_on(0, 48, 1f32);
let note_on_samples = tsf.render_float(4800);
assert!(note_on_samples.into_iter().any(|x| x!=0f32), "Got silence");
tsf.channel_note_off(0, 48);
let note_off_samples = tsf.render_float(4800);
assert!(note_off_samples.into_iter().any(|x| x!=0f32), "Got silence");
for _ in 0..100 {
tsf.render_float(samples);
}
let silence_after_samples = tsf.render_float(samples);
assert!(silence_after_samples.into_iter().all(|x| x==0f32), "Didn't get silence");
}
}