tsf/
lib.rs

1extern crate tsf_sys;
2
3use tsf_sys::*;
4use std::ffi::{CString, c_void};
5use std::os::raw::c_int;
6
7/// Object holding a pointer to a tsf instance.
8///
9/// This object manages the lifecycle of the tsf instance, remembering to free it when dropped.
10///
11/// Method signatures reflect their mutability, i.e. if a function would mutate the underlying C
12/// structure, then it requires a mutable pointer. This includes basically all of the methods.
13///
14/// Whilst `Tsf` implements `Send` and `Sync`, requiring a mutable pointer to use many of the
15/// methods ensures that two threads cannot mutate the underlying C object at once. Often, however,
16/// this is exactly what you want to do: one thread calls `channel_note_on` and `channel_note_off`
17/// whilst another thread (often driven by the audio device or some API into it) will be requesting
18/// data via `render_float`. For this, you will need to consider some kind of synchronization
19/// solution.
20///
21/// A simple solution involves moving `Tsf` into a `Mutex<Tsf>`. Then, when each thread
22/// wants to use it, they can lock their `Mutex` to acquire a guard which dereferences to
23/// `&mut Tsf`. The downside is that each thread may block as it waits to acquire the lock.
24///
25/// Another solution is to have the render thread maintain ownership of `Tsf`, and this thread has a
26/// `Receiver` half of an mpsc channel which it periodically checks for messages (e.g. once between
27/// each call to `render_float`). These messages contain instructions to be carried out on a
28/// `&mut Tsf` (which the render thread has access to and no other threads do). The messages are
29/// sent from other threads which want to mutate `Tsf` with e.g. `channel_note_on` commands. The
30/// key advantage of this approach is that the render thread is in full control, and never blocks
31/// waiting for a lock. It can choose to process as many or as few messages as it likes at a time.
32pub struct Tsf {
33    /// The unsafe pointer to tsf itself.
34    tsf_ptr: *mut tsf_sys::tsf,
35    /// We cache the output mode that we last configured TSF with. This is None iff `set_output` has
36    /// not been called.
37    output_mode: Option<OutputMode>
38}
39
40unsafe impl Send for Tsf {}
41unsafe impl Sync for Tsf {}
42
43impl Drop for Tsf {
44    fn drop(&mut self) {
45        unsafe { tsf_close(self.tsf_ptr) };
46    }
47}
48
49impl Tsf {
50    fn new(tsf_ptr: *mut tsf) -> Self {
51        Tsf {
52            tsf_ptr,
53            output_mode : None
54        }
55    }
56
57    /// Load from a filename of a soundfont (.sf2) file.
58    // TODO: Should really take a path or something like that
59    // TODO: Should really return a result
60    pub fn load_filename<T: Into<Vec<u8>>>(filename: T) -> Option<Tsf> {
61        let filename_cstring = CString::new(filename).expect("filename wasn't a valid C string - did it have an internal 0 byte?");
62        let tsf_ptr: *mut tsf = unsafe { tsf_load_filename(filename_cstring.as_ptr()) };
63        if tsf_ptr.is_null() { None } else { Some(Tsf::new(tsf_ptr)) }
64    }
65
66    /// Load from memory.
67    pub fn load_memory<T: Into<Vec<u8>>>(buffer: T) -> Option<Tsf> {
68        let vec = buffer.into();
69        let tsf_ptr: *mut tsf = unsafe {
70            tsf_load_memory(vec.as_ptr() as *const c_void, vec.len() as c_int)
71        };
72        if tsf_ptr.is_null() { None } else { Some(Tsf::new(tsf_ptr)) }
73    }
74
75    /// Free the memory related to this TSF instance.
76    pub fn close(self) {
77        // Don't actually need to do anything. This function takes ownership (by move) of the TSF
78        // struct and then drops it, and the Drop code does the rest.
79    }
80
81    /// Setup the parameters for the voice render methods.
82    /// * `mode`: if mono or stereo and how stereo channel data is ordered
83    /// * `sample_rate`: the number of samples per second (output frequency)
84    /// * `global_gain_db`: volume gain in decibels (>0 means higher, <0 means lower)
85    pub fn set_output(&mut self, mode: OutputMode, sample_rate: u16, global_gain_db: f32) {
86        let converted_mode = match mode {
87            OutputMode::StereoInterleaved => TSFOutputMode_TSF_STEREO_INTERLEAVED,
88            OutputMode::StereoUnweaved => TSFOutputMode_TSF_STEREO_UNWEAVED,
89            OutputMode::Mono => TSFOutputMode_TSF_MONO,
90        };
91        unsafe { tsf_set_output(self.tsf_ptr, converted_mode, sample_rate as c_int, global_gain_db) };
92
93        // Remember the output mode that was set.
94        self.output_mode = Some(mode);
95    }
96
97    /// Render the specified number of samples as a float array.
98    ///
99    /// Panics if called before `set_output`.
100    ///
101    /// Returns a buffer of size: samples * number of output channels (the number of output channels
102    /// was specified by `Tsf::set_output`).
103    pub fn render_float(&mut self, samples: usize) -> Vec<f32> {
104        let output_channels = match self.output_mode.as_ref().expect("set_output not yet called") {
105            OutputMode::StereoInterleaved => 2,
106            OutputMode::StereoUnweaved => 2,
107            OutputMode::Mono => 1,
108        };
109
110        let mut dst: Vec<f32> = Vec::with_capacity(samples * output_channels);
111        let dst_ptr: *mut f32 = dst.as_mut_ptr();
112        unsafe {
113            tsf_render_float(self.tsf_ptr, dst_ptr, samples as c_int, 0);
114            dst.set_len(samples*output_channels);
115        }
116        dst
117    }
118
119    pub fn channel_note_on(&mut self, channel: u16, key: u8, vel: f32) {
120        assert!(key <= 127u8, "key must be between 0 and 127");
121        assert!(vel >= 0f32 && vel <= 1f32, "vel must be between 0.0 and 1.0");
122        unsafe { tsf_channel_note_on(self.tsf_ptr, channel as c_int, key as c_int, vel) };
123    }
124
125    pub fn channel_note_off(&mut self, channel: u16, key: u8) {
126        assert!(key <= 127u8, "key must be between 0 and 127");
127        unsafe { tsf_channel_note_off(self.tsf_ptr, channel as c_int, key as c_int) };
128    }
129
130    pub fn channel_note_off_all(&mut self, channel: u16) {
131        unsafe { tsf_channel_note_off_all(self.tsf_ptr, channel as c_int) };
132    }
133
134    pub fn note_off_all(&mut self) {
135        unsafe { tsf_note_off_all(self.tsf_ptr) };
136    }
137
138    pub fn channel_set_preset_number(&mut self, channel: u16, preset_number: u16, mididrums: bool) {
139        unsafe { tsf_channel_set_presetnumber(self.tsf_ptr, channel as c_int, preset_number as c_int, if mididrums { 1 } else { 0 }) };
140    }
141
142    pub fn get_preset_count(&mut self) -> u16 {
143        unsafe { tsf_get_presetcount(self.tsf_ptr) as u16 }
144    }
145}
146
147pub enum OutputMode {
148    StereoInterleaved,
149    StereoUnweaved,
150    Mono
151}
152
153#[cfg(test)]
154mod tests {
155    use super::*;
156
157    #[test]
158    fn load_filename_and_render_c3() {
159        let mut tsf = Tsf::load_filename("test_resources/sinewave.sf2").unwrap();
160
161        assert_eq!(1, tsf.get_preset_count());
162
163        let sample_rate = 48000;
164        let samples = (sample_rate / 10) as usize;  // render 0.1s at a time
165
166        tsf.set_output(OutputMode::Mono, sample_rate, 0f32);
167
168        // Render some silence
169        let silence_samples = tsf.render_float(samples);
170        assert!(silence_samples.into_iter().all(|x| x==0f32), "Didn't get silence");
171
172        // Start a note and render it. We need to initialize the channel with a preset
173        tsf.channel_set_preset_number(0, 0, false);
174        tsf.channel_note_on(0, 48, 1f32);
175
176        let note_on_samples = tsf.render_float(4800);
177        assert!(note_on_samples.into_iter().any(|x| x!=0f32), "Got silence");
178
179        // End a note - expect it to ring for a little while.
180        tsf.channel_note_off(0, 48);
181        let note_off_samples = tsf.render_float(4800);
182        assert!(note_off_samples.into_iter().any(|x| x!=0f32), "Got silence");
183
184        // Skip ahead a long time. There's no way the note rings for 10s right? Well 10s at 48000
185        // samples per second is 480k samples. Let's render that many and discard them.
186        for _ in 0..100 {
187            tsf.render_float(samples);
188        }
189
190        // Now check we are back to silence
191        let silence_after_samples = tsf.render_float(samples);
192        assert!(silence_after_samples.into_iter().all(|x| x==0f32), "Didn't get silence");
193
194        // Note that we do not need to call `close` - letting tsf fall out of scope / fall off the
195        // stack is sufficient.
196    }
197}