m8_file_parser/
fx.rs

1use crate::{reader::*, ReferenceTemplating};
2use crate::remapper::{EqMapping, InstrumentMapping, TableMapping};
3use crate::version::*;
4use crate::writer::Writer;
5use crate::CommandPack;
6use array_concat::*;
7
8#[derive(Copy, Clone)]
9pub struct FxCommands {
10    pub commands: &'static [&'static str],
11}
12
13impl FxCommands {
14    pub fn find_indices(&self, to_find: &[&str]) -> Vec<u8> {
15        let mut out = vec![];
16
17        for (i, cmd) in self.commands.iter().enumerate() {
18            if to_find.contains(cmd) {
19                out.push(i as u8)
20            }
21        }
22
23        out
24    }
25
26    pub fn try_render(self, cmd: u8) -> Option<&'static str> {
27        let cmd = cmd as usize;
28
29        if cmd < self.commands.len() {
30            Some(self.commands[cmd])
31        } else {
32            None
33        }
34    }
35}
36
37#[derive(PartialEq, Debug, Clone, Copy)]
38pub struct FX {
39    pub command: u8,
40    pub value: u8,
41}
42
43impl FX {
44    pub fn map_instr(
45        self,
46        instrument_mapping: &InstrumentMapping,
47        table_mapping: &TableMapping,
48        eq_mapping: &EqMapping,
49    ) -> Self {
50        let uval = self.value as usize;
51
52        if instrument_mapping
53            .instrument_tracking_commands
54            .contains(&self.command)
55            && uval < instrument_mapping.mapping.len()
56        {
57            Self {
58                command: self.command,
59                value: instrument_mapping.mapping[uval],
60            }
61        } else if table_mapping
62            .table_tracking_commands
63            .contains(&self.command)
64            && uval < table_mapping.mapping.len()
65        {
66            Self {
67                command: self.command,
68                value: table_mapping.mapping[uval],
69            }
70        } else if eq_mapping.eq_tracking_commands.contains(&self.command)
71            && uval < eq_mapping.mapping.len()
72        {
73            Self {
74                command: self.command,
75                value: eq_mapping.mapping[uval],
76            }
77        } else {
78            self
79        }
80    }
81}
82
83impl Default for FX {
84    fn default() -> Self {
85        Self {
86            command: 0xFF,
87            value: 0,
88        }
89    }
90}
91
92//////////////////////////////////////////
93// MARK: V2 commands
94//////////////////////////////////////////
95
96#[rustfmt::skip] // Keep constants with important order vertical for maintenance
97const SEQ_COMMAND_V2 : [&'static str; 23] = [
98    "ARP",
99    "CHA",
100    "DEL",
101    "GRV",
102    "HOP",
103    "KIL",
104    "RAN",
105    "RET",
106    "REP",
107    "NTH",
108    "PSL",
109    "PSN",
110    "PVB",
111    "PVX",
112    "SCA",
113    "SCG",
114    "SED",
115    "SNG",
116    "TBL",
117    "THO",
118    "TIC",
119    "TPO",
120    "TSP",
121];
122
123#[rustfmt::skip] // Keep constants with important order vertical for maintenance
124const FX_MIXER_COMMAND_V2 : [&'static str; 36] = [
125    "VMV",
126    "XCM",
127    "XCF",
128    "XCW",
129    "XCR",
130    "XDT",
131    "XDF",
132    "XDW",
133    "XDR",
134    "XRS",
135    "XRD",
136    "XRM",
137    "XRF",
138    "XRW",
139    "XRZ",
140    "VCH",
141    "VCD",
142    "VRE",
143    "VT1",
144    "VT2",
145    "VT3",
146    "VT4",
147    "VT5",
148    "VT6",
149    "VT7",
150    "VT8",
151    "DJF",
152    "IVO",
153    "ICH",
154    "IDE",
155    "IRE",
156    "IV2",
157    "IC2",
158    "ID2",
159    "IR2",
160    "USB",
161];
162
163const COMMANDS_V2: [&'static str; concat_arrays_size!(SEQ_COMMAND_V2, FX_MIXER_COMMAND_V2)] =
164    concat_arrays!(SEQ_COMMAND_V2, FX_MIXER_COMMAND_V2);
165
166//////////////////////////////////////////
167// MARK: V3 commands
168//////////////////////////////////////////
169
170#[rustfmt::skip] // Keep constants with important order vertical for maintenance
171const SEQ_COMMAND_V3 : [&'static str; 27] = [
172    "ARP",
173    "CHA",
174    "DEL",
175    "GRV",
176    "HOP",
177    "KIL",
178    "RND",
179    "RNL",
180    "RET",
181    "REP",
182    "RMX",
183    "NTH",
184    "PSL",
185    "PBN",
186    "PVB",
187    "PVX",
188    "SCA",
189    "SCG",
190    "SED",
191    "SNG",
192    "TBL",
193    "THO",
194    "TIC",
195    "TBX",
196    "TPO",
197    "TSP",
198    "OFF"
199];
200
201#[rustfmt::skip] // Keep constants with important order vertical for maintenance
202const FX_MIXER_COMMAND_V3 : [&'static str; 36] = [
203    "VMV",
204    "XCM",
205    "XCF",
206    "XCW",
207    "XCR",
208    "XDT",
209    "XDF",
210    "XDW",
211    "XDR",
212    "XRS",
213    "XRD",
214    "XRM",
215    "XRF",
216    "XRW",
217    "XRZ",
218    "VCH",
219    "VCD",
220    "VRE",
221    "VT1",
222    "VT2",
223    "VT3",
224    "VT4",
225    "VT5",
226    "VT6",
227    "VT7",
228    "VT8",
229    "DJF",
230    "IVO",
231    "ICH",
232    "IDE",
233    "IRE",
234    "IV2",
235    "IC2",
236    "ID2",
237    "IR2",
238    "USB",
239];
240
241const COMMANDS_V3: [&'static str; concat_arrays_size!(SEQ_COMMAND_V3, FX_MIXER_COMMAND_V3)] =
242    concat_arrays!(SEQ_COMMAND_V3, FX_MIXER_COMMAND_V3);
243
244//////////////////////////////////////////
245// MARK: V4 commands
246//////////////////////////////////////////
247
248#[rustfmt::skip] // Keep constants with important order vertical for maintenance
249const FX_MIXER_COMMAND_V4 : [&'static str; 45] = [
250    "VMV",
251    "XCM",
252    "XCF",
253    "XCW",
254    "XCR",
255    "XDT",
256    "XDF",
257    "XDW",
258    "XDR",
259    "XRS",
260    "XRD",
261    "XRM",
262    "XRF",
263    "XRW",
264    "XRZ",
265    "VCH",
266    "VDE",
267    "VRE",
268    "VT1",
269    "VT2",
270    "VT3",
271    "VT4",
272    "VT5",
273    "VT6",
274    "VT7",
275    "VT8",
276    "DJC",
277    "VIN",
278    "ICH",
279    "IDE",
280    "IRE",
281    "VI2",
282    "IC2",
283    "ID2",
284    "IR2",
285    "USB",
286
287    "DJR", // 0x3F
288    "DJT", // 0x40
289    "EQM", // 0x41
290    "EQI", // 0x42
291    "INS", // 0x43
292    "RTO", // 0x44
293    "ARC", // 0x45
294    "GGR", // 0x46
295    "NXT", // 0x47
296];
297
298#[rustfmt::skip] // Keep constants with important order vertical for maintenance
299const FX_MIXER_COMMAND_V6_2 : [&'static str; 51] = [
300    "VMV",
301    "XMM",
302    "XMF",
303    "XMW",
304    "XMR",
305    "XDT",
306    "XDF",
307    "XDW",
308    "XDR",
309    "XRS",
310    "XRD",
311    "XRM",
312    "XRF",
313    "XRW",
314    "XRZ",
315    "VMX",
316    "VDE",
317    "VRE",
318    "VT1",
319    "VT2",
320    "VT3",
321    "VT4",
322    "VT5",
323    "VT6",
324    "VT7",
325    "VT8",
326    "DJC",
327    "VIN",
328    "IMX",
329    "IDE",
330    "IRE",
331    "VI2",
332    "IM2",
333    "ID2",
334    "IR2",
335    "USB",
336
337    "DJR", // 0x3F
338    "DJT", // 0x40
339    "EQM", // 0x41
340    "EQI", // 0x42
341    "INS", // 0x43
342    "RTO", // 0x44
343    "ARC", // 0x45
344    "GGR", // 0x46
345    "NXT", // 0x47
346
347    "XRH", // 0x48
348    "XMT", // 0x49
349    "OTT", // 0x4A
350    "OTC", // 0x4B
351    "OTI", // 0x4C
352    "MTT", // 0x4D
353];
354
355const COMMANDS_V4: [&'static str; concat_arrays_size!(SEQ_COMMAND_V3, FX_MIXER_COMMAND_V4)] =
356    concat_arrays!(SEQ_COMMAND_V3, FX_MIXER_COMMAND_V4);
357
358const COMMANDS_V6_2: [&'static str; concat_arrays_size!(SEQ_COMMAND_V3, FX_MIXER_COMMAND_V6_2)] =
359    concat_arrays!(SEQ_COMMAND_V3, FX_MIXER_COMMAND_V6_2);
360
361impl FX {
362    pub const V4_SIZE: usize = 2;
363
364    pub(crate) fn from_reader(reader: &mut Reader) -> M8Result<Self> {
365        Ok(Self {
366            command: reader.read(),
367            value: reader.read(),
368        })
369    }
370
371    pub fn write(self, w: &mut Writer) {
372        w.write(self.command);
373        w.write(self.value);
374    }
375
376    pub fn is_empty(self) -> bool {
377        self.command == 0xFF
378    }
379
380    pub fn print(&self, fx: FxCommands, pack: CommandPack, templates: &ReferenceTemplating) -> String {
381        if self.is_empty() {
382            format!("---  ")
383        } else {
384            let c = self.format_command(fx, pack);
385            match templates.try_template(&c, self.value) {
386                Some(templated) => templated,
387                None => format!("{}{:02x}", c, self.value)
388            }
389        }
390    }
391
392    /// Retrieve command names for a given version
393    pub fn fx_command_names(ver: Version) -> FxCommands {
394        if ver.after(&FIRMWARE_6_2_SONG_VERSION) {
395            FxCommands {
396                commands: &COMMANDS_V6_2,
397            }
398        } else if ver.after(&FIRMWARE_4_0_SONG_VERSION) {
399            FxCommands {
400                commands: &COMMANDS_V4,
401            }
402        } else if ver.after(&FIRMWARE_3_0_SONG_VERSION) {
403            FxCommands {
404                commands: &COMMANDS_V3,
405            }
406        } else {
407            FxCommands {
408                commands: &COMMANDS_V2,
409            }
410        }
411    }
412
413    fn format_command(&self, fx: FxCommands, instr: CommandPack) -> String {
414        match fx.try_render(self.command) {
415            Some(s) => String::from(s),
416            None => {
417                if instr.accepts(self.command) {
418                    match instr.try_render(self.command) {
419                        Some(v) => String::from(v),
420                        None => format!("I{:02X}", self.command - 0x80),
421                    }
422                } else {
423                    format!("?{:02x}", self.command)
424                }
425            }
426        }
427    }
428}