tsf_sys/
lib.rs

1#![allow(non_upper_case_globals)]
2#![allow(non_camel_case_types)]
3#![allow(non_snake_case)]
4
5include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
6
7#[cfg(test)]
8mod tests {
9    use std::ffi::{c_void, CString};
10    use std::os::raw::c_int;
11
12    use super::*;
13
14    const MINIMAL_SOUND_FONT: &'static [u8] = &[
15        b'R',b'I',b'F',b'F',220,1,0,0,b's',b'f',b'b',b'k',
16        b'L',b'I',b'S',b'T',88,1,0,0,b'p',b'd',b't',b'a',
17        b'p',b'h',b'd',b'r',76,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,0,255,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,
18        b'p',b'b',b'a',b'g',8,0,0,0,0,0,0,0,1,0,0,0,b'p',b'm',b'o',b'd',10,0,0,0,0,0,0,0,0,0,0,0,0,0,b'p',b'g',b'e',b'n',8,0,0,0,41,0,0,0,0,0,0,0,
19        b'i',b'n',b's',b't',44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,
20        b'i',b'b',b'a',b'g',8,0,0,0,0,0,0,0,2,0,0,0,b'i',b'm',b'o',b'd',10,0,0,0,0,0,0,0,0,0,0,0,0,0,
21        b'i',b'g',b'e',b'n',12,0,0,0,54,0,1,0,53,0,0,0,0,0,0,0,
22        b's',b'h',b'd',b'r',92,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,0,0,0,0,0,0,0,49,0,0,0,34,86,0,0,60,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
23        b'L',b'I',b'S',b'T',112,0,0,0,b's',b'd',b't',b'a',b's',b'm',b'p',b'l',100,0,0,0,86,0,119,3,31,7,147,10,43,14,169,17,58,21,189,24,73,28,204,31,73,35,249,38,46,42,71,46,250,48,150,53,242,55,126,60,151,63,108,66,126,72,207,
24        70,86,83,100,72,74,100,163,39,241,163,59,175,59,179,9,179,134,187,6,186,2,194,5,194,15,200,6,202,96,206,159,209,35,213,213,216,45,220,221,223,76,227,221,230,91,234,242,237,105,241,8,245,118,248,32,252
25    ];
26
27    #[test]
28    fn load_memory() {
29        unsafe {
30            let sf_ptr = MINIMAL_SOUND_FONT.as_ptr();
31            let sf_len = MINIMAL_SOUND_FONT.len();
32            let tsf = tsf_load_memory(sf_ptr as *const c_void, sf_len as c_int);
33
34            let preset_count = tsf_get_presetcount(tsf);
35            assert_eq!(1, preset_count);
36
37            tsf_close(tsf);
38        }
39    }
40
41    #[test]
42    fn load_filename_and_render_c3() {
43        unsafe {
44            let filename = CString::new("test_resources/sinewave.sf2").unwrap();
45            let tsf = tsf_load_filename(filename.as_ptr());
46
47            let preset_count = tsf_get_presetcount(tsf);
48            assert_eq!(1, preset_count);
49
50            let sample_rate = 44100;
51            let samples = sample_rate / 10;  // Sample 0.1s below
52            let output_mode = TSFOutputMode_TSF_MONO;
53            let channels = 1;  // Mono has one channel
54            let note: c_int = 48;  // MIDI note 48 is C3, i.e. one octave below middle C
55
56            tsf_set_output(tsf, output_mode, sample_rate as c_int, 0f32);
57
58            {
59                let mut dst: Vec<f32> = Vec::with_capacity(samples * channels);
60                let dst_ptr: *mut f32 = dst.as_mut_ptr();
61                tsf_render_float(tsf, dst_ptr, samples as c_int, 0);
62                dst.set_len(samples * channels);
63                // Should get silence
64                assert!(dst.iter().all(|x| *x == 0f32), "Didn't get silence");
65            }
66
67            tsf_note_on(tsf, 0 as c_int, note, 1.0f32);
68
69            {
70                let mut dst: Vec<f32> = Vec::with_capacity(samples * channels);
71                let dst_ptr: *mut f32 = dst.as_mut_ptr();
72                tsf_render_float(tsf, dst_ptr, samples as c_int, 0);
73                dst.set_len(samples * channels);
74                // Shouldn't get silence.
75                assert!(dst.iter().any(|x| *x != 0f32), "Got silence");
76
77                // Just in case you wanted to analyze the floats yourself to double check that we
78                // get a MIDI note 48, which is a C3 (i.e. ~130.81Hz), we output a .csv.
79                // Since this data covers 0.1s, you should see around 13 peaks/troughs.
80                let (_, csv) = dst.iter().fold((0, String::new()), |acc, x| {
81                    let (next_note_index, string_builder) = acc;
82                    (next_note_index + 1, string_builder + &format!("{},{}\n", next_note_index as f32 / sample_rate as f32, x))
83                });
84                std::fs::create_dir_all("test_outputs").expect("Failed to create test_outputs directory - is there a non-directory already with that name?");
85                std::fs::write("test_outputs/load_soundfont_from_file_and_render_c3.csv", csv).expect("Failed to write file");
86            }
87
88            tsf_close(tsf);
89        }
90    }
91}