m8_file_parser/
fx.rs

1use crate::reader::*;
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
298const COMMANDS_V4: [&'static str; concat_arrays_size!(SEQ_COMMAND_V3, FX_MIXER_COMMAND_V4)] =
299    concat_arrays!(SEQ_COMMAND_V3, FX_MIXER_COMMAND_V4);
300
301impl FX {
302    pub const V4_SIZE: usize = 2;
303
304    pub(crate) fn from_reader(reader: &mut Reader) -> M8Result<Self> {
305        Ok(Self {
306            command: reader.read(),
307            value: reader.read(),
308        })
309    }
310
311    pub fn write(self, w: &mut Writer) {
312        w.write(self.command);
313        w.write(self.value);
314    }
315
316    pub fn is_empty(self) -> bool {
317        self.command == 0xFF
318    }
319
320    pub fn print(&self, fx: FxCommands, pack: CommandPack) -> String {
321        if self.is_empty() {
322            format!("---  ")
323        } else {
324            let c = self.format_command(fx, pack);
325            format!("{}{:02x}", c, self.value)
326        }
327    }
328
329    /// Retrieve command names for a given version
330    pub fn fx_command_names(ver: Version) -> FxCommands {
331        if ver.at_least(4, 0) {
332            FxCommands {
333                commands: &COMMANDS_V4,
334            }
335        } else if ver.at_least(3, 0) {
336            FxCommands {
337                commands: &COMMANDS_V3,
338            }
339        } else {
340            FxCommands {
341                commands: &COMMANDS_V2,
342            }
343        }
344    }
345
346    fn format_command(&self, fx: FxCommands, instr: CommandPack) -> String {
347        match fx.try_render(self.command) {
348            Some(s) => String::from(s),
349            None => {
350                if instr.accepts(self.command) {
351                    match instr.try_render(self.command) {
352                        Some(v) => String::from(v),
353                        None => format!("I{:02X}", self.command - 0x80),
354                    }
355                } else {
356                    format!("?{:02x}", self.command)
357                }
358            }
359        }
360    }
361}