ot_tools_io/banks/
patterns.rs

1/*
2SPDX-License-Identifier: GPL-3.0-or-later
3Copyright © 2024 Mike Robeson [dijksterhuis]
4*/
5
6//! Models for pattern data within a bank.
7use crate::{
8    banks::parts::{
9        AudioTrackAmpParamsValues, AudioTrackFxParamsValues, LfoParamsValues,
10        MidiTrackArpParamsValues, MidiTrackCc1ParamsValues, MidiTrackCc2ParamsValues,
11        MidiTrackLfoParamsValues, MidiTrackMidiParamsValues,
12    },
13    CheckHeader, DefaultsArrayBoxed, OptionEnumValueConvert, OtToolsIoErrors,
14};
15use ot_tools_io_derive::DefaultsAsBoxedBigArray;
16use std::array::from_fn;
17
18use crate::RBoxErr;
19use serde::{Deserialize, Serialize};
20use serde_big_array::{Array, BigArray};
21
22const HALF_PAGE_TRIG_BITMASK_VALUES: [u8; 8] = [1, 2, 4, 8, 16, 32, 64, 128];
23const PATTERN_HEADER: [u8; 8] = [0x50, 0x54, 0x52, 0x4e, 0x00, 0x00, 0x00, 0x00];
24
25/// Header array for a MIDI track section in binary data files: `MTRA`
26const MIDI_TRACK_HEADER: [u8; 4] = [0x4d, 0x54, 0x52, 0x41];
27
28/// Header array for a MIDI track section in binary data files: `TRAC`
29const AUDIO_TRACK_HEADER: [u8; 4] = [0x54, 0x52, 0x41, 0x43];
30
31/// Given a half-page trig bit mask, get an array of 8x boolean values
32/// indicating whether each trig in the half-page is active or not
33pub fn get_halfpage_trigs_from_bitmask_value(bitmask: &u8) -> RBoxErr<[bool; 8]> {
34    let arr: [bool; 8] = HALF_PAGE_TRIG_BITMASK_VALUES
35        .iter()
36        .map(|x| (bitmask & x) > 0)
37        .collect::<Vec<bool>>()
38        .try_into()
39        .unwrap();
40    Ok(arr)
41}
42
43/// Given a half-page trig bit mask, get an array of 8x boolean values
44/// indicating where each trig in the half-page is active or not
45pub fn get_track_trigs_from_bitmasks(bitmasks: &[u8; 8]) -> RBoxErr<[bool; 64]> {
46    let trigs: [bool; 64] = bitmasks
47        .iter()
48        .flat_map(|x: &u8| get_halfpage_trigs_from_bitmask_value(x).unwrap())
49        .collect::<Vec<bool>>()
50        .try_into()
51        .unwrap();
52
53    Ok(trigs)
54}
55
56/// A Trig's parameter locks on the Playback/Machine page for an Audio Track.
57#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Copy)]
58pub struct AudioTrackParameterLockPlayback {
59    pub param1: u8,
60    pub param2: u8,
61    pub param3: u8,
62    pub param4: u8,
63    pub param5: u8,
64    pub param6: u8,
65}
66
67impl Default for AudioTrackParameterLocks {
68    fn default() -> Self {
69        // 255 -> disabled
70
71        // NOTE: the `part.rs` `default` methods for each of these type has
72        // fields all set to the correct defaults for the TRACK view, not p-lock
73        // trigS. So don't try and use the type's `default` method here as you
74        // will end up with a bunch of p-locks on trigs for all the default
75        // values. (Although maybe that's a desired feature for some workflows).
76
77        // Yes, this comment is duplicated below. It is to make sur you've seen
78        // it.
79        Self {
80            machine: AudioTrackParameterLockPlayback {
81                param1: 255,
82                param2: 255,
83                param3: 255,
84                param4: 255,
85                param5: 255,
86                param6: 255,
87            },
88            lfo: LfoParamsValues {
89                spd1: 255,
90                spd2: 255,
91                spd3: 255,
92                dep1: 255,
93                dep2: 255,
94                dep3: 255,
95            },
96            amp: AudioTrackAmpParamsValues {
97                atk: 255,
98                hold: 255,
99                rel: 255,
100                vol: 255,
101                bal: 255,
102                unused: 255,
103            },
104            fx1: AudioTrackFxParamsValues {
105                param_1: 255,
106                param_2: 255,
107                param_3: 255,
108                param_4: 255,
109                param_5: 255,
110                param_6: 255,
111            },
112            fx2: AudioTrackFxParamsValues {
113                param_1: 255,
114                param_2: 255,
115                param_3: 255,
116                param_4: 255,
117                param_5: 255,
118                param_6: 255,
119            },
120            static_slot_id: 255,
121            flex_slot_id: 255,
122        }
123    }
124}
125
126/// A single trig's parameter locks on an Audio Track.
127#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Copy, DefaultsAsBoxedBigArray)]
128pub struct AudioTrackParameterLocks {
129    pub machine: AudioTrackParameterLockPlayback,
130    pub lfo: LfoParamsValues,
131    pub amp: AudioTrackAmpParamsValues,
132    pub fx1: AudioTrackFxParamsValues,
133    pub fx2: AudioTrackFxParamsValues,
134    /// P-Lock to change an audio track's static machine sample slot assignment per trig
135    pub static_slot_id: u8,
136    /// P-Lock to change an audio track's flex machine sample slot assignment per trig
137    pub flex_slot_id: u8,
138}
139
140/// MIDI Track parameter locks.
141#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Copy, DefaultsAsBoxedBigArray)]
142pub struct MidiTrackParameterLocks {
143    pub midi: MidiTrackMidiParamsValues,
144    pub lfo: MidiTrackLfoParamsValues,
145    pub arp: MidiTrackArpParamsValues,
146    pub ctrl1: MidiTrackCc1ParamsValues,
147    pub ctrl2: MidiTrackCc2ParamsValues,
148
149    #[serde(with = "BigArray")]
150    unknown: [u8; 2],
151}
152
153impl Default for MidiTrackParameterLocks {
154    fn default() -> Self {
155        // 255 -> disabled
156
157        // NOTE: the `part.rs` `default` methods for each of these type has
158        // fields all set to the correct defaults for the TRACK view, not p-lock
159        // trigS. So don't try and use the type's `default` method here as you
160        // will end up with a bunch of p-locks on trigs for all the default
161        // values. (Although maybe that's a desired feature for some workflows).
162
163        // Yes, this comment is duplicated above. It is to make sur you've seen
164        // it.
165
166        Self {
167            midi: MidiTrackMidiParamsValues {
168                note: 255,
169                vel: 255,
170                len: 255,
171                not2: 255,
172                not3: 255,
173                not4: 255,
174            },
175            lfo: MidiTrackLfoParamsValues {
176                spd1: 255,
177                spd2: 255,
178                spd3: 255,
179                dep1: 255,
180                dep2: 255,
181                dep3: 255,
182            },
183            arp: MidiTrackArpParamsValues {
184                tran: 255,
185                leg: 255,
186                mode: 255,
187                spd: 255,
188                rnge: 255,
189                nlen: 255,
190            },
191            ctrl1: MidiTrackCc1ParamsValues {
192                pb: 255,
193                at: 255,
194                cc1: 255,
195                cc2: 255,
196                cc3: 255,
197                cc4: 255,
198            },
199            ctrl2: MidiTrackCc2ParamsValues {
200                cc5: 255,
201                cc6: 255,
202                cc7: 255,
203                cc8: 255,
204                cc9: 255,
205                cc10: 255,
206            },
207            unknown: [255, 255],
208        }
209    }
210}
211
212/// Audio & MIDI Track Pattern playback settings.
213#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Copy)]
214pub struct TrackPatternSettings {
215    /// Silence any existing audio playback on the Audio Track when switching Patterns.
216    pub start_silent: u8,
217
218    /// Trigger Audio Track playback without any quantization or syncing to other Audio Tracks.
219    pub plays_free: u8,
220
221    /// Quantization when this Audio Track is Triggered for Playback.
222    ///
223    /// Options
224    /// ```text
225    /// N/A and ONE: 0 (Default)
226    /// ONE2: 1
227    /// HOLD: 2
228    /// ```
229    pub trig_mode: u8,
230
231    /// Track Trigger Quantization.
232    ///
233    /// Options
234    /// ```text
235    /// N/A and TR.LEN: 0 (Default)
236    /// 1/16: 1
237    /// 2/16: 2
238    /// 3/16: 3
239    /// 4/16: 4
240    /// 6/16: 5
241    /// 8/16: 6
242    /// 12/16: 7
243    /// 16/16: 8
244    /// 24/16: 9
245    /// 32/16: 10
246    /// 48/16: 11
247    /// 64/16: 12
248    /// 96/16: 13
249    /// 128/16: 14
250    /// 192/16: 15
251    /// 256/16: 16
252    /// DIRECT: 255
253    /// ```
254    pub trig_quant: u8,
255
256    /// Whether to play the track as a `ONESHOT` track.
257    pub oneshot_trk: u8,
258}
259
260impl Default for TrackPatternSettings {
261    fn default() -> Self {
262        Self {
263            start_silent: 255,
264            plays_free: 0,
265            trig_mode: 0,
266            trig_quant: 0,
267            oneshot_trk: 0,
268        }
269    }
270}
271
272/// Trig bitmasks array for Audio Tracks.
273/// Can be converted into an array of booleans using the `get_track_trigs_from_bitmasks` function.
274///
275/// Trig bitmask arrays have bitmasks stored in this order, which is slightly confusing (pay attention to the difference with 7 + 8!):
276/// 1. 1st half of the 4th page
277/// 2. 2nd half of the 4th page
278/// 3. 1st half of the 3rd page
279/// 4. 2nd half of the 3rd page
280/// 5. 1st half of the 2nd page
281/// 6. 2nd half of the 2nd page
282/// 7. 2nd half of the 1st page
283/// 8. 1st half of the 1st page
284///
285/// ### Bitmask values for trig positions
286/// With single trigs in a half-page
287/// ```text
288/// positions
289/// 1 2 3 4 5 6 7 8 | mask value
290/// ----------------|-----------
291/// - - - - - - - - | 0
292/// x - - - - - - - | 1
293/// - x - - - - - - | 2
294/// - - x - - - - - | 4
295/// - - - x - - - - | 8
296/// - - - - x - - - | 16
297/// - - - - - x - - | 32
298/// - - - - - - x - | 64
299/// - - - - - - - x | 128
300/// ```
301///
302/// When there are multiple trigs in a half-page, the individual position values are summed together:
303///
304/// ```text
305/// 1 2 3 4 5 6 7 8 | mask value
306/// ----------------|-----------
307/// x x - - - - - - | 1 + 2 = 3
308/// x x x x - - - - | 1 + 2 + 4 + 8 = 15
309/// ```
310/// ### Fuller diagram of mask values
311///
312/// ```text
313/// positions
314/// 1 2 3 4 5 6 7 8 | mask value
315/// ----------------|-----------
316/// x - - - - - - - | 1
317/// - x - - - - - - | 2
318/// x x - - - - - - | 3
319/// - - x - - - - - | 4
320/// x - x - - - - - | 5
321/// - x x - - - - - | 6
322/// x x x - - - - - | 7
323/// - - - x - - - - | 8
324/// x - - x - - - - | 9
325/// - x - x - - - - | 10
326/// x x - x - - - - | 11
327/// - - x x - - - - | 12
328/// x - x x - - - - | 13
329/// - x x x - - - - | 14
330/// x x x x - - - - | 15
331/// ................|....
332/// x x x x x x - - | 63
333/// ................|....
334/// - - - - - - - x | 128
335/// ................|....
336/// - x - x - x - x | 170
337/// ................|....
338/// - - - - x x x x | 240
339/// ................|....
340/// x x x x x x x x | 255
341/// ```
342///
343#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
344pub struct AudioTrackTrigMasks {
345    /// Trigger Trig masks -- indicate which Trigger Trigs are active.
346    /// Base track Trig masks are stored backwards, meaning
347    /// the first 8 Trig positions are the last bytes in this section.
348    #[serde(with = "BigArray")]
349    pub trigger: [u8; 8],
350
351    /// Envelope Trig masks -- indicate which Envelope Trigs are active.
352    /// See the description of the `trig_trig_masks` field for an
353    /// explanation of how the masking works.
354    #[serde(with = "BigArray")]
355    pub trigless: [u8; 8],
356
357    /// Parameter-Lock Trig masks -- indicate which Parameter-Lock Trigs are active.
358    /// See the description of the `trig_trig_masks` field for an
359    /// explanation of how the masking works.    
360    #[serde(with = "BigArray")]
361    pub plock: [u8; 8],
362
363    /// Hold Trig masks -- indicate which Hold Trigs are active.
364    /// See the description of the `trig_trig_masks` field for an
365    /// explanation of how the masking works.
366    #[serde(with = "BigArray")]
367    pub oneshot: [u8; 8],
368
369    /// Recorder Trig masks -- indicate which Recorder Trigs are active.
370    /// These seem to function differently to the main Track Trig masks.
371    /// Filling up Recorder Trigs on a Pattern results in a 32 length array
372    /// instead of 8 length.
373    /// Possible that the Trig type is stored in this array as well.
374    #[serde(with = "BigArray")]
375    pub recorder: [u8; 32],
376
377    /// Swing trigs Trig masks.
378    #[serde(with = "BigArray")]
379    pub swing: [u8; 8],
380
381    /// Parameter Slide trigs Trig masks.
382    #[serde(with = "BigArray")]
383    pub slide: [u8; 8],
384}
385
386impl Default for AudioTrackTrigMasks {
387    fn default() -> Self {
388        Self {
389            trigger: from_fn(|_| 0),
390            trigless: from_fn(|_| 0),
391            plock: from_fn(|_| 0),
392            oneshot: from_fn(|_| 0),
393            recorder: from_fn(|_| 0),
394            swing: from_fn(|_| 170),
395            slide: from_fn(|_| 0),
396        }
397    }
398}
399
400/// Audio Track custom scaling when the Pattern is in PER TRACK scale mode.
401#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Copy)]
402pub struct TrackPerTrackModeScale {
403    /// The Audio Track's Length when Pattern is in Per Track mode.
404    /// Default: 16
405    pub per_track_len: u8,
406
407    /// The Audio Track's Scale when Pattern is in Per Track mode.
408    ///
409    /// Options
410    /// ```text
411    /// 0 -> 2x
412    /// 1 -> 3/2x
413    /// 2 -> 1x (Default)
414    /// 3 -> 3/4x
415    /// 4 -> 1/2x
416    /// 5 -> 1/4x
417    /// 6 -> 1/8x
418    /// ```
419    pub per_track_scale: u8,
420}
421
422impl Default for TrackPerTrackModeScale {
423    fn default() -> Self {
424        Self {
425            per_track_len: 16,
426            per_track_scale: 2,
427        }
428    }
429}
430
431/// Sample Slot options for Projects.
432#[derive(Serialize, Deserialize, PartialEq, Debug, Clone, Eq, Hash)]
433pub enum TrigCondition {
434    None,
435    /// > FILL is true (will activate the trig) when fill mode is active (see below).
436    Fill,
437    /// > ... true when FILL is not.
438    NotFill,
439    /// > PRE is true if the most recently evaluated trig condition on the same track was true.
440    Pre,
441    /// > ... true when PRE is not.
442    NotPre,
443    /// > true if the most recently evaluated trig condition on the neighbor track was true.
444    /// > The neighbor track is the track before the one being edited.
445    /// > For example, the neighbor track of track 4 is track 3. If no conditions exist on the
446    /// > neighbor track, the condition is false.
447    Nei,
448    /// > ... true when NEI is not.
449    NotNei,
450    /// > only true the first time the pattern play (when looped).
451    First,
452    /// > ... true when 1st is not.
453    NotFirst,
454    /// > probability condition. 1% chance of being true.
455    Percent1,
456    /// > probability condition. 2% chance of being true.
457    Percent2,
458    /// > probability condition. 4% chance of being true.
459    Percent4,
460    /// > probability condition. 6% chance of being true.
461    Percent6,
462    /// > probability condition. 9% chance of being true.
463    Percent9,
464    /// > probability condition. 13% chance of being true.
465    Percent13,
466    /// > probability condition. 19% chance of being true.
467    Percent19,
468    /// > probability condition. 25% chance of being true.
469    Percent25,
470    /// > probability condition. 33% chance of being true.
471    Percent33,
472    /// > probability condition. 41% chance of being true.
473    Percent41,
474    /// > probability condition. 50% chance of being true.
475    Percent50,
476    /// > probability condition. 59% chance of being true.
477    Percent59,
478    /// > probability condition. 67% chance of being true.
479    Percent67,
480    /// > probability condition. 75% chance of being true.
481    Percent75,
482    /// > probability condition. 81% chance of being true.
483    Percent81,
484    /// > probability condition. 87% chance of being true.
485    Percent87,
486    /// > probability condition. 91% chance of being true.
487    Percent91,
488    /// > probability condition. 94% chance of being true.
489    Percent94,
490    /// > probability condition. 96% chance of being true.
491    Percent96,
492    /// > probability condition. 98% chance of being true.
493    Percent98,
494    /// > probability condition. 99% chance of being true.
495    Percent99,
496    /// pattern loop 1 triggers, pattern loop 2 resets
497    PatternT1R2,
498    /// pattern loop 2 triggers, pattern loop 2 resets
499    PatternT2R2,
500    /// pattern loop 1 triggers, pattern loop 3 resets
501    PatternT1R3,
502    /// pattern loop 2 triggers, pattern loop 3 resets
503    PatternT2R3,
504    /// pattern loop 3 triggers, pattern loop 3 resets
505    PatternT3R3,
506    /// pattern loop 1 triggers, pattern loop 4 resets
507    PatternT1R4,
508    /// pattern loop 2 triggers, pattern loop 4 resets
509    PatternT2R4,
510    /// pattern loop 3 triggers, pattern loop 4 resets
511    PatternT3R4,
512    /// pattern loop 4 triggers, pattern loop 4 resets
513    PatternT4R4,
514    /// pattern loop 1 triggers, pattern loop 5 resets
515    PatternT1R5,
516    /// pattern loop 2 triggers, pattern loop 5 resets
517    PatternT2R5,
518    /// pattern loop 3 triggers, pattern loop 5 resets
519    PatternT3R5,
520    /// pattern loop 4 triggers, pattern loop 5 resets
521    PatternT4R5,
522    /// pattern loop 5 triggers, pattern loop 5 resets
523    PatternT5R5,
524    /// pattern loop 1 triggers, pattern loop 6 resets
525    PatternT1R6,
526    /// pattern loop 2 triggers, pattern loop 6 resets
527    PatternT2R6,
528    /// pattern loop 3 triggers, pattern loop 6 resets
529    PatternT3R6,
530    /// pattern loop 4 triggers, pattern loop 6 resets
531    PatternT4R6,
532    /// pattern loop 5 triggers, pattern loop 6 resets
533    PatternT5R6,
534    /// pattern loop 6 triggers, pattern loop 6 resets
535    PatternT6R6,
536    /// pattern loop 1 triggers, pattern loop 7 resets
537    PatternT1R7,
538    /// pattern loop 2 triggers, pattern loop 7 resets
539    PatternT2R7,
540    /// pattern loop 3 triggers, pattern loop 7 resets
541    PatternT3R7,
542    /// pattern loop 4 triggers, pattern loop 7 resets
543    PatternT4R7,
544    /// pattern loop 5 triggers, pattern loop 7 resets
545    PatternT5R7,
546    /// pattern loop 6 triggers, pattern loop 7 resets
547    PatternT6R7,
548    /// pattern loop 7 triggers, pattern loop 7 resets
549    PatternT7R7,
550    /// pattern loop 1 triggers, pattern loop 8 resets
551    PatternT1R8,
552    /// pattern loop 2 triggers, pattern loop 8 resets
553    PatternT2R8,
554    /// pattern loop 3 triggers, pattern loop 8 resets
555    PatternT3R8,
556    /// pattern loop 4 triggers, pattern loop 8 resets
557    PatternT4R8,
558    /// pattern loop 5 triggers, pattern loop 8 resets
559    PatternT5R8,
560    /// pattern loop 6 triggers, pattern loop 8 resets
561    PatternT6R8,
562    /// pattern loop 7 triggers, pattern loop 8 resets
563    PatternT7R8,
564    /// pattern loop 8 triggers, pattern loop 8 resets
565    PatternT8R8,
566}
567
568impl OptionEnumValueConvert for TrigCondition {
569    type T = TrigCondition;
570    type V = u8;
571
572    fn from_value(v: &Self::V) -> RBoxErr<Self::T> {
573        // read the essay for `AudioTrackTrigs.trig_timings_repeats_conditions`
574        // to understand why rem_euclid is used here
575        match v.rem_euclid(128) {
576            0 => Ok(TrigCondition::None),
577            1 => Ok(TrigCondition::Fill),
578            2 => Ok(TrigCondition::NotFill),
579            3 => Ok(TrigCondition::Pre),
580            4 => Ok(TrigCondition::NotPre),
581            5 => Ok(TrigCondition::Nei),
582            6 => Ok(TrigCondition::NotNei),
583            7 => Ok(TrigCondition::First),
584            8 => Ok(TrigCondition::NotFirst),
585            //
586            9 => Ok(TrigCondition::Percent1),
587            10 => Ok(TrigCondition::Percent2),
588            11 => Ok(TrigCondition::Percent4),
589            12 => Ok(TrigCondition::Percent6),
590            13 => Ok(TrigCondition::Percent9),
591            14 => Ok(TrigCondition::Percent13),
592            15 => Ok(TrigCondition::Percent19),
593            16 => Ok(TrigCondition::Percent25),
594            17 => Ok(TrigCondition::Percent33),
595            18 => Ok(TrigCondition::Percent41),
596            19 => Ok(TrigCondition::Percent50),
597            20 => Ok(TrigCondition::Percent59),
598            21 => Ok(TrigCondition::Percent67),
599            22 => Ok(TrigCondition::Percent75),
600            23 => Ok(TrigCondition::Percent81),
601            24 => Ok(TrigCondition::Percent87),
602            25 => Ok(TrigCondition::Percent91),
603            26 => Ok(TrigCondition::Percent94),
604            27 => Ok(TrigCondition::Percent96),
605            28 => Ok(TrigCondition::Percent98),
606            29 => Ok(TrigCondition::Percent99),
607            //
608            30 => Ok(TrigCondition::PatternT1R2),
609            31 => Ok(TrigCondition::PatternT2R2),
610            //
611            32 => Ok(TrigCondition::PatternT1R3),
612            33 => Ok(TrigCondition::PatternT2R3),
613            34 => Ok(TrigCondition::PatternT3R3),
614            //
615            35 => Ok(TrigCondition::PatternT1R4),
616            36 => Ok(TrigCondition::PatternT2R4),
617            37 => Ok(TrigCondition::PatternT3R4),
618            38 => Ok(TrigCondition::PatternT4R4),
619            //
620            39 => Ok(TrigCondition::PatternT1R5),
621            40 => Ok(TrigCondition::PatternT2R5),
622            41 => Ok(TrigCondition::PatternT3R5),
623            42 => Ok(TrigCondition::PatternT4R5),
624            43 => Ok(TrigCondition::PatternT5R5),
625            //
626            44 => Ok(TrigCondition::PatternT1R6),
627            45 => Ok(TrigCondition::PatternT2R6),
628            46 => Ok(TrigCondition::PatternT3R6),
629            47 => Ok(TrigCondition::PatternT4R6),
630            48 => Ok(TrigCondition::PatternT5R6),
631            49 => Ok(TrigCondition::PatternT6R6),
632            //
633            50 => Ok(TrigCondition::PatternT1R7),
634            51 => Ok(TrigCondition::PatternT2R7),
635            52 => Ok(TrigCondition::PatternT3R7),
636            53 => Ok(TrigCondition::PatternT4R7),
637            54 => Ok(TrigCondition::PatternT5R7),
638            55 => Ok(TrigCondition::PatternT6R7),
639            56 => Ok(TrigCondition::PatternT7R7),
640            //
641            57 => Ok(TrigCondition::PatternT1R8),
642            58 => Ok(TrigCondition::PatternT2R8),
643            59 => Ok(TrigCondition::PatternT3R8),
644            60 => Ok(TrigCondition::PatternT4R8),
645            61 => Ok(TrigCondition::PatternT5R8),
646            62 => Ok(TrigCondition::PatternT6R8),
647            63 => Ok(TrigCondition::PatternT7R8),
648            64 => Ok(TrigCondition::PatternT8R8),
649            //
650            _ => Err(OtToolsIoErrors::NoMatchingOptionEnumValue.into()),
651        }
652    }
653
654    fn value(&self) -> RBoxErr<Self::V> {
655        match self {
656            Self::None => Ok(0),
657            Self::Fill => Ok(1),
658            Self::NotFill => Ok(2),
659            Self::Pre => Ok(3),
660            Self::NotPre => Ok(4),
661            Self::Nei => Ok(5),
662            Self::NotNei => Ok(6),
663            Self::First => Ok(7),
664            Self::NotFirst => Ok(8),
665            Self::Percent1 => Ok(9),
666            Self::Percent2 => Ok(10),
667            Self::Percent4 => Ok(11),
668            Self::Percent6 => Ok(12),
669            Self::Percent9 => Ok(13),
670            Self::Percent13 => Ok(14),
671            Self::Percent19 => Ok(15),
672            Self::Percent25 => Ok(16),
673            Self::Percent33 => Ok(17),
674            Self::Percent41 => Ok(18),
675            Self::Percent50 => Ok(19),
676            Self::Percent59 => Ok(20),
677            Self::Percent67 => Ok(21),
678            Self::Percent75 => Ok(22),
679            Self::Percent81 => Ok(23),
680            Self::Percent87 => Ok(24),
681            Self::Percent91 => Ok(25),
682            Self::Percent94 => Ok(26),
683            Self::Percent96 => Ok(27),
684            Self::Percent98 => Ok(28),
685            Self::Percent99 => Ok(29),
686            Self::PatternT1R2 => Ok(30),
687            Self::PatternT2R2 => Ok(31),
688            Self::PatternT1R3 => Ok(32),
689            Self::PatternT2R3 => Ok(33),
690            Self::PatternT3R3 => Ok(34),
691            Self::PatternT1R4 => Ok(35),
692            Self::PatternT2R4 => Ok(36),
693            Self::PatternT3R4 => Ok(37),
694            Self::PatternT4R4 => Ok(38),
695            Self::PatternT1R5 => Ok(39),
696            Self::PatternT2R5 => Ok(40),
697            Self::PatternT3R5 => Ok(41),
698            Self::PatternT4R5 => Ok(42),
699            Self::PatternT5R5 => Ok(43),
700            Self::PatternT1R6 => Ok(44),
701            Self::PatternT2R6 => Ok(45),
702            Self::PatternT3R6 => Ok(46),
703            Self::PatternT4R6 => Ok(47),
704            Self::PatternT5R6 => Ok(48),
705            Self::PatternT6R6 => Ok(49),
706            Self::PatternT1R7 => Ok(50),
707            Self::PatternT2R7 => Ok(51),
708            Self::PatternT3R7 => Ok(52),
709            Self::PatternT4R7 => Ok(53),
710            Self::PatternT5R7 => Ok(54),
711            Self::PatternT6R7 => Ok(55),
712            Self::PatternT7R7 => Ok(56),
713            Self::PatternT1R8 => Ok(57),
714            Self::PatternT2R8 => Ok(58),
715            Self::PatternT3R8 => Ok(59),
716            Self::PatternT4R8 => Ok(60),
717            Self::PatternT5R8 => Ok(61),
718            Self::PatternT6R8 => Ok(62),
719            Self::PatternT7R8 => Ok(63),
720            Self::PatternT8R8 => Ok(64),
721        }
722    }
723}
724
725/// Track trigs assigned on an Audio Track within a Pattern
726#[derive(PartialEq, Debug, Serialize, Deserialize, Clone)]
727pub struct AudioTrackTrigs {
728    /// Header data section
729    ///
730    /// example data:
731    /// ```text
732    /// TRAC
733    /// 54 52 41 43
734    /// ```
735    #[serde(with = "BigArray")]
736    pub header: [u8; 4],
737
738    /// Unknown data.
739    #[serde(with = "BigArray")]
740    pub unknown_1: [u8; 4],
741
742    /// The zero indexed track number
743    pub track_id: u8,
744
745    /// Trig masks contain the Trig step locations for different trig types
746    pub trig_masks: AudioTrackTrigMasks,
747
748    /// The scale of this Audio Track in Per Track Pattern mode.
749    pub scale_per_track_mode: TrackPerTrackModeScale,
750
751    /// Amount of swing when a Swing Trig is active for the Track.
752    /// Maximum is `30` (`80` on device), minimum is `0` (`50` on device).
753    pub swing_amount: u8,
754
755    /// Pattern settings for this Audio Track
756    pub pattern_settings: TrackPatternSettings,
757
758    /// Unknown data.
759    pub unknown_2: u8,
760
761    /// Parameter-Lock data for all Trigs.
762    // note -- stack overflow if tring to use #[serde(with = "BigArray")]
763    pub plocks: Box<Array<AudioTrackParameterLocks, 64>>,
764
765    /// What the hell is this field?!?!
766    /// It **has to** be something to do with trigs, but i have no idea what it could be.
767    #[serde(with = "BigArray")]
768    pub unknown_3: [u8; 64],
769
770    /// Trig Offsets, Trig Counts and Trig Conditions
771    /// ====
772    /// This is ..... slightly frustrating.
773    ///
774    /// This 64 length array consisting of a pair of bytes for each array element hold three
775    /// data references... Trig Cunts and Trig Conditions use the two bytes independently,
776    /// so they're easier to explain first
777    ///
778    /// Trig Counts and Trig Conditions
779    /// ====
780    ///
781    /// Trig Counts and Trig Conditions data is interleaved for each trig.
782    /// For Trig position 1, array index 0 is the count value and array index 1 is the Trig
783    /// Condition.
784    ///
785    /// For trig counts (1st byte), the value (zero-indexed) is multiplied by 32.
786    /// - 8 trig counts (7 repeats) --> 7 * 3 = 224
787    /// - 4 trig counts (3 repeats) -- 3 * 32 = 96
788    /// - 1 trig counts (0 repeats) -- 0 * 32 = 0
789    ///
790    /// For conditionals, see the `TrigCondition` enum and associated traits for more details.
791    /// The maximum value for a Trig Condition byte is 64.
792    ///
793    /// ```rust
794    /// // no trig micro-timings at all
795    /// [
796    ///     // trig 1
797    ///     [
798    ///         0,   // trig counts (number)
799    ///         0,   // trig condition (enum rep)
800    ///     ],
801    ///     // trig 2
802    ///     [
803    ///         224, // trig counts (max value)
804    ///         64,  // trig condition (max value)
805    ///     ],
806    ///     // trig 3
807    ///     [
808    ///         32,  // trig counts (minimum non-zero value)
809    ///         1,   // trig condition (minimum non-zero value)
810    ///     ],
811    ///     // ... and so on
812    /// ];
813    /// ```
814    ///
815    /// Trig Offsets
816    /// ====
817    ///
818    /// Trig Offset values use both of these interleaved bytes on top of the
819    /// trig repeat and trig condition values... Which makes life more complex
820    /// and somewhat frustrating.
821    ///
822    /// Inspected values
823    /// - -23/384 -> 1st byte 20, 2nd byte 128
824    /// - -1/32 -> 1st byte 26, 2nd byte 0
825    /// - -1/64 -> 1st byte 29, 2nd byte 0
826    /// - -1/128 -> 1st byte 30, 2nd byte 128
827    /// - 1/128 -> 1st byte 1, 2nd byte 128
828    /// - 1/64 -> 1st byte 3, 2nd byte 0
829    /// - 1/32 -> 1st byte 6, 2nd byte 0
830    /// - 23/384 -> 1st byte 11, 2nd byte 128
831    ///
832    /// #### 1st byte
833    /// The 1st byte only has 31 possible values: 255 - 224 (trig count max) = 31.
834    /// So it makes sense sort of that this is a mask? I guess?
835    ///
836    /// #### 2nd byte
837    /// From what I can tell, the second offset byte is either 0 or 128.
838    /// So a 2nd byte for an offset adjusted trig with a `8:8` trig condition is either
839    /// - 128 + 64 = 192
840    /// - 0 + 64 = 64
841    ///
842    /// So you will need to a `x.rem_euclid(128)` somewhere if you want to parse this.
843    ///
844    /// Combining the trig offset with trig count and trig conditions, we end up with
845    /// ```rust
846    /// [
847    ///     // trig one, -23/384 offset with 1x trig count and None condition
848    ///     [
849    ///         20,  // 20 + (32 * 0)
850    ///         128, // 128 + 0
851    ///     ],
852    ///     // trig two, -23/384 offset with 2x trig count and Fill condition
853    ///     [
854    ///         52,  // 20 + (32 * 1)
855    ///         129, // 128 + 1
856    ///     ],
857    ///     // trig three, -23/384 offset with 3x trig count and Fill condition
858    ///     [
859    ///         84,  // 20 + (32 * 2)
860    ///         129, // 128 + 1
861    ///     ],
862    ///     // trig four, -23/384 offset with 3x trig count and NotFill condition
863    ///     [
864    ///         84,  // 20 + (32 * 2)
865    ///         130, // 128 + 2
866    ///     ],
867    ///     // trig five, +1/32 offset with 2x trig count and Fill condition
868    ///     [
869    ///         38,  // 6 + (32 * 1)
870    ///         1,   // 0 + 1
871    ///     ],
872    ///     // trig six, +1/32 offset with 3x trig count and Fill condition
873    ///     [
874    ///         70,  // 6 + (32 * 2)
875    ///         1,   // 0 + 1
876    ///     ],
877    ///     // trig seven, +1/32 offset with 3x trig count and NotFill condition
878    ///     [
879    ///         70,  // 6 + (32 * 2)
880    ///         2,   // 0 + 2
881    ///     ],
882    ///     // .... and so on
883    /// ];
884    /// ```
885    ///
886    /// #### Extending pages and offsets
887    ///
888    /// If you have a trig offset on Trig 1 with only one pattern page activated,
889    /// the trig offsets for Trig 1 are replicated over the relevant trig
890    /// positions for each first trig in the inactive pages in this array.
891    ///
892    /// So, for a 1/32 offset on trig 1 with only one page active, you get the
893    /// following values showing up in this array:
894    /// - pair of bytes at array index 15 -> 1/32
895    /// - pair of bytes at array index 31 -> 1/32
896    /// - pair of bytes at array index 47 -> 1/32
897    ///
898    /// This does not happen for offset values at any other trig position
899    /// (from what I can tell in my limited testing -- trig values 2-4 and 9-11
900    /// inclusive are not replicated in the same way).
901    ///
902    /// This 'replicating trig offset values over unused pages' behaviour does
903    /// not happen for trig counts. I haven't tested whether this applies to trig
904    /// conditions yet.
905    ///
906    /// It seems that this behaviour could be to make sure the octatack plays
907    /// correctly offset trigs when you extend a page live, i.e. when extending
908    /// a one-page pattern to a two-page pattern, if there is a negative offset
909    /// value there the octatrack will need to play the offset trig before the
910    /// first page has completed.
911    ///
912    /// Or it could be a bug :shrug:
913    #[serde(with = "BigArray")]
914    pub trig_offsets_repeats_conditions: [[u8; 2]; 64],
915}
916
917impl AudioTrackTrigs {
918    /// WARNING: This `default` method is not from the `Default` trait, as we
919    /// cannot use a default struct instance to create an array/vector of
920    /// midi track trig data --> the individual default depends on their
921    /// position in the final array!
922    ///
923    /// In the future, maybe it might be worth creating `default_with` and
924    /// `defaults_with` methods to deal with this. But it's not clear they are
925    /// needed just yet. 80/20.
926    fn default(id: u8) -> Self {
927        assert!(id < 8);
928        Self {
929            header: AUDIO_TRACK_HEADER,
930            unknown_1: from_fn(|_| 0),
931            track_id: id,
932            trig_masks: AudioTrackTrigMasks::default(),
933            scale_per_track_mode: TrackPerTrackModeScale::default(),
934            swing_amount: 0,
935            pattern_settings: TrackPatternSettings::default(),
936            unknown_2: 0,
937            plocks: AudioTrackParameterLocks::defaults(),
938            unknown_3: from_fn(|_| 0),
939            trig_offsets_repeats_conditions: from_fn(|_| [0, 0]),
940        }
941    }
942
943    /// WARNING: This `defaults` method is not from the `Defaults` trait, as we
944    /// cannot use a default struct instance to create an array/vector of
945    /// machine slots data --> the individual default depends on their position
946    /// in the final array!
947    ///
948    /// In the future, maybe it might be worth creating `default_with` and
949    /// `defaults_with` methods to deal with this. But it's not clear they are
950    /// needed just yet. 80/20.
951    pub fn defaults<const N: usize>() -> [Self; N] {
952        from_fn(|x| Self::default(x as u8))
953    }
954}
955
956impl CheckHeader for AudioTrackTrigs {
957    fn check_header(&self) -> bool {
958        self.header == AUDIO_TRACK_HEADER
959    }
960}
961
962/// MIDI Track Trig masks.
963/// Can be converted into an array of booleans using the `get_track_trigs_from_bitmasks` function.
964/// See `AudioTrackTrigMasks` for more information.
965///
966/// Trig mask arrays have data stored in this order, which is slightly confusing (pay attention to the difference with 7 + 8!):
967/// 1. 1st half of the 4th page
968/// 2. 2nd half of the 4th page
969/// 3. 1st half of the 3rd page
970/// 4. 2nd half of the 3rd page
971/// 5. 1st half of the 2nd page
972/// 6. 2nd half of the 2nd page
973/// 7. 2nd half of the 1st page
974/// 8. 1st half of the 1st page
975#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Copy)]
976pub struct MidiTrackTrigMasks {
977    /// Note Trig masks.
978    #[serde(with = "BigArray")]
979    pub trigger: [u8; 8],
980
981    /// Trigless Trig masks.
982    #[serde(with = "BigArray")]
983    pub trigless: [u8; 8],
984
985    /// Parameter Lock Trig masks.
986    /// Note this only stores data for exclusive parameter lock *trigs* (light green trigs).
987    #[serde(with = "BigArray")]
988    pub plock: [u8; 8],
989
990    /// Swing trigs mask.
991    #[serde(with = "BigArray")]
992    pub swing: [u8; 8],
993
994    /// this is a block of 8, so looks like a trig mask for tracks,
995    /// but I can't think of what it could be.
996    #[serde(with = "BigArray")]
997    pub unknown: [u8; 8],
998}
999
1000impl Default for MidiTrackTrigMasks {
1001    fn default() -> Self {
1002        Self {
1003            trigger: from_fn(|_| 0),
1004            trigless: from_fn(|_| 0),
1005            plock: from_fn(|_| 0),
1006            swing: from_fn(|_| 170),
1007            unknown: from_fn(|_| 0),
1008        }
1009    }
1010}
1011
1012/// Track trigs assigned on an Audio Track within a Pattern
1013#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
1014pub struct MidiTrackTrigs {
1015    /// Header data section
1016    ///
1017    /// example data:
1018    /// ```text
1019    /// MTRA
1020    /// 4d 54 52 41
1021    /// ```
1022    #[serde(with = "BigArray")]
1023    pub header: [u8; 4],
1024
1025    /// Unknown data.
1026    #[serde(with = "BigArray")]
1027    pub unknown_1: [u8; 4],
1028
1029    /// The zero indexed track number
1030    pub track_id: u8,
1031
1032    /// MIDI Track Trig masks contain the Trig step locations for different trig types
1033    pub trig_masks: MidiTrackTrigMasks,
1034
1035    /// The scale of this MIDI Track in Per Track Pattern mode.
1036    pub scale_per_track_mode: TrackPerTrackModeScale,
1037
1038    /// Amount of swing when a Swing Trig is active for the Track.
1039    /// Maximum is `30` (`80` on device), minimum is `0` (`50` on device).
1040    pub swing_amount: u8,
1041
1042    /// Pattern settings for this MIDI Track
1043    pub pattern_settings: TrackPatternSettings,
1044
1045    /// trig properties -- p-locks etc.
1046    /// the big `0xff` value block within tracks basically.
1047    /// 32 bytes per trig -- 6x parameters for 5x pages plus 2x extra fields at the end.
1048    ///
1049    /// For audio tracks, the 2x extra fields at the end are for sample locks,
1050    /// but there's no such concept for MIDI tracks.
1051    /// It seems like Elektron devs reused their data structures for P-Locks on both Audio + MIDI tracks.
1052    // note -- stack overflow if tring to use #[serde(with = "BigArray")]
1053    pub plocks: Box<Array<MidiTrackParameterLocks, 64>>,
1054
1055    /// See the documentation for `AudioTrackTrigs` on how this field works.
1056    #[serde(with = "BigArray")]
1057    pub trig_offsets_repeats_conditions: [[u8; 2]; 64],
1058}
1059
1060impl MidiTrackTrigs {
1061    /// WARNING: This `default` method is not from the `Default` trait, as we
1062    /// cannot use a default struct instance to create an array/vector of
1063    /// midi track trig data --> the individual default depends on their
1064    /// position in the final array!
1065    ///
1066    /// In the future, maybe it might be worth creating `default_with` and
1067    /// `defaults_with` methods to deal with this. But it's not clear they are
1068    /// needed just yet. 80/20.
1069    fn default(id: u8) -> Self {
1070        // TODO: create an ot-tools error
1071        assert!(id < 8);
1072        Self {
1073            header: MIDI_TRACK_HEADER,
1074            unknown_1: from_fn(|_| 0),
1075            track_id: id,
1076            trig_masks: MidiTrackTrigMasks::default(),
1077            scale_per_track_mode: TrackPerTrackModeScale::default(),
1078            swing_amount: 0,
1079            pattern_settings: TrackPatternSettings::default(),
1080            plocks: MidiTrackParameterLocks::defaults(),
1081            trig_offsets_repeats_conditions: from_fn(|_| [0, 0]),
1082        }
1083    }
1084
1085    /// WARNING: This `defaults` method is not from the `Defaults` trait, as we
1086    /// cannot use a default struct instance to create an array/vector of
1087    /// machine slots data --> the individual default depends on their position
1088    /// in the final array!
1089    ///
1090    /// In the future, maybe it might be worth creating `default_with` and
1091    /// `defaults_with` methods to deal with this. But it's not clear they are
1092    /// needed just yet. 80/20.
1093    pub fn defaults<const N: usize>() -> [Self; N] {
1094        from_fn(|x| Self::default(x as u8))
1095    }
1096}
1097
1098impl CheckHeader for MidiTrackTrigs {
1099    fn check_header(&self) -> bool {
1100        self.header == MIDI_TRACK_HEADER
1101    }
1102}
1103
1104/// Pattern level scaling settings.
1105/// Some of these settings still apply when the pattern is in Per-Track scaling mode.
1106#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
1107pub struct PatternScaleSettings {
1108    /// Multiply this value by `master_len_per_track` to get
1109    /// the real Master Length in Per Track Pattern mode.
1110    ///
1111    /// This field must be set to `255` when Master Length in
1112    /// Per Track Pattern mode is set to `INF`.
1113    ///
1114    /// ```text
1115    /// 0: From 2 steps to 255 steps.
1116    /// 1: From 256 steps to 511 steps.
1117    /// 2: From 512 steps to 767 steps.
1118    /// 3: From 768 steps to 1023 steps.
1119    /// 4: 1024 steps only.
1120    /// 255: `INF`.
1121    /// ```
1122    pub master_len_per_track_multiplier: u8,
1123
1124    /// Master Length in Per Track Pattern mode.
1125    /// Must multiply this by multiplier like this `(x + 1) * (mult + 1)` to get the real number.
1126    ///
1127    /// This field must be set to `255` when Master Length in
1128    /// Per Track Pattern mode is set to `INF`.
1129    ///
1130    /// Minimum value is 2 when the multiplier equals 0.
1131    pub master_len_per_track: u8,
1132
1133    /// The Audio Track's Scale when Pattern is in Per Track mode.
1134    ///
1135    /// Options
1136    /// ```text
1137    /// 0 -> 2x
1138    /// 1 -> 3/2x
1139    /// 2 -> 1x (Default)
1140    /// 3 -> 3/4x
1141    /// 4 -> 1/2x
1142    /// 5 -> 1/4x
1143    /// 6 -> 1/8x
1144    /// ```
1145    pub master_scale_per_track: u8,
1146
1147    /// Master Pattern Length.
1148    /// Determines Pattern Length for all Tracks when NOT in Per Track mode.
1149    pub master_len: u8,
1150
1151    /// Master Pattern playback multiplier.
1152    ///
1153    /// Options
1154    /// ```text
1155    /// 0 -> 2x
1156    /// 1 -> 3/2x
1157    /// 2 -> 1x (Default)
1158    /// 3 -> 3/4x
1159    /// 4 -> 1/2x
1160    /// 5 -> 1/4x
1161    /// 6 -> 1/8x
1162    /// ```
1163    pub master_scale: u8,
1164
1165    /// Scale mode for the Pattern.
1166    ///
1167    /// Options
1168    /// ```text
1169    /// NORMAL: 0 (Default)
1170    /// PER TRACK: 1
1171    /// ```
1172    pub scale_mode: u8,
1173}
1174
1175impl Default for PatternScaleSettings {
1176    fn default() -> Self {
1177        Self {
1178            master_len_per_track_multiplier: 0,
1179            master_len_per_track: 16,
1180            master_scale_per_track: 2,
1181            master_len: 16,
1182            master_scale: 2,
1183            scale_mode: 0,
1184        }
1185    }
1186}
1187
1188/// Chaining behaviour for the pattern.
1189#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
1190pub struct PatternChainBehavior {
1191    /// When `use_project_setting` field is set to `1`/`true`
1192    /// this field should be set to `N/A` with a value of `255`.
1193    pub use_pattern_setting: u8,
1194
1195    /// Pattern Chain Behaviour -- Use the Project level setting for chain
1196    /// behaviour and disable any Pattern level chaining behaviour.
1197    /// Numeric Boolean.
1198    /// When this is `1` the `use_pattern_setting` should be set to `255`.
1199    pub use_project_setting: u8,
1200}
1201
1202// allow the verbose implementation to keep things
1203// - (a) standardised across all types
1204// - (b) easier for non-rustaceans to follow when reading through data structures
1205#[allow(clippy::derivable_impls)]
1206impl Default for PatternChainBehavior {
1207    fn default() -> Self {
1208        Self {
1209            use_pattern_setting: 0,
1210            use_project_setting: 0,
1211        }
1212    }
1213}
1214
1215/// A pattern of trigs stored in the bank.
1216#[derive(PartialEq, Debug, Serialize, Deserialize, Clone, DefaultsAsBoxedBigArray)]
1217pub struct Pattern {
1218    /// Header indicating start of pattern section
1219    ///
1220    /// example data:
1221    /// ```text
1222    /// PTRN....
1223    /// 50 54 52 4e 00 00 00 00
1224    /// ```
1225    #[serde(with = "BigArray")]
1226    pub header: [u8; 8],
1227
1228    /// Audio Track data
1229    #[serde(with = "BigArray")]
1230    pub audio_track_trigs: [AudioTrackTrigs; 8],
1231
1232    /// MIDI Track data
1233    #[serde(with = "BigArray")]
1234    pub midi_track_trigs: [MidiTrackTrigs; 8],
1235
1236    /// Pattern scaling controls and settings
1237    pub scale: PatternScaleSettings,
1238
1239    /// Pattern chaining behaviour and settings
1240    pub chain_behaviour: PatternChainBehavior,
1241
1242    /// Unknown data.
1243    pub unknown: u8,
1244
1245    /// The Part of a Bank assigned to a Pattern.
1246    /// Part 1 = 0; Part 2 = 1; Part 3 = 2; Part 4 = 3.
1247    /// Credit to [@sezare56 on elektronauts for catching this one](https://www.elektronauts.com/t/octalib-a-simple-octatrack-librarian/225192/27)
1248    pub part_assignment: u8,
1249
1250    /// Pattern setting for Tempo.
1251    ///
1252    /// The Tempo value is split across both `tempo_1` and `tempo_2`.
1253    /// Yet to figure out how they relate to each other.
1254    ///
1255    /// Value of 120 BPM is 11 for this field.
1256    /// Value of 30 BPM is 2 for this field.
1257    pub tempo_1: u8,
1258
1259    /// Pattern setting for Tempo.
1260    ///
1261    /// The Tempo value is split across both `tempo_1` and `tempo_2`.
1262    /// Tet to figure out how they relate to each other.
1263    ///
1264    /// Value of 120 BPM is `64` for this field.
1265    /// Value of 30 BPM is `208` for this field.
1266    pub tempo_2: u8,
1267}
1268
1269impl Default for Pattern {
1270    fn default() -> Self {
1271        Self {
1272            header: PATTERN_HEADER,
1273            audio_track_trigs: AudioTrackTrigs::defaults(),
1274            midi_track_trigs: MidiTrackTrigs::defaults(),
1275            scale: PatternScaleSettings::default(),
1276            chain_behaviour: PatternChainBehavior::default(),
1277            unknown: 0,
1278            part_assignment: 0,
1279            // **I believe** these two mask values make the tempo 120.0 BPM
1280            // don't quote me on that though
1281            tempo_1: 11,
1282            tempo_2: 64,
1283        }
1284    }
1285}
1286
1287impl CheckHeader for Pattern {
1288    fn check_header(&self) -> bool {
1289        self.header == PATTERN_HEADER
1290    }
1291}
1292
1293#[cfg(test)]
1294#[allow(unused_imports)]
1295mod test {
1296
1297    mod track_trig_defaults {
1298
1299        mod audio {
1300            use crate::banks::patterns::AudioTrackTrigs;
1301            use crate::RBoxErr;
1302
1303            fn wrap_err(track_id: u8) -> RBoxErr<AudioTrackTrigs> {
1304                Ok(AudioTrackTrigs::default(track_id))
1305            }
1306
1307            #[test]
1308            fn ok_track_id_zero() {
1309                assert!(wrap_err(0).is_ok());
1310            }
1311
1312            #[test]
1313            fn ok_track_id_seven() {
1314                assert!(wrap_err(7).is_ok());
1315            }
1316
1317            // todo: proper error handling (don't use an assert!() in the default method)
1318            #[test]
1319            #[should_panic]
1320            fn err_default_track_id_eight() {
1321                assert!(wrap_err(8).is_err());
1322            }
1323        }
1324        mod midi {
1325            use crate::banks::patterns::MidiTrackTrigs;
1326            use crate::RBoxErr;
1327
1328            fn wrap_err(track_id: u8) -> RBoxErr<MidiTrackTrigs> {
1329                Ok(MidiTrackTrigs::default(track_id))
1330            }
1331
1332            #[test]
1333            fn ok_track_id_zero() {
1334                assert!(wrap_err(0).is_ok());
1335            }
1336
1337            #[test]
1338            fn ok_track_id_seven() {
1339                assert!(wrap_err(7).is_ok());
1340            }
1341
1342            // todo: proper error handling (don't use an assert!() in the default method)
1343            #[test]
1344            #[should_panic]
1345            fn err_default_track_id_eight() {
1346                assert!(wrap_err(8).is_err());
1347            }
1348        }
1349    }
1350    mod trig_bitmasks {
1351
1352        #[test]
1353        fn test_all_track_trigs_from_bitmasks() {
1354            let x: [u8; 8] = std::array::from_fn(|_| 255);
1355            let y: [bool; 64] = std::array::from_fn(|_| true);
1356
1357            assert_eq!(
1358                crate::banks::patterns::get_track_trigs_from_bitmasks(&x).unwrap(),
1359                y
1360            )
1361        }
1362
1363        #[test]
1364        fn test_no_track_trigs_from_bitmasks() {
1365            let x: [u8; 8] = std::array::from_fn(|_| 0);
1366            let y: [bool; 64] = std::array::from_fn(|_| false);
1367
1368            assert_eq!(
1369                crate::banks::patterns::get_track_trigs_from_bitmasks(&x).unwrap(),
1370                y
1371            )
1372        }
1373
1374        #[test]
1375        fn test_halfpage_trig_bitmask_unmask_0() {
1376            assert_eq!(
1377                crate::banks::patterns::get_halfpage_trigs_from_bitmask_value(&0).unwrap(),
1378                [false, false, false, false, false, false, false, false],
1379            );
1380        }
1381
1382        #[test]
1383        fn test_halfpage_trig_bitmask_unmask_1() {
1384            assert_eq!(
1385                crate::banks::patterns::get_halfpage_trigs_from_bitmask_value(&1).unwrap(),
1386                [true, false, false, false, false, false, false, false],
1387            );
1388        }
1389        #[test]
1390        fn test_halfpage_trig_bitmask_unmask_2() {
1391            assert_eq!(
1392                crate::banks::patterns::get_halfpage_trigs_from_bitmask_value(&2).unwrap(),
1393                [false, true, false, false, false, false, false, false],
1394            );
1395        }
1396
1397        #[test]
1398        fn test_halfpage_trig_bitmask_unmask_4() {
1399            assert_eq!(
1400                crate::banks::patterns::get_halfpage_trigs_from_bitmask_value(&4).unwrap(),
1401                [false, false, true, false, false, false, false, false],
1402            );
1403        }
1404
1405        #[test]
1406        fn test_halfpage_trig_bitmask_unmask_8() {
1407            assert_eq!(
1408                crate::banks::patterns::get_halfpage_trigs_from_bitmask_value(&8).unwrap(),
1409                [false, false, false, true, false, false, false, false],
1410            );
1411        }
1412
1413        #[test]
1414        fn test_halfpage_trig_bitmask_unmask_16() {
1415            assert_eq!(
1416                crate::banks::patterns::get_halfpage_trigs_from_bitmask_value(&16).unwrap(),
1417                [false, false, false, false, true, false, false, false],
1418            );
1419        }
1420
1421        #[test]
1422        fn test_halfpage_trig_bitmask_unmask_32() {
1423            assert_eq!(
1424                crate::banks::patterns::get_halfpage_trigs_from_bitmask_value(&32).unwrap(),
1425                [false, false, false, false, false, true, false, false],
1426            );
1427        }
1428
1429        #[test]
1430        fn test_halfpage_trig_bitmask_unmask_64() {
1431            assert_eq!(
1432                crate::banks::patterns::get_halfpage_trigs_from_bitmask_value(&64).unwrap(),
1433                [false, false, false, false, false, false, true, false],
1434            );
1435        }
1436
1437        #[test]
1438        fn test_halfpage_trig_bitmask_unmask_128() {
1439            assert_eq!(
1440                crate::banks::patterns::get_halfpage_trigs_from_bitmask_value(&128).unwrap(),
1441                [false, false, false, false, false, false, false, true],
1442            );
1443        }
1444
1445        #[test]
1446        fn test_halfpage_trig_bitmask_unmask_3() {
1447            assert_eq!(
1448                crate::banks::patterns::get_halfpage_trigs_from_bitmask_value(&3).unwrap(),
1449                [true, true, false, false, false, false, false, false],
1450            );
1451        }
1452
1453        #[test]
1454        fn test_halfpage_trig_bitmask_unmask_7() {
1455            assert_eq!(
1456                crate::banks::patterns::get_halfpage_trigs_from_bitmask_value(&7).unwrap(),
1457                [true, true, true, false, false, false, false, false],
1458            );
1459        }
1460
1461        #[test]
1462        fn test_halfpage_trig_bitmask_unmask_15() {
1463            assert_eq!(
1464                crate::banks::patterns::get_halfpage_trigs_from_bitmask_value(&15).unwrap(),
1465                [true, true, true, true, false, false, false, false],
1466            );
1467        }
1468
1469        #[test]
1470        fn test_halfpage_trig_bitmask_unmask_31() {
1471            assert_eq!(
1472                crate::banks::patterns::get_halfpage_trigs_from_bitmask_value(&31).unwrap(),
1473                [true, true, true, true, true, false, false, false],
1474            );
1475        }
1476
1477        #[test]
1478        fn test_halfpage_trig_bitmask_unmask_63() {
1479            assert_eq!(
1480                crate::banks::patterns::get_halfpage_trigs_from_bitmask_value(&63).unwrap(),
1481                [true, true, true, true, true, true, false, false],
1482            );
1483        }
1484
1485        #[test]
1486        fn test_halfpage_trig_bitmask_unmask_127() {
1487            assert_eq!(
1488                crate::banks::patterns::get_halfpage_trigs_from_bitmask_value(&127).unwrap(),
1489                [true, true, true, true, true, true, true, false],
1490            );
1491        }
1492
1493        #[test]
1494        fn test_halfpage_trig_bitmask_unmask_255() {
1495            assert_eq!(
1496                crate::banks::patterns::get_halfpage_trigs_from_bitmask_value(&255).unwrap(),
1497                [true, true, true, true, true, true, true, true],
1498            );
1499        }
1500    }
1501
1502    mod integrity {
1503        mod pattern {
1504            // valid header: [0x50, 0x54, 0x52, 0x4e, 0x00, 0x00, 0x00, 0x00];
1505            use crate::banks::patterns::Pattern;
1506            use crate::CheckHeader;
1507
1508            #[test]
1509            fn true_valid_header() {
1510                let pattern = Pattern::default();
1511                assert!(pattern.check_header());
1512            }
1513
1514            #[test]
1515            fn false_invalid_header() {
1516                let mut pattern = Pattern::default();
1517                pattern.header[0] = 0x01;
1518                pattern.header[1] = 0x01;
1519                pattern.header[7] = 0x50;
1520                assert!(!pattern.check_header());
1521            }
1522        }
1523        mod audio_track_trigs {
1524            use crate::banks::patterns::AudioTrackTrigs;
1525            use crate::CheckHeader;
1526
1527            #[test]
1528            fn true_valid_header() {
1529                let trigs = AudioTrackTrigs::default(0);
1530                assert!(trigs.check_header());
1531            }
1532
1533            #[test]
1534            fn false_invalid_header() {
1535                let mut trigs = AudioTrackTrigs::default(0);
1536                trigs.header[0] = 0x01;
1537                trigs.header[1] = 0x01;
1538                trigs.header[2] = 0x50;
1539                assert!(!trigs.check_header());
1540            }
1541        }
1542        mod midi_track_trigs {
1543            use crate::banks::patterns::MidiTrackTrigs;
1544            use crate::CheckHeader;
1545
1546            #[test]
1547            fn true_valid_header() {
1548                let trigs = MidiTrackTrigs::default(0);
1549                assert!(trigs.check_header());
1550            }
1551
1552            #[test]
1553            fn false_invalid_header() {
1554                let mut trigs = MidiTrackTrigs::default(0);
1555                trigs.header[0] = 0x01;
1556                trigs.header[1] = 0x01;
1557                trigs.header[2] = 0x50;
1558                assert!(!trigs.check_header());
1559            }
1560        }
1561    }
1562}