fluidlite/synth/
font.rs

1use crate::{ffi, option_from_ptr, Chan, Error, FontId, FontRef, PresetRef, Result, Status, Synth};
2use std::{ffi::CString, marker::PhantomData, path::Path};
3
4/**
5SoundFont management
6 */
7impl Synth {
8    /**
9    Loads a SoundFont file and creates a new SoundFont. The newly
10    loaded SoundFont will be put on top of the SoundFont
11    stack. Presets are searched starting from the SoundFont on the
12    top of the stack, working the way down the stack until a preset
13    is found.
14     */
15    pub fn sfload<P: AsRef<Path>>(&self, filename: P, reset_presets: bool) -> Result<FontId> {
16        let filename = filename.as_ref().to_str().ok_or(Error::Path)?;
17        let filename = CString::new(filename).map_err(|_| Error::Path)?;
18
19        self.neg_err(unsafe {
20            ffi::fluid_synth_sfload(self.handle, filename.as_ptr(), reset_presets as _)
21        })
22        .map(|id| id as _)
23    }
24
25    /**
26    Reload a SoundFont. The reloaded SoundFont retains its ID and
27    index on the stack.
28     */
29    pub fn sfreload(&self, id: FontId) -> Result<FontId> {
30        self.neg_err(unsafe { ffi::fluid_synth_sfreload(self.handle, id as _) })
31            .map(|id| id as _)
32    }
33
34    /**
35    Removes a SoundFont from the stack and deallocates it.
36     */
37    pub fn sfunload(&self, id: FontId, reset_presets: bool) -> Status {
38        self.zero_ok(unsafe { ffi::fluid_synth_sfunload(self.handle, id, reset_presets as _) })
39    }
40
41    /**
42    Count the number of loaded SoundFonts.
43     */
44    pub fn sfcount(&self) -> u32 {
45        unsafe { ffi::fluid_synth_sfcount(self.handle) as _ }
46    }
47
48    /**
49    Get a SoundFont. The SoundFont is specified by its index on the
50    stack. The top of the stack has index zero.
51
52    - `num` The number of the SoundFont (0 <= num < sfcount)
53     */
54    pub fn get_sfont(&self, num: u32) -> Option<FontRef<'_>> {
55        option_from_ptr(unsafe { ffi::fluid_synth_get_sfont(self.handle, num) })
56            .map(FontRef::from_ptr)
57    }
58
59    /**
60    Get an iterator over loaded SoundFonts.
61     */
62    pub fn sfont_iter(&self) -> FontIter<'_> {
63        FontIter::from_ptr(self.handle)
64    }
65
66    /**
67    Get a SoundFont. The SoundFont is specified by its ID.
68     */
69    pub fn get_sfont_by_id(&self, id: FontId) -> Option<FontRef<'_>> {
70        option_from_ptr(unsafe { ffi::fluid_synth_get_sfont_by_id(self.handle, id) })
71            .map(FontRef::from_ptr)
72    }
73
74    /**
75    Remove a SoundFont that was previously added using
76    fluid_synth_add_sfont(). The synthesizer does not delete the
77    SoundFont; this is responsability of the caller.
78     */
79    pub fn remove_sfont(&self, sfont: FontRef<'_>) {
80        unsafe {
81            ffi::fluid_synth_remove_sfont(self.handle, sfont.as_ptr());
82        }
83    }
84
85    /*
86    /**
87    Add a SoundFont. The SoundFont will be put on top of
88    the SoundFont stack.
89     */
90    pub fn add_sfont(&self, sfont: &SFont) -> Result<FontId> {
91        self.neg_err(unsafe { ffi::fluid_synth_add_sfont(self.handle, sfont.as_ptr()) })
92    }
93     */
94
95    /**
96    Get the preset of a channel
97     */
98    pub fn get_channel_preset(&self, chan: Chan) -> Option<PresetRef<'_>> {
99        option_from_ptr(unsafe { ffi::fluid_synth_get_channel_preset(self.handle, chan as _) })
100            .map(PresetRef::from_ptr)
101    }
102
103    /**
104    Offset the bank numbers in a SoundFont.
105    Returns -1 if an error occured (out of memory or negative offset)
106     */
107    pub fn set_bank_offset(&self, sfont_id: FontId, offset: u32) -> Status {
108        self.zero_ok(unsafe {
109            ffi::fluid_synth_set_bank_offset(self.handle, sfont_id as _, offset as _)
110        })
111    }
112
113    /**
114    Get the offset of the bank numbers in a SoundFont.
115     */
116    pub fn get_bank_offset(&self, sfont_id: FontId) -> Result<u32> {
117        self.neg_err(unsafe { ffi::fluid_synth_get_bank_offset(self.handle, sfont_id as _) })
118            .map(|val| val as _)
119    }
120}
121
122#[cfg(test)]
123mod test {
124    use crate::{IsFont, IsPreset, Settings, Synth};
125
126    #[test]
127    fn font_and_preset() {
128        let synth = Synth::new(Settings::new().unwrap()).unwrap();
129
130        assert_eq!(synth.sfcount(), 0);
131
132        synth.sfload("sf_/Boomwhacker.sf2", true).unwrap();
133
134        assert_eq!(synth.sfcount(), 1);
135
136        let font = synth.get_sfont(0).unwrap();
137
138        assert_eq!(font.get_id(), 1);
139        assert_eq!(font.get_name().unwrap(), "sf_/Boomwhacker.sf2");
140
141        let preset = font.get_preset(0, 0).unwrap();
142
143        assert_eq!(preset.get_name().unwrap(), "Boomwhacker");
144        assert_eq!(preset.get_banknum().unwrap(), 0);
145        assert_eq!(preset.get_num().unwrap(), 0);
146    }
147}
148
149/**
150The iterator over loaded SoundFonts.
151 */
152pub struct FontIter<'a> {
153    handle: *mut ffi::fluid_synth_t,
154    phantom: PhantomData<&'a ()>,
155    font_no: u32,
156}
157
158impl<'a> FontIter<'a> {
159    fn from_ptr(handle: *mut ffi::fluid_synth_t) -> Self {
160        Self {
161            handle,
162            phantom: PhantomData,
163            font_no: 0,
164        }
165    }
166}
167
168impl<'a> Iterator for FontIter<'a> {
169    type Item = FontRef<'a>;
170
171    fn next(&mut self) -> Option<Self::Item> {
172        let font =
173            option_from_ptr(unsafe { ffi::fluid_synth_get_sfont(self.handle, self.font_no) })
174                .map(FontRef::from_ptr);
175        if font.is_some() {
176            self.font_no += 1;
177        }
178        font
179    }
180}