Skip to main content

m8_file_parser/instruments/
common.rs

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