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; 50] = [
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", 
350    "OTC", 
351    "OTI",
352];
353
354const COMMANDS_V4: [&'static str; concat_arrays_size!(SEQ_COMMAND_V3, FX_MIXER_COMMAND_V4)] =
355    concat_arrays!(SEQ_COMMAND_V3, FX_MIXER_COMMAND_V4);
356
357const COMMANDS_V6_2: [&'static str; concat_arrays_size!(SEQ_COMMAND_V3, FX_MIXER_COMMAND_V6_2)] =
358    concat_arrays!(SEQ_COMMAND_V3, FX_MIXER_COMMAND_V6_2);
359
360impl FX {
361    pub const V4_SIZE: usize = 2;
362
363    pub(crate) fn from_reader(reader: &mut Reader) -> M8Result<Self> {
364        Ok(Self {
365            command: reader.read(),
366            value: reader.read(),
367        })
368    }
369
370    pub fn write(self, w: &mut Writer) {
371        w.write(self.command);
372        w.write(self.value);
373    }
374
375    pub fn is_empty(self) -> bool {
376        self.command == 0xFF
377    }
378
379    pub fn print(&self, fx: FxCommands, pack: CommandPack, templates: &ReferenceTemplating) -> String {
380        if self.is_empty() {
381            format!("---  ")
382        } else {
383            let c = self.format_command(fx, pack);
384            match templates.try_template(&c, self.value) {
385                Some(templated) => templated,
386                None => format!("{}{:02x}", c, self.value)
387            }
388        }
389    }
390
391    /// Retrieve command names for a given version
392    pub fn fx_command_names(ver: Version) -> FxCommands {
393        if ver.at_least(6, 1) {
394            FxCommands {
395                commands: &COMMANDS_V6_2,
396            }
397        } else if ver.at_least(4, 0) {
398            FxCommands {
399                commands: &COMMANDS_V4,
400            }
401        } else if ver.at_least(3, 0) {
402            FxCommands {
403                commands: &COMMANDS_V3,
404            }
405        } else {
406            FxCommands {
407                commands: &COMMANDS_V2,
408            }
409        }
410    }
411
412    fn format_command(&self, fx: FxCommands, instr: CommandPack) -> String {
413        match fx.try_render(self.command) {
414            Some(s) => String::from(s),
415            None => {
416                if instr.accepts(self.command) {
417                    match instr.try_render(self.command) {
418                        Some(v) => String::from(v),
419                        None => format!("I{:02X}", self.command - 0x80),
420                    }
421                } else {
422                    format!("?{:02x}", self.command)
423                }
424            }
425        }
426    }
427}