ot_tools_io/
parts.rs

1/*
2SPDX-License-Identifier: GPL-3.0-or-later
3Copyright © 2024 Mike Robeson [dijksterhuis]
4*/
5
6//! Model for part data within a bank.
7use crate::OtToolsIoError;
8use crate::{Defaults, HasHeaderField};
9use ot_tools_io_derive::{ArrayDefaults, BoxedBigArrayDefaults};
10use serde::{Deserialize, Serialize};
11use serde_big_array::{Array, BigArray};
12use std::array::from_fn;
13
14/// Header data for Parts, indicating when a new Part data section starts in binary data files:
15/// `PART`
16const PART_HEADER: [u8; 4] = [0x50, 0x41, 0x52, 0x54];
17
18#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
19pub enum OnOrOff {
20    On = 1,
21    Off = 0,
22}
23
24/// Audio Track MAIN and CUE volume.
25/// Both are 108 by default.
26#[derive(
27    Debug, Serialize, Deserialize, Clone, PartialEq, Copy, ArrayDefaults, BoxedBigArrayDefaults,
28)]
29pub struct AudioTrackVolume {
30    pub main: u8,
31    pub cue: u8,
32}
33
34// TODO: Double check values
35impl Default for AudioTrackVolume {
36    fn default() -> Self {
37        Self {
38            main: 0x6c, // 108
39            cue: 0x6c,  // 108
40        }
41    }
42}
43
44/// Scenes currently selected in the Part.
45/// Whether Scenes are muted or not are controlled at the Project level.
46#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
47pub struct ActiveScenes {
48    pub scene_a: u8,
49    pub scene_b: u8,
50}
51
52impl Default for ActiveScenes {
53    fn default() -> Self {
54        Self {
55            scene_a: 0,
56            scene_b: 8,
57        }
58    }
59}
60
61/// An Audio Track's Setup values for the Static and Flex machine on the track (loop setting/slice setting/len setting etc.).
62#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Copy)]
63pub struct AudioTrackMachineParamsSetupStd {
64    pub xloop: u8,
65    pub slic: u8,
66    pub len: u8,
67    pub rate: u8,
68    pub tstr: u8,
69    pub tsns: u8,
70}
71
72/// An Audio Track's Setup values for the Thru machine on the track. Should not contain any data.
73#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Copy)]
74pub struct AudioTrackMachineParamsSetupThru {
75    pub unused_1: u8,
76    pub unused_2: u8,
77    pub unused_3: u8,
78    pub unused_4: u8,
79    pub unused_5: u8,
80    pub unused_6: u8,
81}
82
83/// An Audio Track's Setup values for the Neighbor machine on the track. Should not contain any data.
84#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Copy)]
85pub struct AudioTrackMachineParamsSetupNeighbor {
86    pub unused_1: u8,
87    pub unused_2: u8,
88    pub unused_3: u8,
89    pub unused_4: u8,
90    pub unused_5: u8,
91    pub unused_6: u8,
92}
93
94/// An Audio Track's Setup values for the Pickup machine on the track.
95#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Copy)]
96pub struct AudioTrackMachineParamsSetupPickup {
97    pub unused_1: u8,
98    pub unused_2: u8,
99    pub unused_3: u8,
100    pub unused_4: u8,
101    pub tstr: u8,
102    pub tsns: u8,
103}
104
105/// Audio Tracks Machine Setup pages.
106/// As before, separate from other Audio Track parameter fields to be persisted,
107/// allowing safer audio triggering.
108#[derive(
109    Debug, Serialize, Deserialize, Clone, PartialEq, Copy, ArrayDefaults, BoxedBigArrayDefaults,
110)]
111pub struct AudioTrackMachinesParamsSetup {
112    pub static_machine: AudioTrackMachineParamsSetupStd,
113    pub flex_machine: AudioTrackMachineParamsSetupStd,
114    pub thru_machine: AudioTrackMachineParamsSetupThru,
115    pub neighbor_machine: AudioTrackMachineParamsSetupNeighbor,
116    pub pickup_machine: AudioTrackMachineParamsSetupPickup,
117}
118
119impl Default for AudioTrackMachinesParamsSetup {
120    fn default() -> Self {
121        Self {
122            static_machine: AudioTrackMachineParamsSetupStd {
123                xloop: 1,
124                slic: 0,
125                len: 0,
126                rate: 0,
127                tstr: 1,
128                tsns: 64,
129            },
130            flex_machine: AudioTrackMachineParamsSetupStd {
131                xloop: 1,
132                slic: 0,
133                len: 0,
134                rate: 0,
135                tstr: 1,
136                tsns: 64,
137            },
138            thru_machine: AudioTrackMachineParamsSetupThru {
139                unused_1: 0,
140                unused_2: 0,
141                unused_3: 0,
142                unused_4: 0,
143                unused_5: 0,
144                unused_6: 0,
145            },
146            neighbor_machine: AudioTrackMachineParamsSetupNeighbor {
147                unused_1: 0,
148                unused_2: 0,
149                unused_3: 0,
150                unused_4: 0,
151                unused_5: 0,
152                unused_6: 0,
153            },
154            pickup_machine: AudioTrackMachineParamsSetupPickup {
155                unused_1: 0,
156                unused_2: 0,
157                unused_3: 0,
158                unused_4: 0,
159                tstr: 1,
160                tsns: 64,
161            },
162        }
163    }
164}
165
166/// Audio Tracks Machine Slot assignments.
167/// Sample Slots assigned for each machine.
168/// Also tracks the recording buffer sample slot assignment.
169#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, BoxedBigArrayDefaults)]
170pub struct AudioTrackMachineSlot {
171    pub static_slot_id: u8,
172    pub flex_slot_id: u8,
173    pub unused_1: u8,
174    pub unused_2: u8,
175    pub recorder_slot_id: u8,
176}
177
178impl Default for AudioTrackMachineSlot {
179    fn default() -> Self {
180        Self {
181            static_slot_id: 0,
182            flex_slot_id: 0,
183            unused_1: 0,
184            unused_2: 0,
185            recorder_slot_id: 128,
186        }
187    }
188}
189
190// need to implement manually to handle id fields
191impl<const N: usize> Defaults<[Self; N]> for AudioTrackMachineSlot {
192    fn defaults() -> [Self; N]
193    where
194        Self: Default,
195    {
196        from_fn(|i| Self {
197            static_slot_id: i as u8,
198            flex_slot_id: i as u8,
199            recorder_slot_id: 128 + i as u8,
200            ..Default::default()
201        })
202    }
203}
204
205/// Values of an Audio Track's parameters page for the Static or FLex machine on the track (pitch/slice/len etc).
206#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Copy)]
207pub struct AudioTrackMachineParamsValuesStd {
208    pub ptch: u8,
209    pub strt: u8,
210    pub len: u8,
211    pub rate: u8,
212    pub rtrg: u8,
213    pub rtim: u8,
214}
215
216/// Values of an Audio Track's parameters page for the Flex machine on the track (in/vol etc).
217#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Copy)]
218pub struct AudioTrackMachineParamsValuesThru {
219    pub in_ab: u8,
220    pub vol_ab: u8,
221    pub unused_1: u8,
222    pub in_cd: u8,
223    pub vol_cd: u8,
224    pub unused_2: u8,
225}
226
227/// Values of an Audio Track's parameters page for the Neighbor machine on the track. Should not contain any data.
228#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Copy)]
229pub struct AudioTrackMachineParamsValuesNeighbor {
230    pub unused_1: u8,
231    pub unused_2: u8,
232    pub unused_3: u8,
233    pub unused_4: u8,
234    pub unused_5: u8,
235    pub unused_6: u8,
236}
237
238/// Values of an Audio Track's parameters page for the Pickup machine on the track. (pitch/dir/len etc).
239#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Copy)]
240pub struct AudioTrackMachineParamsValuesPickup {
241    pub ptch: u8,
242    pub dir: u8,
243    pub len: u8,
244    pub unused_1: u8,
245    pub gain: u8,
246    pub op: u8,
247}
248
249/// Audio Tracks Machine Parameter pages.
250/// Machine parameter values persist after the machine type is changed,
251/// meaning audio keeps playing until the new machine is triggered to play new audio.
252#[derive(
253    Debug, Serialize, Deserialize, Clone, PartialEq, Copy, ArrayDefaults, BoxedBigArrayDefaults,
254)]
255pub struct AudioTrackMachinesParamsValues {
256    pub static_machine: AudioTrackMachineParamsValuesStd,
257    pub flex_machine: AudioTrackMachineParamsValuesStd,
258    pub thru_machine: AudioTrackMachineParamsValuesThru,
259    pub neighbor_machine: AudioTrackMachineParamsValuesNeighbor,
260    pub pickup_machine: AudioTrackMachineParamsValuesPickup,
261}
262
263impl Default for AudioTrackMachinesParamsValues {
264    fn default() -> Self {
265        Self {
266            static_machine: AudioTrackMachineParamsValuesStd {
267                ptch: 64,
268                strt: 0,
269                len: 0,
270                rate: 127,
271                rtrg: 0,
272                rtim: 79,
273            },
274            flex_machine: AudioTrackMachineParamsValuesStd {
275                ptch: 64,
276                strt: 0,
277                len: 0,
278                rate: 127,
279                rtrg: 0,
280                rtim: 79,
281            },
282            thru_machine: AudioTrackMachineParamsValuesThru {
283                in_ab: 0,
284                vol_ab: 64,
285                unused_1: 0,
286                in_cd: 0,
287                vol_cd: 64,
288                unused_2: 0,
289            },
290            neighbor_machine: AudioTrackMachineParamsValuesNeighbor {
291                unused_1: 0,
292                unused_2: 0,
293                unused_3: 0,
294                unused_4: 0,
295                unused_5: 0,
296                unused_6: 0,
297            },
298            pickup_machine: AudioTrackMachineParamsValuesPickup {
299                ptch: 64,
300                dir: 2,
301                len: 1,
302                unused_1: 127,
303                gain: 64,
304                op: 1,
305            },
306        }
307    }
308}
309
310/// Values of a Track's main LFO parameters page.
311/// Note that the speed and depth settings in the LFO Setup menu are looking at these values too.
312#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Copy)]
313pub struct LfoParamsValues {
314    pub spd1: u8,
315    pub spd2: u8,
316    pub spd3: u8,
317    pub dep1: u8,
318    pub dep2: u8,
319    pub dep3: u8,
320}
321
322/// Values of a Track's main AMP parameters page.
323#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Copy)]
324pub struct AudioTrackAmpParamsValues {
325    pub atk: u8,
326    pub hold: u8,
327    pub rel: u8,
328    pub vol: u8,
329    pub bal: u8,
330    /// Reserved space for the \<F\> parameter used by Scenes and LFOs
331    pub f: u8,
332}
333
334// allow the verbose implementation to keep things
335// - (a) standardised across all types
336// - (b) easier for non-rustaceans to follow when reading through data structures
337#[allow(clippy::derivable_impls)]
338impl Default for AudioTrackAmpParamsValues {
339    fn default() -> Self {
340        Self {
341            atk: 0,
342            hold: 127,
343            rel: 127,
344            vol: 64,
345            bal: 64,
346            f: 127,
347        }
348    }
349}
350
351/// Values of a Track's FX parameters page.
352#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Copy)]
353pub struct AudioTrackFxParamsValues {
354    pub param_1: u8,
355    pub param_2: u8,
356    pub param_3: u8,
357    pub param_4: u8,
358    pub param_5: u8,
359    pub param_6: u8,
360}
361
362/// Audio Tracks Parameter Page values. LFO, Amp, FX etc.
363#[derive(
364    Debug, Serialize, Deserialize, Clone, PartialEq, Copy, ArrayDefaults, BoxedBigArrayDefaults,
365)]
366pub struct AudioTrackParamsValues {
367    pub lfo: LfoParamsValues,
368    pub amp: AudioTrackAmpParamsValues,
369    pub fx1: AudioTrackFxParamsValues,
370    pub fx2: AudioTrackFxParamsValues,
371}
372
373impl Default for AudioTrackParamsValues {
374    fn default() -> Self {
375        Self {
376            lfo: LfoParamsValues {
377                spd1: 32,
378                spd2: 32,
379                spd3: 32,
380                dep1: 0,
381                dep2: 0,
382                dep3: 0,
383            },
384            amp: AudioTrackAmpParamsValues::default(),
385            fx1: AudioTrackFxParamsValues {
386                param_1: 0,
387                param_2: 127,
388                param_3: 0,
389                param_4: 64,
390                param_5: 0,
391                param_6: 64,
392            },
393            fx2: AudioTrackFxParamsValues {
394                param_1: 47,
395                param_2: 0,
396                param_3: 127,
397                param_4: 0,
398                param_5: 127,
399                param_6: 0,
400            },
401        }
402    }
403}
404
405/// First set of values for a Track's LFO Setup page.
406#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Copy)]
407pub struct LfoParamsSetup1 {
408    pub lfo1_pmtr: u8,
409    pub lfo2_pmtr: u8,
410    pub lfo3_pmtr: u8,
411    pub lfo1_wave: u8,
412    pub lfo2_wave: u8,
413    pub lfo3_wave: u8,
414}
415
416/// Second set of values for a Track's LFO Setup page.
417#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Copy)]
418pub struct LfoParamsSetup2 {
419    pub lfo1_mult: u8,
420    pub lfo2_mult: u8,
421    pub lfo3_mult: u8,
422    pub lfo1_trig: u8,
423    pub lfo2_trig: u8,
424    pub lfo3_trig: u8,
425}
426
427/// Values for a Track's AMP Setup page.
428#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Copy)]
429pub struct AudioTrackAmpParamsSetup {
430    pub amp: u8,
431    pub sync: u8,
432    pub atck: u8,
433    pub fx1: u8,
434    pub fx2: u8,
435    // This is the hidden <F> parameter used in LFOs and Scenes etc.
436    pub unused: u8,
437}
438
439/// Values for a Track's FX Setup page.
440#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Copy)]
441pub struct AudioTrackFxParamsSetup {
442    pub setting1: u8,
443    pub setting2: u8,
444    pub setting3: u8,
445    pub setting4: u8,
446    pub setting5: u8,
447    pub setting6: u8,
448}
449
450/// Audio Tracks Parameter Setup pages for LFO, Amp, FX etc.
451#[derive(
452    Debug, Serialize, Deserialize, Clone, PartialEq, Copy, ArrayDefaults, BoxedBigArrayDefaults,
453)]
454pub struct AudioTrackParamsSetup {
455    /// Parameter Target and Wave selection for the 3x LFOs on this Audio Track.
456    pub lfo_setup_1: LfoParamsSetup1,
457    /// Amplitude setup page
458    pub amp: AudioTrackAmpParamsSetup,
459    /// FX #1 setup page
460    pub fx1: AudioTrackFxParamsSetup,
461    /// FX #2 setup page
462    pub fx2: AudioTrackFxParamsSetup,
463    /// Multiplier and Trigger type selection for the 3x LFOs on this Audio Track.
464    pub lfo_setup_2: LfoParamsSetup2,
465}
466
467impl Default for AudioTrackParamsSetup {
468    fn default() -> Self {
469        Self {
470            lfo_setup_1: LfoParamsSetup1 {
471                lfo1_pmtr: 0,
472                lfo2_pmtr: 0,
473                lfo3_pmtr: 0,
474                lfo1_wave: 0,
475                lfo2_wave: 0,
476                lfo3_wave: 0,
477            },
478            amp: AudioTrackAmpParamsSetup {
479                amp: 1,
480                sync: 1,
481                atck: 0,
482                fx1: 0,
483                fx2: 0,
484                unused: 0,
485            },
486            fx1: AudioTrackFxParamsSetup {
487                setting1: 0,
488                setting2: 0,
489                setting3: 1,
490                setting4: 0,
491                setting5: 3,
492                setting6: 0,
493            },
494            fx2: AudioTrackFxParamsSetup {
495                setting1: 0,
496                setting2: 1,
497                setting3: 127,
498                setting4: 1,
499                setting5: 0,
500                setting6: 0,
501            },
502            lfo_setup_2: LfoParamsSetup2 {
503                lfo1_mult: 0,
504                lfo2_mult: 0,
505                lfo3_mult: 0,
506                lfo1_trig: 0,
507                lfo2_trig: 0,
508                lfo3_trig: 0,
509            },
510        }
511    }
512}
513
514/// Values for a MIDI Track's MIDI parameter page.
515#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Copy)]
516pub struct MidiTrackMidiParamsValues {
517    pub note: u8,
518    pub vel: u8,
519    pub len: u8,
520    pub not2: u8,
521    pub not3: u8,
522    pub not4: u8,
523}
524
525/// Values for a MIDI Track's Arp parameter page.
526#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Copy)]
527pub struct MidiTrackArpParamsValues {
528    pub tran: u8,
529    pub leg: u8,
530    pub mode: u8,
531    pub spd: u8,
532    pub rnge: u8,
533    pub nlen: u8,
534}
535
536/// Values for a MIDI Track's CC1 parameter page.
537#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Copy)]
538pub struct MidiTrackCc1ParamsValues {
539    pub pb: u8,
540    pub at: u8,
541    pub cc1: u8,
542    pub cc2: u8,
543    pub cc3: u8,
544    pub cc4: u8,
545}
546
547/// Values for a MIDI Track's CC2 parameter page.
548#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Copy)]
549pub struct MidiTrackCc2ParamsValues {
550    pub cc5: u8,
551    pub cc6: u8,
552    pub cc7: u8,
553    pub cc8: u8,
554    pub cc9: u8,
555    pub cc10: u8,
556}
557
558/*
559MIDI TRACK PARAMETER BYTES:
560===========================
56130 64 06 40 40 40 | midi note
56220 20 20 00 00 00 | lfo
56340 00 00 05 00 06 | arp
56440 00 7f 00 00 40 | midi ctrl 1
56500 00 00 00 00 00 | midi ctrl 2
56600 00             | unused (internal re-use of audio track struct)
567===========================
568*/
569/// MIDI Track parameter values. MIDI/Note, LFO, Amp, Ctrl 1, Ctrl 2.
570#[derive(
571    Debug, Serialize, Deserialize, Clone, PartialEq, Copy, ArrayDefaults, BoxedBigArrayDefaults,
572)]
573pub struct MidiTrackParamsValues {
574    pub midi: MidiTrackMidiParamsValues,
575    pub lfo: LfoParamsValues,
576    pub arp: MidiTrackArpParamsValues,
577    pub ctrl1: MidiTrackCc1ParamsValues,
578    pub ctrl2: MidiTrackCc2ParamsValues,
579    #[serde(with = "BigArray")]
580    pub unknown: [u8; 2],
581}
582
583impl Default for MidiTrackParamsValues {
584    fn default() -> Self {
585        Self {
586            midi: MidiTrackMidiParamsValues {
587                note: 48,
588                vel: 100,
589                len: 6,
590                not2: 64,
591                not3: 64,
592                not4: 64,
593            },
594            lfo: LfoParamsValues {
595                spd1: 32,
596                spd2: 32,
597                spd3: 32,
598                dep1: 0,
599                dep2: 0,
600                dep3: 0,
601            },
602            arp: MidiTrackArpParamsValues {
603                tran: 64,
604                leg: 0,
605                mode: 0,
606                spd: 5,
607                rnge: 0,
608                nlen: 6,
609            },
610            ctrl1: MidiTrackCc1ParamsValues {
611                pb: 64,
612                at: 0,
613                cc1: 127,
614                cc2: 0,
615                cc3: 0,
616                cc4: 64,
617            },
618            ctrl2: MidiTrackCc2ParamsValues {
619                cc5: 0,
620                cc6: 0,
621                cc7: 0,
622                cc8: 0,
623                cc9: 0,
624                cc10: 0,
625            },
626            unknown: [0, 0],
627        }
628    }
629}
630
631/// Values for a MIDI Track's Note Setup page.
632#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Copy)]
633pub struct MidiTrackNoteParamsSetup {
634    pub chan: u8,
635    pub bank: u8,
636    pub prog: u8,
637    pub unused_1: u8,
638    pub sbank: u8,
639    pub unused_2: u8,
640}
641
642/// Values for a MIDI Track's Arp Setup page.
643#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Copy)]
644pub struct MidiTrackArpParamsSetup {
645    pub unused_1: u8,
646    pub unused_2: u8,
647    /// Length of the arp sequence
648    pub len: u8,
649    pub unused_3: u8,
650    pub unused_4: u8,
651    /// maximum 24 -- B Min
652    /// minimum 0 -- Off
653    pub key: u8,
654}
655
656/// Values for a MIDI Track's CC1 Setup page.
657#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Copy)]
658pub struct MidiTrackCc1ParamsSetup {
659    pub unused_1: u8,
660    pub unused_2: u8,
661    pub cc1: u8,
662    pub cc2: u8,
663    pub cc3: u8,
664    pub cc4: u8,
665}
666
667/// Values for a MIDI Track's CC2 Setup page.
668#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Copy)]
669pub struct MidiTrackCc2ParamsSetup {
670    pub cc5: u8,
671    pub cc6: u8,
672    pub cc7: u8,
673    pub cc8: u8,
674    pub cc9: u8,
675    pub cc10: u8,
676}
677
678/// MIDI Tracks Parameter Setup pages for LFO, Amp, FX etc.
679#[derive(
680    Debug, Serialize, Deserialize, Clone, PartialEq, Copy, ArrayDefaults, BoxedBigArrayDefaults,
681)]
682pub struct MidiTrackParamsSetup {
683    pub note: MidiTrackNoteParamsSetup,
684    pub lfo1: LfoParamsSetup1,
685    pub arp: MidiTrackArpParamsSetup,
686    pub ctrl1: MidiTrackCc1ParamsSetup,
687    pub ctrl2: MidiTrackCc2ParamsSetup,
688    pub lfo2: LfoParamsSetup2,
689}
690
691impl Default for MidiTrackParamsSetup {
692    fn default() -> Self {
693        Self {
694            note: MidiTrackNoteParamsSetup {
695                chan: 0,
696                bank: 128,
697                prog: 128,
698                unused_1: 0,
699                sbank: 128,
700                unused_2: 0,
701            },
702            lfo1: LfoParamsSetup1 {
703                lfo1_pmtr: 0,
704                lfo2_pmtr: 0,
705                lfo3_pmtr: 0,
706                lfo1_wave: 0,
707                lfo2_wave: 0,
708                lfo3_wave: 0,
709            },
710            arp: MidiTrackArpParamsSetup {
711                unused_1: 0,
712                unused_2: 0,
713                len: 7,
714                unused_3: 0,
715                unused_4: 0,
716                key: 0,
717            },
718            ctrl1: MidiTrackCc1ParamsSetup {
719                unused_1: 0,
720                unused_2: 0,
721                cc1: 7,
722                cc2: 1,
723                cc3: 2,
724                cc4: 10,
725            },
726            ctrl2: MidiTrackCc2ParamsSetup {
727                cc5: 71,
728                cc6: 72,
729                cc7: 73,
730                cc8: 74,
731                cc9: 75,
732                cc10: 76,
733            },
734            lfo2: LfoParamsSetup2 {
735                lfo1_mult: 0,
736                lfo2_mult: 0,
737                lfo3_mult: 0,
738                lfo1_trig: 0,
739                lfo2_trig: 0,
740                lfo3_trig: 0,
741            },
742        }
743    }
744}
745
746/// A custom LFO Design -- array of 16 values.
747/// 0 -> 127 values (above line) maps to 0 -> 127.
748/// -1 -> -127 values (above line) seems to map to 255 -> 128.
749#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, ArrayDefaults, BoxedBigArrayDefaults)]
750pub struct CustomLfoDesign(pub [u8; 16]);
751
752/// Default is a 16 length array with zero values
753impl Default for CustomLfoDesign {
754    fn default() -> Self {
755        Self(from_fn(|_| 0))
756    }
757}
758
759/// LFO Interpolation mask.
760/// Indicates which LFO steps should have values interpolated when LFO is triggered.
761/// Not sure exactly how the calculation works yet.
762#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, ArrayDefaults, BoxedBigArrayDefaults)]
763pub struct CustomLfoInterpolationMask(pub [u8; 2]);
764
765impl Default for CustomLfoInterpolationMask {
766    fn default() -> Self {
767        Self(from_fn(|_| 0))
768    }
769}
770
771/// First page of settings controlling an Audio Track's Recording Buffer configuration.
772#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Copy)]
773pub struct RecorderSetupSources {
774    pub in_ab: u8,
775    pub in_cd: u8,
776    /// `64` is MAX. `63` and lower are actual values.
777    pub rlen: u8,
778    pub trig: u8,
779    /// internal recording source
780    pub src3: u8,
781    pub xloop: u8,
782}
783
784/// Second page of settings controlling an Audio Track's Recording Buffer configuration.
785#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Copy)]
786pub struct RecorderSetupProcessing {
787    pub fin: u8,
788    pub fout: u8,
789    pub ab: u8,
790    pub qrec: u8,
791    pub qpl: u8,
792    pub cd: u8,
793}
794
795/// Recorder Setup Pages
796#[derive(
797    Debug, Serialize, Deserialize, Clone, PartialEq, Copy, ArrayDefaults, BoxedBigArrayDefaults,
798)]
799pub struct RecorderSetup {
800    pub src: RecorderSetupSources,
801    pub proc: RecorderSetupProcessing,
802}
803
804impl Default for RecorderSetup {
805    fn default() -> Self {
806        Self {
807            src: RecorderSetupSources {
808                in_ab: 1,
809                in_cd: 1,
810                rlen: 64,
811                trig: 0,
812                src3: 0,
813                xloop: 1,
814            },
815            proc: RecorderSetupProcessing {
816                fin: 0,
817                fout: 0,
818                ab: 0,
819                qrec: 255,
820                qpl: 255,
821                cd: 0,
822            },
823        }
824    }
825}
826
827// todo: merge with the Pattern parameter-lock variant of this?
828/// A scene's parameter assignments on the Playback/Machine page for an Audio Track.
829#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Copy)]
830pub struct AudioTrackSceneLockPlayback {
831    pub param1: u8,
832    pub param2: u8,
833    pub param3: u8,
834    pub param4: u8,
835    pub param5: u8,
836    pub param6: u8,
837}
838
839/// Scene parameter assignments for a single audio track.
840///
841/// 255 is no assignment on the scene for that specific audio track's parameter
842/// value.
843///
844/// Any other value is the Scene's assigned value for that control.
845#[derive(
846    Debug, Serialize, Deserialize, Clone, PartialEq, Copy, ArrayDefaults, BoxedBigArrayDefaults,
847)]
848pub struct AudioTrackScenesParamsAssignments {
849    /// Scene assignments for the Audio Track's active playback machine
850    pub machine: AudioTrackSceneLockPlayback,
851    /// Scene assignments for the Audio Track's LFO parameters
852    pub lfo: LfoParamsValues,
853    /// Scene assignments for the Audio Track's AMP parameters
854    pub amp: AudioTrackAmpParamsValues,
855    /// Scene assignments for the Audio Track's FX1 parameters
856    pub fx1: AudioTrackFxParamsValues,
857    /// Scene assignments for the Audio Track's FX2 parameters
858    pub fx2: AudioTrackFxParamsValues,
859    /// Unknown, likely leftover sample slot assignment (it
860    /// seems the underlying machine OS code re-uses the same
861    /// data structure in several places).
862    pub unknown_1: u8,
863    /// Unknown, likely leftover sample slot assignment (it
864    /// seems the underlying machine OS code re-uses the same
865    /// data structure in several places).
866    pub unknown_2: u8,
867}
868
869impl Default for AudioTrackScenesParamsAssignments {
870    fn default() -> Self {
871        Self {
872            machine: AudioTrackSceneLockPlayback {
873                param1: 255,
874                param2: 255,
875                param3: 255,
876                param4: 255,
877                param5: 255,
878                param6: 255,
879            },
880            lfo: LfoParamsValues {
881                spd1: 255,
882                spd2: 255,
883                spd3: 255,
884                dep1: 255,
885                dep2: 255,
886                dep3: 255,
887            },
888            amp: AudioTrackAmpParamsValues {
889                atk: 255,
890                hold: 255,
891                rel: 255,
892                vol: 255,
893                bal: 255,
894                f: 255,
895            },
896            fx1: AudioTrackFxParamsValues {
897                param_1: 255,
898                param_2: 255,
899                param_3: 255,
900                param_4: 255,
901                param_5: 255,
902                param_6: 255,
903            },
904            fx2: AudioTrackFxParamsValues {
905                param_1: 255,
906                param_2: 255,
907                param_3: 255,
908                param_4: 255,
909                param_5: 255,
910                param_6: 255,
911            },
912            unknown_1: 255,
913            unknown_2: 255,
914        }
915    }
916}
917
918/// Parameters for a specified Scene can be configured across all 8 tracks
919#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, ArrayDefaults, BoxedBigArrayDefaults)]
920pub struct SceneParams(pub [AudioTrackScenesParamsAssignments; 8]);
921
922impl Default for SceneParams {
923    fn default() -> Self {
924        Self(from_fn(|_| AudioTrackScenesParamsAssignments::default()))
925    }
926}
927
928/// XLV Scene Parameter assignments for an Audio Track,
929/// plus two other parameters i don't know yet
930#[derive(
931    Debug, Serialize, Deserialize, Clone, PartialEq, Copy, ArrayDefaults, BoxedBigArrayDefaults,
932)]
933pub struct SceneXlvAssignments {
934    /// Main Volume fade control (XLV) for each Audio Track
935    /// `MAX` -> `127`.
936    /// `MIN` -> `0`.
937    #[serde(with = "BigArray")]
938    pub track_xlvs: [u8; 8],
939
940    /// Don't know what this data block is for yet.
941    /// Possibly leftover from using the same struct in the machine code
942    /// as for audio track parameter values.
943    #[serde(with = "BigArray")]
944    pub unknown: [u8; 2],
945}
946
947impl Default for SceneXlvAssignments {
948    fn default() -> Self {
949        Self {
950            track_xlvs: from_fn(|_| 255),
951            unknown: from_fn(|_| 255),
952        }
953    }
954}
955
956/// A MIDI Track's custom Arp sequence.
957/// `0` -> `+63` values (above line) maps to `0` -> `63`.
958/// `-1` -> `-63` values (below line) map to `255` -> `192`.
959#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, ArrayDefaults, BoxedBigArrayDefaults)]
960pub struct MidiArpSequence(pub [u8; 16]);
961
962impl Default for MidiArpSequence {
963    fn default() -> Self {
964        Self(from_fn(|_| 0))
965    }
966}
967
968/// Parts in the bank, containing track data.
969#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, BoxedBigArrayDefaults)]
970pub struct Part {
971    #[serde(with = "BigArray")]
972    pub header: [u8; 4],
973
974    /// All 0 values.
975    #[serde(with = "BigArray")]
976    pub data_block_1: [u8; 4],
977
978    /// Zero-indexed part number
979    pub part_id: u8,
980
981    /// Audio Tracks active FX for FX1.
982    /// ```text
983    /// OFF -> 0
984    /// FILTER -> 4
985    /// EQ -> 12
986    /// DJ EQ -> 13
987    /// PHASER -> 16
988    /// FLANGER -> 17
989    /// CHORUS -> 18
990    /// SPATIALIZER -> 5
991    /// COMB FILTER -> 19
992    /// COMPRESSOR -> 24
993    /// LOFI -> 24
994    /// ```
995    #[serde(with = "BigArray")]
996    pub audio_track_fx1: [u8; 8],
997
998    /// Audio Tracks active FX for FX2.
999    /// ```text
1000    /// OFF -> 0
1001    /// FILTER -> 4
1002    /// EQ -> 12
1003    /// DJ EQ -> 13
1004    /// PHASER -> 16
1005    /// FLANGER -> 17
1006    /// CHORUS -> 18
1007    /// SPATIALIZER -> 5
1008    /// COMB FILTER -> 19
1009    /// COMPRESSOR -> 24
1010    /// LOFI -> 24
1011    /// DELAY -> 8
1012    /// PLATE REVERB -> 20
1013    /// SPRING REVERB -> 21
1014    /// DARK REVERB -> 22
1015    /// ```
1016    #[serde(with = "BigArray")]
1017    pub audio_track_fx2: [u8; 8],
1018
1019    /// Currently selected Scenes for a Part.
1020    pub active_scenes: ActiveScenes,
1021
1022    /// Volumes for Audio Tracks.
1023    #[serde(with = "BigArray")]
1024    pub audio_track_volumes: [AudioTrackVolume; 8],
1025
1026    /// Audio Tracks Machine types.
1027    /// Static = 0, Flex = 1, Thru = 2, Neighbour = 3, Pickup = 4.
1028    #[serde(with = "BigArray")]
1029    pub audio_track_machine_types: [u8; 8],
1030
1031    /// Parameters for Audio Track machines:
1032    /// Static, Flex, Thru, Neighbor and Pickup machines.
1033    #[serde(with = "BigArray")]
1034    pub audio_track_machine_params: [AudioTrackMachinesParamsValues; 8],
1035
1036    /// Audio Track audio processing parameter values:
1037    /// Amplitude, LFOs, fx1 and fx2.
1038    #[serde(with = "BigArray")]
1039    pub audio_track_params_values: [AudioTrackParamsValues; 8],
1040
1041    /// Setup values for Audio Track machines:
1042    /// Static, Flex, Thru, Neighbor and Pickup machines.
1043    #[serde(with = "BigArray")]
1044    pub audio_track_machine_setup: [AudioTrackMachinesParamsSetup; 8],
1045
1046    /// Audio Track sample slot assignments.
1047    #[serde(with = "BigArray")]
1048    pub audio_track_machine_slots: [AudioTrackMachineSlot; 8],
1049
1050    /// Audio Track audio processing setup values:
1051    /// Amplitude, LFOs, fx1 and fx2.
1052    #[serde(with = "BigArray")]
1053    pub audio_track_params_setup: [AudioTrackParamsSetup; 8],
1054
1055    /// MIDI Track processing values:
1056    /// Amplitude, LFOs, fx1 and fx2.
1057    #[serde(with = "BigArray")]
1058    pub midi_track_params_values: [MidiTrackParamsValues; 8],
1059
1060    /// MIDI Track processing setup values:
1061    /// Amplitude, LFOs, fx1 and fx2.
1062    #[serde(with = "BigArray")]
1063    pub midi_track_params_setup: [MidiTrackParamsSetup; 8],
1064
1065    /// Audio Track Recorder settings.
1066    #[serde(with = "BigArray")]
1067    pub recorder_setup: [RecorderSetup; 8],
1068
1069    /// Scene parameter assignments. There are 16 Scenes in a bank. Each scene
1070    /// can be used to modify parameter page values across any of the audio
1071    /// tracks.
1072    #[serde(with = "BigArray")]
1073    pub scenes: [SceneParams; 16],
1074
1075    #[serde(with = "BigArray")]
1076    pub scene_xlvs: [SceneXlvAssignments; 16],
1077
1078    /// Custom LFO designs for Audio Tracks.
1079    #[serde(with = "BigArray")]
1080    pub audio_tracks_custom_lfo_designs: [CustomLfoDesign; 8],
1081
1082    /// Interpolation of steps in custom LFOs for Audio Tracks.
1083    /// Indicates which LFO steps should have values interpolated when LFO is triggered.
1084    /// Not sure exactly how the calculation works yet.
1085    #[serde(with = "BigArray")]
1086    pub audio_tracks_custom_lfos_interpolation_masks: [CustomLfoInterpolationMask; 8],
1087
1088    /// Custom LFO designs for MIDI Tracks.
1089    #[serde(with = "BigArray")]
1090    pub midi_tracks_custom_lfos: [CustomLfoDesign; 8],
1091
1092    /// Interpolation of steps in custom LFOs for MIDI Tracks.
1093    /// Indicates which LFO steps should have values interpolated when LFO is triggered.
1094    /// Not sure exactly how the calculation works yet.
1095    #[serde(with = "BigArray")]
1096    pub midi_tracks_custom_lfos_interpolation_masks: [CustomLfoInterpolationMask; 8],
1097
1098    /// Arp Sequence Mutes for MIDI tracks.
1099    /// Not sure how these work.
1100    /// Muting some of the notes on M-TR-8 has the last array element set to 7 instead of 255.
1101    #[serde(with = "BigArray")]
1102    pub midi_tracks_arp_mute_masks: [u8; 16],
1103
1104    /// Arp Sequence Notes for MIDI tracks.
1105    pub midi_tracks_arp_seqs: [MidiArpSequence; 8],
1106}
1107
1108impl Default for Part {
1109    fn default() -> Self {
1110        Self {
1111            header: PART_HEADER,
1112            data_block_1: from_fn(|_| 0),
1113            part_id: 1,
1114            audio_track_fx1: from_fn(|_| 4),
1115            audio_track_fx2: from_fn(|_| 8),
1116            active_scenes: ActiveScenes::default(),
1117            audio_track_volumes: AudioTrackVolume::defaults(),
1118            audio_track_machine_types: from_fn(|_| 0),
1119            audio_track_machine_params: AudioTrackMachinesParamsValues::defaults(),
1120            audio_track_params_values: AudioTrackParamsValues::defaults(),
1121            audio_track_machine_setup: AudioTrackMachinesParamsSetup::defaults(),
1122            audio_track_machine_slots: AudioTrackMachineSlot::defaults(),
1123            audio_track_params_setup: AudioTrackParamsSetup::defaults(),
1124            midi_track_params_values: MidiTrackParamsValues::defaults(),
1125            midi_track_params_setup: MidiTrackParamsSetup::defaults(),
1126            recorder_setup: RecorderSetup::defaults(),
1127            scenes: SceneParams::defaults(),
1128            scene_xlvs: SceneXlvAssignments::defaults(),
1129            audio_tracks_custom_lfo_designs: CustomLfoDesign::defaults(),
1130            audio_tracks_custom_lfos_interpolation_masks: CustomLfoInterpolationMask::defaults(),
1131            midi_tracks_custom_lfos: CustomLfoDesign::defaults(),
1132            midi_tracks_custom_lfos_interpolation_masks: CustomLfoInterpolationMask::defaults(),
1133            midi_tracks_arp_mute_masks: from_fn(|_| 255),
1134            midi_tracks_arp_seqs: MidiArpSequence::defaults(),
1135        }
1136    }
1137}
1138
1139// need to implement manually to handle part_id field
1140impl<const N: usize> Defaults<[Self; N]> for Part {
1141    fn defaults() -> [Self; N]
1142    where
1143        Self: Default,
1144    {
1145        from_fn(|i| Self {
1146            part_id: i as u8,
1147            ..Default::default()
1148        })
1149    }
1150}
1151
1152impl HasHeaderField for Part {
1153    fn check_header(&self) -> Result<bool, OtToolsIoError> {
1154        Ok(self.header == PART_HEADER)
1155    }
1156}
1157
1158#[cfg(test)]
1159mod part_header_field {
1160    use crate::{
1161        test_utils::get_blank_proj_dirpath, BankFile, HasHeaderField, OctatrackFileIO,
1162        OtToolsIoError,
1163    };
1164    #[test]
1165    fn valid() -> Result<(), OtToolsIoError> {
1166        let path = get_blank_proj_dirpath().join("bank01.work");
1167        let parts = BankFile::from_data_file(&path)?.parts.saved.clone();
1168        let part = parts[0].clone();
1169        assert!(part.check_header()?);
1170        Ok(())
1171    }
1172
1173    #[test]
1174    fn invalid() -> Result<(), OtToolsIoError> {
1175        let path = get_blank_proj_dirpath().join("bank01.work");
1176        let parts = BankFile::from_data_file(&path)?.parts.saved.clone();
1177        let mut part = parts[0].clone();
1178
1179        part.header[0] = 255;
1180        part.header[1] = 255;
1181        part.header[2] = 255;
1182        part.header[3] = 255;
1183
1184        assert!(!part.check_header()?);
1185        Ok(())
1186    }
1187}
1188
1189/// Contains the two different types of Part data
1190#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
1191pub struct Parts {
1192    /// Unsaved Part data for a Bank.
1193    ///
1194    /// Part state prior to saving a Part via the Part menu (but after SYNC TO CARD).
1195    pub unsaved: Box<Array<Part, 4>>,
1196    /// Saved Part data for a Bank.
1197    ///
1198    /// Part state once the Part has been saved via the Part menu is stored here.
1199    pub saved: Box<Array<Part, 4>>,
1200}
1201
1202impl Default for Parts {
1203    fn default() -> Self {
1204        Self {
1205            unsaved: Part::defaults(),
1206            saved: Part::defaults(),
1207        }
1208    }
1209}
1210
1211impl HasHeaderField for Parts {
1212    fn check_header(&self) -> Result<bool, OtToolsIoError> {
1213        let unsaved = self
1214            .unsaved
1215            .iter()
1216            .all(|x| x.check_header().is_ok_and(|x| x));
1217        let saved = self.saved.iter().all(|x| x.check_header().is_ok_and(|x| x));
1218
1219        Ok(saved && unsaved)
1220    }
1221}
1222
1223#[cfg(test)]
1224mod parts_header_field {
1225    use crate::{
1226        test_utils::get_blank_proj_dirpath, BankFile, HasHeaderField, OctatrackFileIO,
1227        OtToolsIoError,
1228    };
1229    #[test]
1230    fn valid() -> Result<(), OtToolsIoError> {
1231        let path = get_blank_proj_dirpath().join("bank01.work");
1232        let parts = BankFile::from_data_file(&path)?.parts;
1233        assert!(parts.check_header()?);
1234        Ok(())
1235    }
1236
1237    #[test]
1238    fn invalid() -> Result<(), OtToolsIoError> {
1239        let path = get_blank_proj_dirpath().join("bank01.work");
1240        let mut parts = BankFile::from_data_file(&path)?.parts;
1241
1242        parts.saved[0].header[0] = 255;
1243        parts.saved[0].header[1] = 255;
1244        parts.saved[0].header[2] = 255;
1245        parts.saved[0].header[3] = 255;
1246
1247        assert!(!parts.check_header()?);
1248        Ok(())
1249    }
1250}