soundfont/
lib.rs

1pub mod raw;
2
3#[doc(hidden)]
4#[deprecated = "use `raw` instead"]
5pub use raw as data;
6
7pub mod error;
8pub use error::Error;
9
10mod riff;
11
12use raw::{
13    Bag, Generator, GeneratorAmountRange, GeneratorType, Info, InstrumentHeader, Modulator,
14    PresetHeader, RawSoundFontData, SampleData, SampleHeader,
15};
16
17use std::io::{Read, Seek};
18
19#[derive(Debug)]
20pub struct Preset {
21    pub header: PresetHeader,
22    pub zones: Vec<Zone>,
23}
24
25#[derive(Debug)]
26pub struct Instrument {
27    pub header: InstrumentHeader,
28    pub zones: Vec<Zone>,
29}
30
31#[derive(Debug)]
32pub struct SoundFont2 {
33    pub info: Info,
34    pub presets: Vec<Preset>,
35    pub instruments: Vec<Instrument>,
36    pub sample_headers: Vec<SampleHeader>,
37    pub sample_data: SampleData,
38}
39
40impl SoundFont2 {
41    pub fn load<F: Read + Seek>(file: &mut F) -> Result<Self, Error> {
42        RawSoundFontData::load(file).map(Self::from_raw)
43    }
44
45    pub fn from_raw(data: RawSoundFontData) -> Self {
46        fn get_zones(
47            zones: &[Bag],
48            modulators: &[Modulator],
49            generators: &[Generator],
50            start: usize,
51            end: usize,
52        ) -> Vec<Zone> {
53            let mut zone_items = Vec::new();
54            for j in start..end {
55                let curr = zones.get(j).unwrap();
56                let next = zones.get(j + 1);
57
58                let mod_list = {
59                    let start = curr.modulator_id as usize;
60                    let end = if let Some(next) = next {
61                        next.modulator_id as usize
62                    } else {
63                        zones.len()
64                    };
65
66                    let mut list = Vec::new();
67
68                    for i in start..end {
69                        let item = modulators.get(i);
70                        if let Some(item) = item {
71                            list.push(item.to_owned());
72                        }
73                    }
74                    list
75                };
76
77                let gen_list = {
78                    let start = curr.generator_id as usize;
79                    let end = if let Some(next) = next {
80                        next.generator_id as usize
81                    } else {
82                        zones.len()
83                    };
84
85                    let mut list = Vec::new();
86
87                    for i in start..end {
88                        let item = generators.get(i);
89                        if let Some(item) = item {
90                            list.push(item.to_owned());
91                        }
92                    }
93                    list
94                };
95
96                zone_items.push(Zone { mod_list, gen_list });
97            }
98            zone_items
99        }
100
101        let instruments = {
102            let headers = &data.hydra.instrument_headers;
103            let zones = &data.hydra.instrument_bags;
104            let modulators = &data.hydra.instrument_modulators;
105            let generators = &data.hydra.instrument_generators;
106
107            let iter = headers.iter();
108            let mut iter_peek = headers.iter();
109            // `iter_peek` has to be one item ahead of `iter`
110            iter_peek.next();
111
112            let mut list = Vec::new();
113
114            for header in iter {
115                let curr = header;
116                let next = iter_peek.next();
117
118                let start = curr.bag_id as usize;
119
120                let end = if let Some(next) = next {
121                    next.bag_id as usize
122                } else {
123                    zones.len()
124                };
125
126                let zone_items = get_zones(zones, modulators, generators, start, end);
127
128                // Ignore Terminator
129                if header.name != "EOS" {
130                    list.push(Instrument {
131                        header: header.clone(),
132                        zones: zone_items,
133                    })
134                }
135            }
136            list
137        };
138
139        let presets = {
140            let headers = &data.hydra.preset_headers;
141            let zones = &data.hydra.preset_bags;
142            let modulators = &data.hydra.preset_modulators;
143            let generators = &data.hydra.preset_generators;
144
145            let iter = headers.iter();
146            let mut iter_peek = headers.iter();
147            // `iter_peek` has to be one item ahead of `iter`
148            iter_peek.next();
149
150            let mut list = Vec::new();
151            for header in iter {
152                let curr = header;
153                let next = iter_peek.next();
154
155                let start = curr.bag_id as usize;
156
157                let end = if let Some(next) = next {
158                    next.bag_id as usize
159                } else {
160                    zones.len()
161                };
162
163                let zone_items = get_zones(zones, modulators, generators, start, end);
164
165                // Ignore Terminator
166                if header.name != "EOP" {
167                    list.push(Preset {
168                        header: header.clone(),
169                        zones: zone_items,
170                    })
171                }
172            }
173
174            list
175        };
176
177        Self {
178            info: data.info,
179            presets,
180            instruments,
181            sample_headers: data
182                .hydra
183                .sample_headers
184                .into_iter()
185                // Ignore Terminator
186                .filter(|h| h.name != "EOS")
187                .collect(),
188            sample_data: data.sample_data,
189        }
190    }
191
192    pub fn sort_presets(mut self) -> Self {
193        self.presets.sort_by(|a, b| {
194            let aval = (a.header.bank as i32) << 16 | a.header.preset as i32;
195            let bbal = (b.header.bank as i32) << 16 | b.header.preset as i32;
196            let cmp = aval - bbal;
197
198            cmp.cmp(&0)
199        });
200        self
201    }
202}
203
204#[derive(Debug, Clone)]
205pub struct Zone {
206    pub mod_list: Vec<Modulator>,
207    pub gen_list: Vec<Generator>,
208}
209
210impl Zone {
211    pub fn key_range(&self) -> Option<&i16> {
212        self.gen_list
213            .iter()
214            .find(|g| g.ty == GeneratorType::KeyRange)
215            .map(|g| g.amount.as_i16().unwrap())
216    }
217    pub fn vel_range(&self) -> Option<&GeneratorAmountRange> {
218        self.gen_list
219            .iter()
220            .find(|g| g.ty == GeneratorType::VelRange)
221            .map(|g| g.amount.as_range().unwrap())
222    }
223    pub fn instrument(&self) -> Option<&u16> {
224        self.gen_list
225            .iter()
226            .find(|g| g.ty == GeneratorType::Instrument)
227            .map(|g| g.amount.as_u16().unwrap())
228    }
229    pub fn sample(&self) -> Option<&u16> {
230        self.gen_list
231            .iter()
232            .find(|g| g.ty == GeneratorType::SampleID)
233            .map(|g| g.amount.as_u16().unwrap())
234    }
235}
236
237/// In SoundFontâ„¢ world where specification might as well be a suggestion
238/// we have to support people putting random values in random places.
239///
240/// In places where spec valolations commonly occcu we use [`SfEnum`].
241/// When value is spec compliant it will be [`SfEnum::Value`],
242/// otherwise the value will be stored as a raw int in [`SfEnum::Unknown`].
243#[derive(Debug, PartialEq, Eq, Copy, Clone)]
244pub enum SfEnum<T, RAW> {
245    Value(T),
246    Unknown(RAW),
247}
248
249impl<T: PartialEq, RAW> PartialEq<T> for SfEnum<T, RAW> {
250    #[inline]
251    fn eq(&self, other: &T) -> bool {
252        if let Self::Value(ty) = self {
253            ty == other
254        } else {
255            false
256        }
257    }
258}