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#[rustfmt::skip] const 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] const 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#[rustfmt::skip] const 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] const 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#[rustfmt::skip] const 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", "DJT", "EQM", "EQI", "INS", "RTO", "ARC", "GGR", "NXT", ];
297
298#[rustfmt::skip] const 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", "DJT", "EQM", "EQI", "INS", "RTO", "ARC", "GGR", "NXT", "XRH", "XMT", "OTT", "OTC", "OTI", "MTT", ];
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 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}