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