m8_files/instruments/
common.rs

1use std::fmt;
2
3use super::modulator::*;
4use crate::reader::*;
5use crate::writer::Writer;
6use crate::Version;
7use arr_macro::arr;
8
9/// Type storing transpose field and eq number
10#[derive(PartialEq, Copy, Clone, Default, Debug)]
11pub struct TranspEq {
12    pub transpose: bool,
13    pub eq: u8,
14}
15
16impl TranspEq {
17    pub fn from(ver: Version, transpose: bool, eq: u8) -> TranspEq {
18        if ver.at_least(4, 1) {
19            Self {
20                transpose,
21                eq: 0x00,
22            }
23        } else {
24            Self { transpose, eq }
25        }
26    }
27
28    pub fn from_version(ver: Version, value: u8) -> Self {
29        if ver.at_least(4, 1) {
30            Self {
31                transpose: (value & 1) != 0,
32                eq: 0x00,
33            }
34        } else {
35            Self {
36                transpose: (value & 1) != 0,
37                eq: value >> 1,
38            }
39        }
40    }
41}
42
43impl From<TranspEq> for u8 {
44    fn from(value: TranspEq) -> Self {
45        (if value.transpose { 1 } else { 0 }) | (value.eq << 1)
46    }
47}
48
49#[rustfmt::skip] // Keep constats with important order vertical for maintenance
50const LIMIT_TYPE : [&str; 8] = [
51    "CLIP",
52    "SIN",
53    "FOLD",
54    "WRAP",
55    "POST",
56    "POSTAD",
57    "POST:W1",
58    "POST:W2"
59];
60
61#[derive(PartialEq, Clone, Copy)]
62pub struct LimitType(u8);
63
64impl fmt::Debug for LimitType {
65    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
66        f.write_str(LIMIT_TYPE[self.0 as usize])
67    }
68}
69
70impl TryFrom<u8> for LimitType {
71    type Error = ParseError;
72
73    fn try_from(value: u8) -> std::result::Result<Self, Self::Error> {
74        if (value as usize) < LIMIT_TYPE.len() {
75            Ok(LimitType(value))
76        } else {
77            Err(ParseError(format!("Invalid fm wave {}", value)))
78        }
79    }
80}
81
82impl LimitType {
83    pub fn id(self) -> u8 {
84        let LimitType(v) = self;
85        v
86    }
87
88    pub fn str(self) -> &'static str {
89        LIMIT_TYPE[self.id() as usize]
90    }
91}
92
93#[derive(PartialEq, Debug, Clone)]
94pub struct SynthParams {
95    pub volume: u8,
96    pub pitch: u8,
97    pub fine_tune: u8,
98
99    pub filter_type: u8,
100    pub filter_cutoff: u8,
101    pub filter_res: u8,
102
103    pub amp: u8,
104    pub limit: LimitType,
105
106    pub mixer_pan: u8,
107    pub mixer_dry: u8,
108    pub mixer_chorus: u8,
109    pub mixer_delay: u8,
110    pub mixer_reverb: u8,
111
112    pub associated_eq: u8,
113
114    pub mods: [Mod; SynthParams::MODULATOR_COUNT],
115}
116
117#[rustfmt::skip] // Keep constats with important order vertical for maintenance
118pub const COMMON_FILTER_TYPES : [&'static str; 8] = [
119    "OFF",
120    "LOWPASS",
121    "HIGHPAS",
122    "BANDPAS",
123    "BANDSTP",
124    "LP > HP",
125    "ZDF LP",
126    "ZDF HP",
127];
128
129impl SynthParams {
130    pub const MODULATOR_COUNT: usize = 4;
131
132    pub fn set_eq(&mut self, eq: u8) {
133        self.associated_eq = eq
134    }
135
136    pub fn mod_only2(_reader: &mut Reader) -> M8Result<Self> {
137        Ok(Self {
138            volume: 0,
139            pitch: 0,
140            fine_tune: 0,
141
142            filter_type: 0,
143            filter_cutoff: 0,
144            filter_res: 0,
145
146            amp: 0,
147            limit: LimitType::try_from(0)?,
148
149            mixer_pan: 0,
150            mixer_dry: 0,
151            mixer_chorus: 0,
152            mixer_delay: 0,
153            mixer_reverb: 0,
154
155            associated_eq: 0xFF,
156            mods: arr![AHDEnv::default().to_mod(); 4],
157        })
158    }
159
160    pub fn mod_only3(reader: &mut Reader, mod_offset: usize) -> M8Result<Self> {
161        reader.set_pos(reader.pos() + mod_offset);
162
163        let mods = arr![Mod::from_reader(reader)?; 4];
164
165        Ok(Self {
166            volume: 0,
167            pitch: 0,
168            fine_tune: 0,
169
170            filter_type: 0,
171            filter_cutoff: 0,
172            filter_res: 0,
173
174            amp: 0,
175            limit: LimitType::try_from(0)?,
176
177            mixer_pan: 0,
178            mixer_dry: 0,
179            mixer_chorus: 0,
180            mixer_delay: 0,
181            mixer_reverb: 0,
182            associated_eq: 0xFF,
183
184            mods,
185        })
186    }
187
188    pub fn from_reader2(
189        reader: &mut Reader,
190        volume: u8,
191        pitch: u8,
192        fine_tune: u8,
193    ) -> M8Result<Self> {
194        Ok(Self {
195            volume,
196            pitch,
197            fine_tune,
198
199            filter_type: reader.read(),
200            filter_cutoff: reader.read(),
201            filter_res: reader.read(),
202
203            amp: reader.read(),
204            limit: LimitType::try_from(reader.read())?,
205
206            mixer_pan: reader.read(),
207            mixer_dry: reader.read(),
208            mixer_chorus: reader.read(),
209            mixer_delay: reader.read(),
210            mixer_reverb: reader.read(),
211
212            associated_eq: 0xFF,
213
214            mods: [
215                AHDEnv::from_reader2(reader)?.to_mod(),
216                AHDEnv::from_reader2(reader)?.to_mod(),
217                LFO::from_reader2(reader)?.to_mod(),
218                LFO::from_reader2(reader)?.to_mod(),
219            ],
220        })
221    }
222
223    pub fn write(&self, ver: Version, w: &mut Writer, mod_offset: usize) {
224        w.write(self.filter_type);
225        w.write(self.filter_cutoff);
226        w.write(self.filter_res);
227
228        w.write(self.amp);
229        w.write(self.limit.0);
230
231        w.write(self.mixer_pan);
232        w.write(self.mixer_dry);
233        w.write(self.mixer_chorus);
234        w.write(self.mixer_delay);
235        w.write(self.mixer_reverb);
236
237        let writer_pos = w.pos();
238        if ver.at_least(4, 1) {
239            w.seek(writer_pos + mod_offset - 1);
240            w.write(self.associated_eq);
241        }
242
243        w.seek(writer_pos + mod_offset);
244        for m in &self.mods {
245            m.write(w);
246        }
247    }
248
249    pub fn write_modes(&self, w: &mut Writer, mod_offset: usize) {
250        w.seek(w.pos() + mod_offset);
251        for m in &self.mods {
252            m.write(w);
253        }
254    }
255
256    pub fn from_reader3(
257        version: Version,
258        reader: &mut Reader,
259        volume: u8,
260        pitch: u8,
261        fine_tune: u8,
262        eq: u8,
263        mod_offset: usize,
264    ) -> M8Result<Self> {
265        let filter_type = reader.read();
266        let filter_cutoff = reader.read();
267        let filter_res = reader.read();
268
269        let amp = reader.read();
270        let limit = reader.read();
271
272        let mixer_pan = reader.read();
273        let mixer_dry = reader.read();
274        let mixer_chorus = reader.read();
275        let mixer_delay = reader.read();
276        let mixer_reverb = reader.read();
277
278        let reader_pos = reader.pos();
279        let associated_eq = if version.at_least(4, 1) {
280            reader.set_pos(reader_pos + mod_offset - 1);
281            reader.read()
282        } else if version.at_least(4, 0) {
283            eq
284        } else {
285            0xFF
286        };
287
288        reader.set_pos(reader_pos + mod_offset);
289
290        let mods = arr![Mod::from_reader(reader)?; 4];
291
292        Ok(Self {
293            volume,
294            pitch,
295            fine_tune,
296
297            filter_type,
298            filter_cutoff,
299            filter_res,
300
301            amp,
302            limit: LimitType::try_from(limit)?,
303
304            mixer_pan,
305            mixer_dry,
306            mixer_chorus,
307            mixer_delay,
308            mixer_reverb,
309
310            associated_eq,
311
312            mods,
313        })
314    }
315}