ot_tools_io/
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::{CheckHeader, DefaultsArrayBoxed, OptionEnumValueConvert, OtToolsIoErrors};
8use ot_tools_io_derive::DefaultsAsBoxedBigArray;
9use std::array::from_fn;
10
11use crate::parts::{
12    AudioTrackAmpParamsValues, AudioTrackFxParamsValues, LfoParamsValues, MidiTrackArpParamsValues,
13    MidiTrackCc1ParamsValues, MidiTrackCc2ParamsValues, MidiTrackMidiParamsValues,
14};
15use crate::RBoxErr;
16use serde::{Deserialize, Serialize};
17use serde_big_array::{Array, BigArray};
18
19const PATTERN_HEADER: [u8; 8] = [0x50, 0x54, 0x52, 0x4e, 0x00, 0x00, 0x00, 0x00];
20
21/// Header array for a MIDI track section in binary data files: `MTRA`
22const MIDI_TRACK_HEADER: [u8; 4] = [0x4d, 0x54, 0x52, 0x41];
23
24/// Header array for a MIDI track section in binary data files: `TRAC`
25const AUDIO_TRACK_HEADER: [u8; 4] = [0x54, 0x52, 0x41, 0x43];
26
27/// A Trig's parameter locks on the Playback/Machine page for an Audio Track.
28#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Copy)]
29pub struct AudioTrackParameterLockPlayback {
30    pub param1: u8,
31    pub param2: u8,
32    pub param3: u8,
33    pub param4: u8,
34    pub param5: u8,
35    pub param6: u8,
36}
37
38impl Default for AudioTrackParameterLocks {
39    fn default() -> Self {
40        // 255 -> disabled
41
42        // NOTE: the `part.rs` `default` methods for each of these type has
43        // fields all set to the correct defaults for the TRACK view, not p-lock
44        // trigS. So don't try and use the type's `default` method here as you
45        // will end up with a bunch of p-locks on trigs for all the default
46        // values. (Although maybe that's a desired feature for some workflows).
47
48        // Yes, this comment is duplicated below. It is to make sur you've seen
49        // it.
50        Self {
51            machine: AudioTrackParameterLockPlayback {
52                param1: 255,
53                param2: 255,
54                param3: 255,
55                param4: 255,
56                param5: 255,
57                param6: 255,
58            },
59            lfo: LfoParamsValues {
60                spd1: 255,
61                spd2: 255,
62                spd3: 255,
63                dep1: 255,
64                dep2: 255,
65                dep3: 255,
66            },
67            amp: AudioTrackAmpParamsValues {
68                atk: 255,
69                hold: 255,
70                rel: 255,
71                vol: 255,
72                bal: 255,
73                unused: 255,
74            },
75            fx1: AudioTrackFxParamsValues {
76                param_1: 255,
77                param_2: 255,
78                param_3: 255,
79                param_4: 255,
80                param_5: 255,
81                param_6: 255,
82            },
83            fx2: AudioTrackFxParamsValues {
84                param_1: 255,
85                param_2: 255,
86                param_3: 255,
87                param_4: 255,
88                param_5: 255,
89                param_6: 255,
90            },
91            static_slot_id: 255,
92            flex_slot_id: 255,
93        }
94    }
95}
96
97/// A single trig's parameter locks on an Audio Track.
98#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Copy, DefaultsAsBoxedBigArray)]
99pub struct AudioTrackParameterLocks {
100    pub machine: AudioTrackParameterLockPlayback,
101    pub lfo: LfoParamsValues,
102    pub amp: AudioTrackAmpParamsValues,
103    pub fx1: AudioTrackFxParamsValues,
104    pub fx2: AudioTrackFxParamsValues,
105    /// P-Lock to change an audio track's static machine sample slot assignment per trig
106    pub static_slot_id: u8,
107    /// P-Lock to change an audio track's flex machine sample slot assignment per trig
108    pub flex_slot_id: u8,
109}
110
111/// MIDI Track parameter locks.
112#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Copy, DefaultsAsBoxedBigArray)]
113pub struct MidiTrackParameterLocks {
114    pub midi: MidiTrackMidiParamsValues,
115    pub lfo: LfoParamsValues,
116    pub arp: MidiTrackArpParamsValues,
117    pub ctrl1: MidiTrackCc1ParamsValues,
118    pub ctrl2: MidiTrackCc2ParamsValues,
119
120    #[serde(with = "BigArray")]
121    unknown: [u8; 2],
122}
123
124impl Default for MidiTrackParameterLocks {
125    fn default() -> Self {
126        // 255 -> disabled
127
128        // NOTE: the `part.rs` `default` methods for each of these type has
129        // fields all set to the correct defaults for the TRACK view, not p-lock
130        // trigS. So don't try and use the type's `default` method here as you
131        // will end up with a bunch of p-locks on trigs for all the default
132        // values. (Although maybe that's a desired feature for some workflows).
133
134        // Yes, this comment is duplicated above. It is to make sur you've seen
135        // it.
136
137        Self {
138            midi: MidiTrackMidiParamsValues {
139                note: 255,
140                vel: 255,
141                len: 255,
142                not2: 255,
143                not3: 255,
144                not4: 255,
145            },
146            lfo: LfoParamsValues {
147                spd1: 255,
148                spd2: 255,
149                spd3: 255,
150                dep1: 255,
151                dep2: 255,
152                dep3: 255,
153            },
154            arp: MidiTrackArpParamsValues {
155                tran: 255,
156                leg: 255,
157                mode: 255,
158                spd: 255,
159                rnge: 255,
160                nlen: 255,
161            },
162            ctrl1: MidiTrackCc1ParamsValues {
163                pb: 255,
164                at: 255,
165                cc1: 255,
166                cc2: 255,
167                cc3: 255,
168                cc4: 255,
169            },
170            ctrl2: MidiTrackCc2ParamsValues {
171                cc5: 255,
172                cc6: 255,
173                cc7: 255,
174                cc8: 255,
175                cc9: 255,
176                cc10: 255,
177            },
178            unknown: [255, 255],
179        }
180    }
181}
182
183/// Audio & MIDI Track Pattern playback settings.
184#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Copy)]
185pub struct TrackPatternSettings {
186    /// Silence any existing audio playback on the Audio Track when switching Patterns.
187    pub start_silent: u8,
188
189    /// Trigger Audio Track playback without any quantization or syncing to other Audio Tracks.
190    pub plays_free: u8,
191
192    /// Quantization when this Audio Track is Triggered for Playback.
193    ///
194    /// Options
195    /// ```text
196    /// N/A and ONE: 0 (Default)
197    /// ONE2: 1
198    /// HOLD: 2
199    /// ```
200    pub trig_mode: u8,
201
202    /// Track Trigger Quantization.
203    ///
204    /// Options
205    /// ```text
206    /// N/A and TR.LEN: 0 (Default)
207    /// 1/16: 1
208    /// 2/16: 2
209    /// 3/16: 3
210    /// 4/16: 4
211    /// 6/16: 5
212    /// 8/16: 6
213    /// 12/16: 7
214    /// 16/16: 8
215    /// 24/16: 9
216    /// 32/16: 10
217    /// 48/16: 11
218    /// 64/16: 12
219    /// 96/16: 13
220    /// 128/16: 14
221    /// 192/16: 15
222    /// 256/16: 16
223    /// DIRECT: 255
224    /// ```
225    pub trig_quant: u8,
226
227    /// Whether to play the track as a `ONESHOT` track.
228    pub oneshot_trk: u8,
229}
230
231impl Default for TrackPatternSettings {
232    fn default() -> Self {
233        Self {
234            start_silent: 255,
235            plays_free: 0,
236            trig_mode: 0,
237            trig_quant: 0,
238            oneshot_trk: 0,
239        }
240    }
241}
242
243/// Trig bitmasks array for Audio Tracks.
244/// Can be converted into an array of booleans using the `get_track_trigs_from_bitmasks` function.
245///
246/// Trig bitmask arrays have bitmasks stored in this order, which is slightly confusing (pay attention to the difference with 7 + 8!):
247/// 1. 1st half of the 4th page
248/// 2. 2nd half of the 4th page
249/// 3. 1st half of the 3rd page
250/// 4. 2nd half of the 3rd page
251/// 5. 1st half of the 2nd page
252/// 6. 2nd half of the 2nd page
253/// 7. 2nd half of the 1st page
254/// 8. 1st half of the 1st page
255///
256/// ### Bitmask values for trig positions
257/// With single trigs in a half-page
258/// ```text
259/// positions
260/// 1 2 3 4 5 6 7 8 | mask value
261/// ----------------|-----------
262/// - - - - - - - - | 0
263/// x - - - - - - - | 1
264/// - x - - - - - - | 2
265/// - - x - - - - - | 4
266/// - - - x - - - - | 8
267/// - - - - x - - - | 16
268/// - - - - - x - - | 32
269/// - - - - - - x - | 64
270/// - - - - - - - x | 128
271/// ```
272///
273/// When there are multiple trigs in a half-page, the individual position values are summed together:
274///
275/// ```text
276/// 1 2 3 4 5 6 7 8 | mask value
277/// ----------------|-----------
278/// x x - - - - - - | 1 + 2 = 3
279/// x x x x - - - - | 1 + 2 + 4 + 8 = 15
280/// ```
281/// ### Fuller diagram of mask values
282///
283/// ```text
284/// positions
285/// 1 2 3 4 5 6 7 8 | mask value
286/// ----------------|-----------
287/// x - - - - - - - | 1
288/// - x - - - - - - | 2
289/// x x - - - - - - | 3
290/// - - x - - - - - | 4
291/// x - x - - - - - | 5
292/// - x x - - - - - | 6
293/// x x x - - - - - | 7
294/// - - - x - - - - | 8
295/// x - - x - - - - | 9
296/// - x - x - - - - | 10
297/// x x - x - - - - | 11
298/// - - x x - - - - | 12
299/// x - x x - - - - | 13
300/// - x x x - - - - | 14
301/// x x x x - - - - | 15
302/// ................|....
303/// x x x x x x - - | 63
304/// ................|....
305/// - - - - - - - x | 128
306/// ................|....
307/// - x - x - x - x | 170
308/// ................|....
309/// - - - - x x x x | 240
310/// ................|....
311/// x x x x x x x x | 255
312/// ```
313///
314#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
315pub struct AudioTrackTrigMasks {
316    /// Trigger Trig masks -- indicate which Trigger Trigs are active.
317    /// Base track Trig masks are stored backwards, meaning
318    /// the first 8 Trig positions are the last bytes in this section.
319    #[serde(with = "BigArray")]
320    pub trigger: [u8; 8],
321
322    /// Envelope Trig masks -- indicate which Envelope Trigs are active.
323    /// See the description of the `trig_trig_masks` field for an
324    /// explanation of how the masking works.
325    #[serde(with = "BigArray")]
326    pub trigless: [u8; 8],
327
328    /// Parameter-Lock Trig masks -- indicate which Parameter-Lock Trigs are active.
329    /// See the description of the `trig_trig_masks` field for an
330    /// explanation of how the masking works.    
331    #[serde(with = "BigArray")]
332    pub plock: [u8; 8],
333
334    /// Hold Trig masks -- indicate which Hold Trigs are active.
335    /// See the description of the `trig_trig_masks` field for an
336    /// explanation of how the masking works.
337    #[serde(with = "BigArray")]
338    pub oneshot: [u8; 8],
339
340    /// Recorder Trig masks -- indicate which Recorder Trigs are active.
341    /// These seem to function differently to the main Track Trig masks.
342    /// Filling up Recorder Trigs on a Pattern results in a 32 length array
343    /// instead of 8 length.
344    /// Possible that the Trig type is stored in this array as well.
345    #[serde(with = "BigArray")]
346    pub recorder: [u8; 32],
347
348    /// Swing trigs Trig masks.
349    #[serde(with = "BigArray")]
350    pub swing: [u8; 8],
351
352    /// Parameter Slide trigs Trig masks.
353    #[serde(with = "BigArray")]
354    pub slide: [u8; 8],
355}
356
357impl Default for AudioTrackTrigMasks {
358    fn default() -> Self {
359        Self {
360            trigger: from_fn(|_| 0),
361            trigless: from_fn(|_| 0),
362            plock: from_fn(|_| 0),
363            oneshot: from_fn(|_| 0),
364            recorder: from_fn(|_| 0),
365            swing: from_fn(|_| 170),
366            slide: from_fn(|_| 0),
367        }
368    }
369}
370
371/// Audio Track custom scaling when the Pattern is in PER TRACK scale mode.
372#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Copy)]
373pub struct TrackPerTrackModeScale {
374    /// The Audio Track's Length when Pattern is in Per Track mode.
375    /// Default: 16
376    pub per_track_len: u8,
377
378    /// The Audio Track's Scale when Pattern is in Per Track mode.
379    ///
380    /// Options
381    /// ```text
382    /// 0 -> 2x
383    /// 1 -> 3/2x
384    /// 2 -> 1x (Default)
385    /// 3 -> 3/4x
386    /// 4 -> 1/2x
387    /// 5 -> 1/4x
388    /// 6 -> 1/8x
389    /// ```
390    pub per_track_scale: u8,
391}
392
393impl Default for TrackPerTrackModeScale {
394    fn default() -> Self {
395        Self {
396            per_track_len: 16,
397            per_track_scale: 2,
398        }
399    }
400}
401
402/// Sample Slot options for Projects.
403#[derive(Serialize, Deserialize, PartialEq, Debug, Clone, Eq, Hash)]
404pub enum TrigCondition {
405    // Uses implicit discriminants to increment values from 0 -> 64 (inclusive)
406    // https://doc.rust-lang.org/stable/reference/items/enumerations.html#implicit-discriminants
407    None = 0,
408    /// > FILL is true (will activate the trig) when fill mode is active (see below).
409    Fill,
410    /// > ... true when FILL is not.
411    NotFill,
412    /// > PRE is true if the most recently evaluated trig condition on the same track was true.
413    Pre,
414    /// > ... true when PRE is not.
415    NotPre,
416    /// > true if the most recently evaluated trig condition on the neighbor track was true.
417    /// > The neighbor track is the track before the one being edited.
418    /// > For example, the neighbor track of track 4 is track 3. If no conditions exist on the
419    /// > neighbor track, the condition is false.
420    Nei,
421    /// > ... true when NEI is not.
422    NotNei,
423    /// > only true the first time the pattern play (when looped).
424    First,
425    /// > ... true when 1st is not.
426    NotFirst,
427    /// > probability condition. 1% chance of being true.
428    Percent1,
429    /// > probability condition. 2% chance of being true.
430    Percent2,
431    /// > probability condition. 4% chance of being true.
432    Percent4,
433    /// > probability condition. 6% chance of being true.
434    Percent6,
435    /// > probability condition. 9% chance of being true.
436    Percent9,
437    /// > probability condition. 13% chance of being true.
438    Percent13,
439    /// > probability condition. 19% chance of being true.
440    Percent19,
441    /// > probability condition. 25% chance of being true.
442    Percent25,
443    /// > probability condition. 33% chance of being true.
444    Percent33,
445    /// > probability condition. 41% chance of being true.
446    Percent41,
447    /// > probability condition. 50% chance of being true.
448    Percent50,
449    /// > probability condition. 59% chance of being true.
450    Percent59,
451    /// > probability condition. 67% chance of being true.
452    Percent67,
453    /// > probability condition. 75% chance of being true.
454    Percent75,
455    /// > probability condition. 81% chance of being true.
456    Percent81,
457    /// > probability condition. 87% chance of being true.
458    Percent87,
459    /// > probability condition. 91% chance of being true.
460    Percent91,
461    /// > probability condition. 94% chance of being true.
462    Percent94,
463    /// > probability condition. 96% chance of being true.
464    Percent96,
465    /// > probability condition. 98% chance of being true.
466    Percent98,
467    /// > probability condition. 99% chance of being true.
468    Percent99,
469    /// pattern loop 1 triggers, pattern loop 2 resets
470    PatternT1R2,
471    /// pattern loop 2 triggers, pattern loop 2 resets
472    PatternT2R2,
473    /// pattern loop 1 triggers, pattern loop 3 resets
474    PatternT1R3,
475    /// pattern loop 2 triggers, pattern loop 3 resets
476    PatternT2R3,
477    /// pattern loop 3 triggers, pattern loop 3 resets
478    PatternT3R3,
479    /// pattern loop 1 triggers, pattern loop 4 resets
480    PatternT1R4,
481    /// pattern loop 2 triggers, pattern loop 4 resets
482    PatternT2R4,
483    /// pattern loop 3 triggers, pattern loop 4 resets
484    PatternT3R4,
485    /// pattern loop 4 triggers, pattern loop 4 resets
486    PatternT4R4,
487    /// pattern loop 1 triggers, pattern loop 5 resets
488    PatternT1R5,
489    /// pattern loop 2 triggers, pattern loop 5 resets
490    PatternT2R5,
491    /// pattern loop 3 triggers, pattern loop 5 resets
492    PatternT3R5,
493    /// pattern loop 4 triggers, pattern loop 5 resets
494    PatternT4R5,
495    /// pattern loop 5 triggers, pattern loop 5 resets
496    PatternT5R5,
497    /// pattern loop 1 triggers, pattern loop 6 resets
498    PatternT1R6,
499    /// pattern loop 2 triggers, pattern loop 6 resets
500    PatternT2R6,
501    /// pattern loop 3 triggers, pattern loop 6 resets
502    PatternT3R6,
503    /// pattern loop 4 triggers, pattern loop 6 resets
504    PatternT4R6,
505    /// pattern loop 5 triggers, pattern loop 6 resets
506    PatternT5R6,
507    /// pattern loop 6 triggers, pattern loop 6 resets
508    PatternT6R6,
509    /// pattern loop 1 triggers, pattern loop 7 resets
510    PatternT1R7,
511    /// pattern loop 2 triggers, pattern loop 7 resets
512    PatternT2R7,
513    /// pattern loop 3 triggers, pattern loop 7 resets
514    PatternT3R7,
515    /// pattern loop 4 triggers, pattern loop 7 resets
516    PatternT4R7,
517    /// pattern loop 5 triggers, pattern loop 7 resets
518    PatternT5R7,
519    /// pattern loop 6 triggers, pattern loop 7 resets
520    PatternT6R7,
521    /// pattern loop 7 triggers, pattern loop 7 resets
522    PatternT7R7,
523    /// pattern loop 1 triggers, pattern loop 8 resets
524    PatternT1R8,
525    /// pattern loop 2 triggers, pattern loop 8 resets
526    PatternT2R8,
527    /// pattern loop 3 triggers, pattern loop 8 resets
528    PatternT3R8,
529    /// pattern loop 4 triggers, pattern loop 8 resets
530    PatternT4R8,
531    /// pattern loop 5 triggers, pattern loop 8 resets
532    PatternT5R8,
533    /// pattern loop 6 triggers, pattern loop 8 resets
534    PatternT6R8,
535    /// pattern loop 7 triggers, pattern loop 8 resets
536    PatternT7R8,
537    /// pattern loop 8 triggers, pattern loop 8 resets
538    PatternT8R8,
539}
540
541impl OptionEnumValueConvert<u8> for TrigCondition {
542    fn from_value(v: &u8) -> RBoxErr<Self> {
543        // read the essay for `AudioTrackTrigs.trig_timings_repeats_conditions`
544        // to understand why rem_euclid is used here
545        match v.rem_euclid(128) {
546            0 => Ok(TrigCondition::None),
547            1 => Ok(TrigCondition::Fill),
548            2 => Ok(TrigCondition::NotFill),
549            3 => Ok(TrigCondition::Pre),
550            4 => Ok(TrigCondition::NotPre),
551            5 => Ok(TrigCondition::Nei),
552            6 => Ok(TrigCondition::NotNei),
553            7 => Ok(TrigCondition::First),
554            8 => Ok(TrigCondition::NotFirst),
555            //
556            9 => Ok(TrigCondition::Percent1),
557            10 => Ok(TrigCondition::Percent2),
558            11 => Ok(TrigCondition::Percent4),
559            12 => Ok(TrigCondition::Percent6),
560            13 => Ok(TrigCondition::Percent9),
561            14 => Ok(TrigCondition::Percent13),
562            15 => Ok(TrigCondition::Percent19),
563            16 => Ok(TrigCondition::Percent25),
564            17 => Ok(TrigCondition::Percent33),
565            18 => Ok(TrigCondition::Percent41),
566            19 => Ok(TrigCondition::Percent50),
567            20 => Ok(TrigCondition::Percent59),
568            21 => Ok(TrigCondition::Percent67),
569            22 => Ok(TrigCondition::Percent75),
570            23 => Ok(TrigCondition::Percent81),
571            24 => Ok(TrigCondition::Percent87),
572            25 => Ok(TrigCondition::Percent91),
573            26 => Ok(TrigCondition::Percent94),
574            27 => Ok(TrigCondition::Percent96),
575            28 => Ok(TrigCondition::Percent98),
576            29 => Ok(TrigCondition::Percent99),
577            //
578            30 => Ok(TrigCondition::PatternT1R2),
579            31 => Ok(TrigCondition::PatternT2R2),
580            //
581            32 => Ok(TrigCondition::PatternT1R3),
582            33 => Ok(TrigCondition::PatternT2R3),
583            34 => Ok(TrigCondition::PatternT3R3),
584            //
585            35 => Ok(TrigCondition::PatternT1R4),
586            36 => Ok(TrigCondition::PatternT2R4),
587            37 => Ok(TrigCondition::PatternT3R4),
588            38 => Ok(TrigCondition::PatternT4R4),
589            //
590            39 => Ok(TrigCondition::PatternT1R5),
591            40 => Ok(TrigCondition::PatternT2R5),
592            41 => Ok(TrigCondition::PatternT3R5),
593            42 => Ok(TrigCondition::PatternT4R5),
594            43 => Ok(TrigCondition::PatternT5R5),
595            //
596            44 => Ok(TrigCondition::PatternT1R6),
597            45 => Ok(TrigCondition::PatternT2R6),
598            46 => Ok(TrigCondition::PatternT3R6),
599            47 => Ok(TrigCondition::PatternT4R6),
600            48 => Ok(TrigCondition::PatternT5R6),
601            49 => Ok(TrigCondition::PatternT6R6),
602            //
603            50 => Ok(TrigCondition::PatternT1R7),
604            51 => Ok(TrigCondition::PatternT2R7),
605            52 => Ok(TrigCondition::PatternT3R7),
606            53 => Ok(TrigCondition::PatternT4R7),
607            54 => Ok(TrigCondition::PatternT5R7),
608            55 => Ok(TrigCondition::PatternT6R7),
609            56 => Ok(TrigCondition::PatternT7R7),
610            //
611            57 => Ok(TrigCondition::PatternT1R8),
612            58 => Ok(TrigCondition::PatternT2R8),
613            59 => Ok(TrigCondition::PatternT3R8),
614            60 => Ok(TrigCondition::PatternT4R8),
615            61 => Ok(TrigCondition::PatternT5R8),
616            62 => Ok(TrigCondition::PatternT6R8),
617            63 => Ok(TrigCondition::PatternT7R8),
618            64 => Ok(TrigCondition::PatternT8R8),
619            //
620            _ => Err(OtToolsIoErrors::NoMatchingOptionEnumValue.into()),
621        }
622    }
623
624    fn value(&self) -> RBoxErr<u8> {
625        Ok(self.clone() as u8)
626    }
627}
628
629#[cfg(test)]
630mod test_spec {
631
632    mod from_value {
633        use crate::patterns::TrigCondition;
634        use crate::OptionEnumValueConvert;
635
636        #[test]
637        fn none() {
638            assert_eq!(TrigCondition::None, TrigCondition::from_value(&0).unwrap())
639        }
640        #[test]
641        fn fill() {
642            assert_eq!(TrigCondition::Fill, TrigCondition::from_value(&1).unwrap())
643        }
644        #[test]
645        fn notfill() {
646            assert_eq!(
647                TrigCondition::NotFill,
648                TrigCondition::from_value(&2).unwrap()
649            )
650        }
651        #[test]
652        fn pre() {
653            assert_eq!(TrigCondition::Pre, TrigCondition::from_value(&3).unwrap())
654        }
655        #[test]
656        fn notpre() {
657            assert_eq!(
658                TrigCondition::NotPre,
659                TrigCondition::from_value(&4).unwrap()
660            )
661        }
662        #[test]
663        fn nei() {
664            assert_eq!(TrigCondition::Nei, TrigCondition::from_value(&5).unwrap())
665        }
666        #[test]
667        fn notnei() {
668            assert_eq!(
669                TrigCondition::NotNei,
670                TrigCondition::from_value(&6).unwrap()
671            )
672        }
673        #[test]
674        fn first() {
675            assert_eq!(TrigCondition::First, TrigCondition::from_value(&7).unwrap())
676        }
677        #[test]
678        fn notfirst() {
679            assert_eq!(
680                TrigCondition::NotFirst,
681                TrigCondition::from_value(&8).unwrap()
682            )
683        }
684        #[test]
685        fn perc1() {
686            assert_eq!(
687                TrigCondition::Percent1,
688                TrigCondition::from_value(&9).unwrap()
689            )
690        }
691        #[test]
692        fn perc2() {
693            assert_eq!(
694                TrigCondition::Percent2,
695                TrigCondition::from_value(&10).unwrap()
696            )
697        }
698        #[test]
699        fn perc4() {
700            assert_eq!(
701                TrigCondition::Percent4,
702                TrigCondition::from_value(&11).unwrap()
703            )
704        }
705        #[test]
706        fn perc6() {
707            assert_eq!(
708                TrigCondition::Percent6,
709                TrigCondition::from_value(&12).unwrap()
710            )
711        }
712        #[test]
713        fn perc9() {
714            assert_eq!(
715                TrigCondition::Percent9,
716                TrigCondition::from_value(&13).unwrap()
717            )
718        }
719        #[test]
720        fn perc13() {
721            assert_eq!(
722                TrigCondition::Percent13,
723                TrigCondition::from_value(&14).unwrap()
724            )
725        }
726        #[test]
727        fn perc19() {
728            assert_eq!(
729                TrigCondition::Percent19,
730                TrigCondition::from_value(&15).unwrap()
731            )
732        }
733        #[test]
734        fn perc25() {
735            assert_eq!(
736                TrigCondition::Percent25,
737                TrigCondition::from_value(&16).unwrap()
738            )
739        }
740        #[test]
741        fn perc33() {
742            assert_eq!(
743                TrigCondition::Percent33,
744                TrigCondition::from_value(&17).unwrap()
745            )
746        }
747        #[test]
748        fn perc41() {
749            assert_eq!(
750                TrigCondition::Percent41,
751                TrigCondition::from_value(&18).unwrap()
752            )
753        }
754        #[test]
755        fn perc50() {
756            assert_eq!(
757                TrigCondition::Percent50,
758                TrigCondition::from_value(&19).unwrap()
759            )
760        }
761        #[test]
762        fn perc59() {
763            assert_eq!(
764                TrigCondition::Percent59,
765                TrigCondition::from_value(&20).unwrap()
766            )
767        }
768        #[test]
769        fn perc67() {
770            assert_eq!(
771                TrigCondition::Percent67,
772                TrigCondition::from_value(&21).unwrap()
773            )
774        }
775        #[test]
776        fn perc75() {
777            assert_eq!(
778                TrigCondition::Percent75,
779                TrigCondition::from_value(&22).unwrap()
780            )
781        }
782        #[test]
783        fn perc81() {
784            assert_eq!(
785                TrigCondition::Percent81,
786                TrigCondition::from_value(&23).unwrap()
787            )
788        }
789        #[test]
790        fn perc87() {
791            assert_eq!(
792                TrigCondition::Percent87,
793                TrigCondition::from_value(&24).unwrap()
794            )
795        }
796        #[test]
797        fn perc91() {
798            assert_eq!(
799                TrigCondition::Percent91,
800                TrigCondition::from_value(&25).unwrap()
801            )
802        }
803        #[test]
804        fn perc94() {
805            assert_eq!(
806                TrigCondition::Percent94,
807                TrigCondition::from_value(&26).unwrap()
808            )
809        }
810        #[test]
811        fn perc96() {
812            assert_eq!(
813                TrigCondition::Percent96,
814                TrigCondition::from_value(&27).unwrap()
815            )
816        }
817        #[test]
818        fn perc98() {
819            assert_eq!(
820                TrigCondition::Percent98,
821                TrigCondition::from_value(&28).unwrap()
822            )
823        }
824        #[test]
825        fn perc99() {
826            assert_eq!(
827                TrigCondition::Percent99,
828                TrigCondition::from_value(&29).unwrap()
829            )
830        }
831        #[test]
832        fn pat_t1r2() {
833            assert_eq!(
834                TrigCondition::PatternT1R2,
835                TrigCondition::from_value(&30).unwrap()
836            )
837        }
838        #[test]
839        fn pat_t2r2() {
840            assert_eq!(
841                TrigCondition::PatternT2R2,
842                TrigCondition::from_value(&31).unwrap()
843            )
844        }
845        #[test]
846        fn pat_t1r3() {
847            assert_eq!(
848                TrigCondition::PatternT1R3,
849                TrigCondition::from_value(&32).unwrap()
850            )
851        }
852        #[test]
853        fn pat_t2r3() {
854            assert_eq!(
855                TrigCondition::PatternT2R3,
856                TrigCondition::from_value(&33).unwrap()
857            )
858        }
859        #[test]
860        fn pat_t3r3() {
861            assert_eq!(
862                TrigCondition::PatternT3R3,
863                TrigCondition::from_value(&34).unwrap()
864            )
865        }
866
867        #[test]
868        fn pat_t1r4() {
869            assert_eq!(
870                TrigCondition::PatternT1R4,
871                TrigCondition::from_value(&35).unwrap()
872            )
873        }
874        #[test]
875        fn pat_t2r4() {
876            assert_eq!(
877                TrigCondition::PatternT2R4,
878                TrigCondition::from_value(&36).unwrap()
879            )
880        }
881        #[test]
882        fn pat_t3r4() {
883            assert_eq!(
884                TrigCondition::PatternT3R4,
885                TrigCondition::from_value(&37).unwrap()
886            )
887        }
888        #[test]
889        fn pat_t4r4() {
890            assert_eq!(
891                TrigCondition::PatternT4R4,
892                TrigCondition::from_value(&38).unwrap()
893            )
894        }
895
896        #[test]
897        fn pat_t1r5() {
898            assert_eq!(
899                TrigCondition::PatternT1R5,
900                TrigCondition::from_value(&39).unwrap()
901            )
902        }
903        #[test]
904        fn pat_t2r5() {
905            assert_eq!(
906                TrigCondition::PatternT2R5,
907                TrigCondition::from_value(&40).unwrap()
908            )
909        }
910        #[test]
911        fn pat_t3r5() {
912            assert_eq!(
913                TrigCondition::PatternT3R5,
914                TrigCondition::from_value(&41).unwrap()
915            )
916        }
917        #[test]
918        fn pat_t4r5() {
919            assert_eq!(
920                TrigCondition::PatternT4R5,
921                TrigCondition::from_value(&42).unwrap()
922            )
923        }
924        #[test]
925        fn pat_t5r5() {
926            assert_eq!(
927                TrigCondition::PatternT5R5,
928                TrigCondition::from_value(&43).unwrap()
929            )
930        }
931
932        #[test]
933        fn pat_t1r6() {
934            assert_eq!(
935                TrigCondition::PatternT1R6,
936                TrigCondition::from_value(&44).unwrap()
937            )
938        }
939        #[test]
940        fn pat_t2r6() {
941            assert_eq!(
942                TrigCondition::PatternT2R6,
943                TrigCondition::from_value(&45).unwrap()
944            )
945        }
946        #[test]
947        fn pat_t3r6() {
948            assert_eq!(
949                TrigCondition::PatternT3R6,
950                TrigCondition::from_value(&46).unwrap()
951            )
952        }
953        #[test]
954        fn pat_t4r6() {
955            assert_eq!(
956                TrigCondition::PatternT4R6,
957                TrigCondition::from_value(&47).unwrap()
958            )
959        }
960        #[test]
961        fn pat_t5r6() {
962            assert_eq!(
963                TrigCondition::PatternT5R6,
964                TrigCondition::from_value(&48).unwrap()
965            )
966        }
967        #[test]
968        fn pat_t6r6() {
969            assert_eq!(
970                TrigCondition::PatternT6R6,
971                TrigCondition::from_value(&49).unwrap()
972            )
973        }
974
975        #[test]
976        fn pat_t1r7() {
977            assert_eq!(
978                TrigCondition::PatternT1R7,
979                TrigCondition::from_value(&50).unwrap()
980            )
981        }
982        #[test]
983        fn pat_t2r7() {
984            assert_eq!(
985                TrigCondition::PatternT2R7,
986                TrigCondition::from_value(&51).unwrap()
987            )
988        }
989        #[test]
990        fn pat_t3r7() {
991            assert_eq!(
992                TrigCondition::PatternT3R7,
993                TrigCondition::from_value(&52).unwrap()
994            )
995        }
996        #[test]
997        fn pat_t4r7() {
998            assert_eq!(
999                TrigCondition::PatternT4R7,
1000                TrigCondition::from_value(&53).unwrap()
1001            )
1002        }
1003        #[test]
1004        fn pat_t5r7() {
1005            assert_eq!(
1006                TrigCondition::PatternT5R7,
1007                TrigCondition::from_value(&54).unwrap()
1008            )
1009        }
1010        #[test]
1011        fn pat_t6r7() {
1012            assert_eq!(
1013                TrigCondition::PatternT6R7,
1014                TrigCondition::from_value(&55).unwrap()
1015            )
1016        }
1017        #[test]
1018        fn pat_t7r7() {
1019            assert_eq!(
1020                TrigCondition::PatternT7R7,
1021                TrigCondition::from_value(&56).unwrap()
1022            )
1023        }
1024
1025        #[test]
1026        fn pat_t1r8() {
1027            assert_eq!(
1028                TrigCondition::PatternT1R8,
1029                TrigCondition::from_value(&57).unwrap()
1030            )
1031        }
1032        #[test]
1033        fn pat_t2r8() {
1034            assert_eq!(
1035                TrigCondition::PatternT2R8,
1036                TrigCondition::from_value(&58).unwrap()
1037            )
1038        }
1039        #[test]
1040        fn pat_t3r8() {
1041            assert_eq!(
1042                TrigCondition::PatternT3R8,
1043                TrigCondition::from_value(&59).unwrap()
1044            )
1045        }
1046        #[test]
1047        fn pat_t4r8() {
1048            assert_eq!(
1049                TrigCondition::PatternT4R8,
1050                TrigCondition::from_value(&60).unwrap()
1051            )
1052        }
1053        #[test]
1054        fn pat_t5r8() {
1055            assert_eq!(
1056                TrigCondition::PatternT5R8,
1057                TrigCondition::from_value(&61).unwrap()
1058            )
1059        }
1060        #[test]
1061        fn pat_t6r8() {
1062            assert_eq!(
1063                TrigCondition::PatternT6R8,
1064                TrigCondition::from_value(&62).unwrap()
1065            )
1066        }
1067        #[test]
1068        fn pat_t7r8() {
1069            assert_eq!(
1070                TrigCondition::PatternT7R8,
1071                TrigCondition::from_value(&63).unwrap()
1072            )
1073        }
1074        #[test]
1075        fn pat_t8r8() {
1076            assert_eq!(
1077                TrigCondition::PatternT8R8,
1078                TrigCondition::from_value(&64).unwrap()
1079            )
1080        }
1081    }
1082    mod get_value {
1083        use crate::patterns::TrigCondition;
1084        use crate::OptionEnumValueConvert;
1085
1086        #[test]
1087        fn none() {
1088            assert_eq!(TrigCondition::None.value().unwrap(), 0)
1089        }
1090        #[test]
1091        fn fill() {
1092            assert_eq!(TrigCondition::Fill.value().unwrap(), 1)
1093        }
1094        #[test]
1095        fn notfill() {
1096            assert_eq!(TrigCondition::NotFill.value().unwrap(), 2)
1097        }
1098        #[test]
1099        fn pre() {
1100            assert_eq!(TrigCondition::Pre.value().unwrap(), 3)
1101        }
1102        #[test]
1103        fn notpre() {
1104            assert_eq!(TrigCondition::NotPre.value().unwrap(), 4)
1105        }
1106        #[test]
1107        fn nei() {
1108            assert_eq!(TrigCondition::Nei.value().unwrap(), 5)
1109        }
1110        #[test]
1111        fn notnei() {
1112            assert_eq!(TrigCondition::NotNei.value().unwrap(), 6)
1113        }
1114        #[test]
1115        fn first() {
1116            assert_eq!(TrigCondition::First.value().unwrap(), 7)
1117        }
1118        #[test]
1119        fn notfirst() {
1120            assert_eq!(TrigCondition::NotFirst.value().unwrap(), 8)
1121        }
1122        #[test]
1123        fn perc1() {
1124            assert_eq!(TrigCondition::Percent1.value().unwrap(), 9)
1125        }
1126        #[test]
1127        fn perc2() {
1128            assert_eq!(TrigCondition::Percent2.value().unwrap(), 10)
1129        }
1130        #[test]
1131        fn perc4() {
1132            assert_eq!(TrigCondition::Percent4.value().unwrap(), 11)
1133        }
1134        #[test]
1135        fn perc6() {
1136            assert_eq!(TrigCondition::Percent6.value().unwrap(), 12)
1137        }
1138        #[test]
1139        fn perc9() {
1140            assert_eq!(TrigCondition::Percent9.value().unwrap(), 13)
1141        }
1142        #[test]
1143        fn perc13() {
1144            assert_eq!(TrigCondition::Percent13.value().unwrap(), 14)
1145        }
1146        #[test]
1147        fn perc19() {
1148            assert_eq!(TrigCondition::Percent19.value().unwrap(), 15)
1149        }
1150        #[test]
1151        fn perc25() {
1152            assert_eq!(TrigCondition::Percent25.value().unwrap(), 16)
1153        }
1154        #[test]
1155        fn perc33() {
1156            assert_eq!(TrigCondition::Percent33.value().unwrap(), 17)
1157        }
1158        #[test]
1159        fn perc41() {
1160            assert_eq!(TrigCondition::Percent41.value().unwrap(), 18)
1161        }
1162        #[test]
1163        fn perc50() {
1164            assert_eq!(TrigCondition::Percent50.value().unwrap(), 19)
1165        }
1166        #[test]
1167        fn perc59() {
1168            assert_eq!(TrigCondition::Percent59.value().unwrap(), 20)
1169        }
1170        #[test]
1171        fn perc67() {
1172            assert_eq!(TrigCondition::Percent67.value().unwrap(), 21)
1173        }
1174        #[test]
1175        fn perc75() {
1176            assert_eq!(TrigCondition::Percent75.value().unwrap(), 22)
1177        }
1178        #[test]
1179        fn perc81() {
1180            assert_eq!(TrigCondition::Percent81.value().unwrap(), 23)
1181        }
1182        #[test]
1183        fn perc87() {
1184            assert_eq!(TrigCondition::Percent87.value().unwrap(), 24)
1185        }
1186        #[test]
1187        fn perc91() {
1188            assert_eq!(TrigCondition::Percent91.value().unwrap(), 25)
1189        }
1190        #[test]
1191        fn perc94() {
1192            assert_eq!(TrigCondition::Percent94.value().unwrap(), 26)
1193        }
1194        #[test]
1195        fn perc96() {
1196            assert_eq!(TrigCondition::Percent96.value().unwrap(), 27)
1197        }
1198        #[test]
1199        fn perc98() {
1200            assert_eq!(TrigCondition::Percent98.value().unwrap(), 28)
1201        }
1202        #[test]
1203        fn perc99() {
1204            assert_eq!(TrigCondition::Percent99.value().unwrap(), 29)
1205        }
1206        #[test]
1207        fn pat_t1r2() {
1208            assert_eq!(TrigCondition::PatternT1R2.value().unwrap(), 30)
1209        }
1210        #[test]
1211        fn pat_t2r2() {
1212            assert_eq!(TrigCondition::PatternT2R2.value().unwrap(), 31)
1213        }
1214        #[test]
1215        fn pat_t1r3() {
1216            assert_eq!(TrigCondition::PatternT1R3.value().unwrap(), 32)
1217        }
1218        #[test]
1219        fn pat_t2r3() {
1220            assert_eq!(TrigCondition::PatternT2R3.value().unwrap(), 33)
1221        }
1222        #[test]
1223        fn pat_t3r3() {
1224            assert_eq!(TrigCondition::PatternT3R3.value().unwrap(), 34)
1225        }
1226
1227        #[test]
1228        fn pat_t1r4() {
1229            assert_eq!(TrigCondition::PatternT1R4.value().unwrap(), 35)
1230        }
1231        #[test]
1232        fn pat_t2r4() {
1233            assert_eq!(TrigCondition::PatternT2R4.value().unwrap(), 36)
1234        }
1235        #[test]
1236        fn pat_t3r4() {
1237            assert_eq!(TrigCondition::PatternT3R4.value().unwrap(), 37)
1238        }
1239        #[test]
1240        fn pat_t4r4() {
1241            assert_eq!(TrigCondition::PatternT4R4.value().unwrap(), 38)
1242        }
1243
1244        #[test]
1245        fn pat_t1r5() {
1246            assert_eq!(TrigCondition::PatternT1R5.value().unwrap(), 39)
1247        }
1248        #[test]
1249        fn pat_t2r5() {
1250            assert_eq!(TrigCondition::PatternT2R5.value().unwrap(), 40)
1251        }
1252        #[test]
1253        fn pat_t3r5() {
1254            assert_eq!(TrigCondition::PatternT3R5.value().unwrap(), 41)
1255        }
1256        #[test]
1257        fn pat_t4r5() {
1258            assert_eq!(TrigCondition::PatternT4R5.value().unwrap(), 42)
1259        }
1260        #[test]
1261        fn pat_t5r5() {
1262            assert_eq!(TrigCondition::PatternT5R5.value().unwrap(), 43)
1263        }
1264
1265        #[test]
1266        fn pat_t1r6() {
1267            assert_eq!(TrigCondition::PatternT1R6.value().unwrap(), 44)
1268        }
1269        #[test]
1270        fn pat_t2r6() {
1271            assert_eq!(TrigCondition::PatternT2R6.value().unwrap(), 45)
1272        }
1273        #[test]
1274        fn pat_t3r6() {
1275            assert_eq!(TrigCondition::PatternT3R6.value().unwrap(), 46)
1276        }
1277        #[test]
1278        fn pat_t4r6() {
1279            assert_eq!(TrigCondition::PatternT4R6.value().unwrap(), 47)
1280        }
1281        #[test]
1282        fn pat_t5r6() {
1283            assert_eq!(TrigCondition::PatternT5R6.value().unwrap(), 48)
1284        }
1285        #[test]
1286        fn pat_t6r6() {
1287            assert_eq!(TrigCondition::PatternT6R6.value().unwrap(), 49)
1288        }
1289
1290        #[test]
1291        fn pat_t1r7() {
1292            assert_eq!(TrigCondition::PatternT1R7.value().unwrap(), 50)
1293        }
1294        #[test]
1295        fn pat_t2r7() {
1296            assert_eq!(TrigCondition::PatternT2R7.value().unwrap(), 51)
1297        }
1298        #[test]
1299        fn pat_t3r7() {
1300            assert_eq!(TrigCondition::PatternT3R7.value().unwrap(), 52)
1301        }
1302        #[test]
1303        fn pat_t4r7() {
1304            assert_eq!(TrigCondition::PatternT4R7.value().unwrap(), 53)
1305        }
1306        #[test]
1307        fn pat_t5r7() {
1308            assert_eq!(TrigCondition::PatternT5R7.value().unwrap(), 54)
1309        }
1310        #[test]
1311        fn pat_t6r7() {
1312            assert_eq!(TrigCondition::PatternT6R7.value().unwrap(), 55)
1313        }
1314        #[test]
1315        fn pat_t7r7() {
1316            assert_eq!(TrigCondition::PatternT7R7.value().unwrap(), 56)
1317        }
1318
1319        #[test]
1320        fn pat_t1r8() {
1321            assert_eq!(TrigCondition::PatternT1R8.value().unwrap(), 57)
1322        }
1323        #[test]
1324        fn pat_t2r8() {
1325            assert_eq!(TrigCondition::PatternT2R8.value().unwrap(), 58)
1326        }
1327        #[test]
1328        fn pat_t3r8() {
1329            assert_eq!(TrigCondition::PatternT3R8.value().unwrap(), 59)
1330        }
1331        #[test]
1332        fn pat_t4r8() {
1333            assert_eq!(TrigCondition::PatternT4R8.value().unwrap(), 60)
1334        }
1335        #[test]
1336        fn pat_t5r8() {
1337            assert_eq!(TrigCondition::PatternT5R8.value().unwrap(), 61)
1338        }
1339        #[test]
1340        fn pat_t6r8() {
1341            assert_eq!(TrigCondition::PatternT6R8.value().unwrap(), 62)
1342        }
1343        #[test]
1344        fn pat_t7r8() {
1345            assert_eq!(TrigCondition::PatternT7R8.value().unwrap(), 63)
1346        }
1347        #[test]
1348        fn pat_t8r8() {
1349            assert_eq!(TrigCondition::PatternT8R8.value().unwrap(), 64)
1350        }
1351    }
1352}
1353
1354/// Track trigs assigned on an Audio Track within a Pattern
1355#[derive(PartialEq, Debug, Serialize, Deserialize, Clone)]
1356pub struct AudioTrackTrigs {
1357    /// Header data section
1358    ///
1359    /// example data:
1360    /// ```text
1361    /// TRAC
1362    /// 54 52 41 43
1363    /// ```
1364    #[serde(with = "BigArray")]
1365    pub header: [u8; 4],
1366
1367    /// Unknown data.
1368    #[serde(with = "BigArray")]
1369    pub unknown_1: [u8; 4],
1370
1371    /// The zero indexed track number
1372    pub track_id: u8,
1373
1374    /// Trig masks contain the Trig step locations for different trig types
1375    pub trig_masks: AudioTrackTrigMasks,
1376
1377    /// The scale of this Audio Track in Per Track Pattern mode.
1378    pub scale_per_track_mode: TrackPerTrackModeScale,
1379
1380    /// Amount of swing when a Swing Trig is active for the Track.
1381    /// Maximum is `30` (`80` on device), minimum is `0` (`50` on device).
1382    pub swing_amount: u8,
1383
1384    /// Pattern settings for this Audio Track
1385    pub pattern_settings: TrackPatternSettings,
1386
1387    /// Unknown data.
1388    pub unknown_2: u8,
1389
1390    /// Parameter-Lock data for all Trigs.
1391    // note -- stack overflow if tring to use #[serde(with = "BigArray")]
1392    pub plocks: Box<Array<AudioTrackParameterLocks, 64>>,
1393
1394    /// What the hell is this field?!?!
1395    /// It **has to** be something to do with trigs, but i have no idea what it could be.
1396    #[serde(with = "BigArray")]
1397    pub unknown_3: [u8; 64],
1398
1399    /// Trig Offsets, Trig Counts and Trig Conditions
1400    /// ====
1401    /// This is ..... slightly frustrating.
1402    ///
1403    /// This 64 length array consisting of a pair of bytes for each array element hold three
1404    /// data references... Trig Cunts and Trig Conditions use the two bytes independently,
1405    /// so they're easier to explain first
1406    ///
1407    /// Trig Counts and Trig Conditions
1408    /// ====
1409    ///
1410    /// Trig Counts and Trig Conditions data is interleaved for each trig.
1411    /// For Trig position 1, array index 0 is the count value and array index 1 is the Trig
1412    /// Condition.
1413    ///
1414    /// For trig counts (1st byte), the value (zero-indexed) is multiplied by 32.
1415    /// - 8 trig counts (7 repeats) --> 7 * 3 = 224
1416    /// - 4 trig counts (3 repeats) -- 3 * 32 = 96
1417    /// - 1 trig counts (0 repeats) -- 0 * 32 = 0
1418    ///
1419    /// For conditionals, see the `TrigCondition` enum and associated traits for more details.
1420    /// The maximum value for a Trig Condition byte is 64.
1421    ///
1422    /// ```rust
1423    /// // no trig micro-timings at all
1424    /// [
1425    ///     // trig 1
1426    ///     [
1427    ///         0,   // trig counts (number)
1428    ///         0,   // trig condition (enum rep)
1429    ///     ],
1430    ///     // trig 2
1431    ///     [
1432    ///         224, // trig counts (max value)
1433    ///         64,  // trig condition (max value)
1434    ///     ],
1435    ///     // trig 3
1436    ///     [
1437    ///         32,  // trig counts (minimum non-zero value)
1438    ///         1,   // trig condition (minimum non-zero value)
1439    ///     ],
1440    ///     // ... and so on
1441    /// ];
1442    /// ```
1443    ///
1444    /// Trig Offsets
1445    /// ====
1446    ///
1447    /// Trig Offset values use both of these interleaved bytes on top of the
1448    /// trig repeat and trig condition values... Which makes life more complex
1449    /// and somewhat frustrating.
1450    ///
1451    /// Inspected values
1452    /// - -23/384 -> 1st byte 20, 2nd byte 128
1453    /// - -1/32 -> 1st byte 26, 2nd byte 0
1454    /// - -1/64 -> 1st byte 29, 2nd byte 0
1455    /// - -1/128 -> 1st byte 30, 2nd byte 128
1456    /// - 1/128 -> 1st byte 1, 2nd byte 128
1457    /// - 1/64 -> 1st byte 3, 2nd byte 0
1458    /// - 1/32 -> 1st byte 6, 2nd byte 0
1459    /// - 23/384 -> 1st byte 11, 2nd byte 128
1460    ///
1461    /// #### 1st byte
1462    /// The 1st byte only has 31 possible values: 255 - 224 (trig count max) = 31.
1463    /// So it makes sense sort of that this is a mask? I guess?
1464    ///
1465    /// #### 2nd byte
1466    /// From what I can tell, the second offset byte is either 0 or 128.
1467    /// So a 2nd byte for an offset adjusted trig with a `8:8` trig condition is either
1468    /// - 128 + 64 = 192
1469    /// - 0 + 64 = 64
1470    ///
1471    /// So you will need to a `x.rem_euclid(128)` somewhere if you want to parse this.
1472    ///
1473    /// Combining the trig offset with trig count and trig conditions, we end up with
1474    /// ```rust
1475    /// [
1476    ///     // trig one, -23/384 offset with 1x trig count and None condition
1477    ///     [
1478    ///         20,  // 20 + (32 * 0)
1479    ///         128, // 128 + 0
1480    ///     ],
1481    ///     // trig two, -23/384 offset with 2x trig count and Fill condition
1482    ///     [
1483    ///         52,  // 20 + (32 * 1)
1484    ///         129, // 128 + 1
1485    ///     ],
1486    ///     // trig three, -23/384 offset with 3x trig count and Fill condition
1487    ///     [
1488    ///         84,  // 20 + (32 * 2)
1489    ///         129, // 128 + 1
1490    ///     ],
1491    ///     // trig four, -23/384 offset with 3x trig count and NotFill condition
1492    ///     [
1493    ///         84,  // 20 + (32 * 2)
1494    ///         130, // 128 + 2
1495    ///     ],
1496    ///     // trig five, +1/32 offset with 2x trig count and Fill condition
1497    ///     [
1498    ///         38,  // 6 + (32 * 1)
1499    ///         1,   // 0 + 1
1500    ///     ],
1501    ///     // trig six, +1/32 offset with 3x trig count and Fill condition
1502    ///     [
1503    ///         70,  // 6 + (32 * 2)
1504    ///         1,   // 0 + 1
1505    ///     ],
1506    ///     // trig seven, +1/32 offset with 3x trig count and NotFill condition
1507    ///     [
1508    ///         70,  // 6 + (32 * 2)
1509    ///         2,   // 0 + 2
1510    ///     ],
1511    ///     // .... and so on
1512    /// ];
1513    /// ```
1514    ///
1515    /// #### Extending pages and offsets
1516    ///
1517    /// If you have a trig offset on Trig 1 with only one pattern page activated,
1518    /// the trig offsets for Trig 1 are replicated over the relevant trig
1519    /// positions for each first trig in the inactive pages in this array.
1520    ///
1521    /// So, for a 1/32 offset on trig 1 with only one page active, you get the
1522    /// following values showing up in this array:
1523    /// - pair of bytes at array index 15 -> 1/32
1524    /// - pair of bytes at array index 31 -> 1/32
1525    /// - pair of bytes at array index 47 -> 1/32
1526    ///
1527    /// This does not happen for offset values at any other trig position
1528    /// (from what I can tell in my limited testing -- trig values 2-4 and 9-11
1529    /// inclusive are not replicated in the same way).
1530    ///
1531    /// This 'replicating trig offset values over unused pages' behaviour does
1532    /// not happen for trig counts. I haven't tested whether this applies to trig
1533    /// conditions yet.
1534    ///
1535    /// It seems that this behaviour could be to make sure the octatrack plays
1536    /// correctly offset trigs when you extend a page live, i.e. when extending
1537    /// a one-page pattern to a two-page pattern, if there is a negative offset
1538    /// value there the octatrack will need to play the offset trig before the
1539    /// first page has completed.
1540    ///
1541    /// Or it could be a bug :shrug:
1542    #[serde(with = "BigArray")]
1543    pub trig_offsets_repeats_conditions: [[u8; 2]; 64],
1544}
1545
1546impl AudioTrackTrigs {
1547    /// WARNING: This `default` method is not from the `Default` trait, as we
1548    /// cannot use a default struct instance to create an array/vector of
1549    /// midi track trig data --> the individual default depends on their
1550    /// position in the final array!
1551    ///
1552    /// In the future, maybe it might be worth creating `default_with` and
1553    /// `defaults_with` methods to deal with this. But it's not clear they are
1554    /// needed just yet. 80/20.
1555    fn default(id: u8) -> Self {
1556        assert!(id < 8);
1557        Self {
1558            header: AUDIO_TRACK_HEADER,
1559            unknown_1: from_fn(|_| 0),
1560            track_id: id,
1561            trig_masks: AudioTrackTrigMasks::default(),
1562            scale_per_track_mode: TrackPerTrackModeScale::default(),
1563            swing_amount: 0,
1564            pattern_settings: TrackPatternSettings::default(),
1565            unknown_2: 0,
1566            plocks: AudioTrackParameterLocks::defaults(),
1567            unknown_3: from_fn(|_| 0),
1568            trig_offsets_repeats_conditions: from_fn(|_| [0, 0]),
1569        }
1570    }
1571
1572    /// WARNING: This `defaults` method is not from the `Defaults` trait, as we
1573    /// cannot use a default struct instance to create an array/vector of
1574    /// machine slots data --> the individual default depends on their position
1575    /// in the final array!
1576    ///
1577    /// In the future, maybe it might be worth creating `default_with` and
1578    /// `defaults_with` methods to deal with this. But it's not clear they are
1579    /// needed just yet. 80/20.
1580    pub fn defaults<const N: usize>() -> [Self; N] {
1581        from_fn(|x| Self::default(x as u8))
1582    }
1583}
1584
1585impl CheckHeader for AudioTrackTrigs {
1586    fn check_header(&self) -> bool {
1587        self.header == AUDIO_TRACK_HEADER
1588    }
1589}
1590
1591/// MIDI Track Trig masks.
1592/// Can be converted into an array of booleans using the `get_track_trigs_from_bitmasks` function.
1593/// See `AudioTrackTrigMasks` for more information.
1594///
1595/// Trig mask arrays have data stored in this order, which is slightly confusing (pay attention to the difference with 7 + 8!):
1596/// 1. 1st half of the 4th page
1597/// 2. 2nd half of the 4th page
1598/// 3. 1st half of the 3rd page
1599/// 4. 2nd half of the 3rd page
1600/// 5. 1st half of the 2nd page
1601/// 6. 2nd half of the 2nd page
1602/// 7. 2nd half of the 1st page
1603/// 8. 1st half of the 1st page
1604#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Copy)]
1605pub struct MidiTrackTrigMasks {
1606    /// Note Trig masks.
1607    #[serde(with = "BigArray")]
1608    pub trigger: [u8; 8],
1609
1610    /// Trigless Trig masks.
1611    #[serde(with = "BigArray")]
1612    pub trigless: [u8; 8],
1613
1614    /// Parameter Lock Trig masks.
1615    /// Note this only stores data for exclusive parameter lock *trigs* (light green trigs).
1616    #[serde(with = "BigArray")]
1617    pub plock: [u8; 8],
1618
1619    /// Swing trigs mask.
1620    #[serde(with = "BigArray")]
1621    pub swing: [u8; 8],
1622
1623    /// this is a block of 8, so looks like a trig mask for tracks,
1624    /// but I can't think of what it could be.
1625    #[serde(with = "BigArray")]
1626    pub unknown: [u8; 8],
1627}
1628
1629impl Default for MidiTrackTrigMasks {
1630    fn default() -> Self {
1631        Self {
1632            trigger: from_fn(|_| 0),
1633            trigless: from_fn(|_| 0),
1634            plock: from_fn(|_| 0),
1635            swing: from_fn(|_| 170),
1636            unknown: from_fn(|_| 0),
1637        }
1638    }
1639}
1640
1641/// Track trigs assigned on an Audio Track within a Pattern
1642#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
1643pub struct MidiTrackTrigs {
1644    /// Header data section
1645    ///
1646    /// example data:
1647    /// ```text
1648    /// MTRA
1649    /// 4d 54 52 41
1650    /// ```
1651    #[serde(with = "BigArray")]
1652    pub header: [u8; 4],
1653
1654    /// Unknown data.
1655    #[serde(with = "BigArray")]
1656    pub unknown_1: [u8; 4],
1657
1658    /// The zero indexed track number
1659    pub track_id: u8,
1660
1661    /// MIDI Track Trig masks contain the Trig step locations for different trig types
1662    pub trig_masks: MidiTrackTrigMasks,
1663
1664    /// The scale of this MIDI Track in Per Track Pattern mode.
1665    pub scale_per_track_mode: TrackPerTrackModeScale,
1666
1667    /// Amount of swing when a Swing Trig is active for the Track.
1668    /// Maximum is `30` (`80` on device), minimum is `0` (`50` on device).
1669    pub swing_amount: u8,
1670
1671    /// Pattern settings for this MIDI Track
1672    pub pattern_settings: TrackPatternSettings,
1673
1674    /// trig properties -- p-locks etc.
1675    /// the big `0xff` value block within tracks basically.
1676    /// 32 bytes per trig -- 6x parameters for 5x pages plus 2x extra fields at the end.
1677    ///
1678    /// For audio tracks, the 2x extra fields at the end are for sample locks,
1679    /// but there's no such concept for MIDI tracks.
1680    /// It seems like Elektron devs reused their data structures for P-Locks on both Audio + MIDI tracks.
1681    // note -- stack overflow if trying to use #[serde(with = "BigArray")]
1682    pub plocks: Box<Array<MidiTrackParameterLocks, 64>>,
1683
1684    /// See the documentation for `AudioTrackTrigs` on how this field works.
1685    #[serde(with = "BigArray")]
1686    pub trig_offsets_repeats_conditions: [[u8; 2]; 64],
1687}
1688
1689impl MidiTrackTrigs {
1690    /// WARNING: This `default` method is not from the `Default` trait, as we
1691    /// cannot use a default struct instance to create an array/vector of
1692    /// midi track trig data --> the individual default depends on their
1693    /// position in the final array!
1694    ///
1695    /// In the future, maybe it might be worth creating `default_with` and
1696    /// `defaults_with` methods to deal with this. But it's not clear they are
1697    /// needed just yet. 80/20.
1698    fn default(id: u8) -> Self {
1699        // TODO: create an ot-tools error
1700        assert!(id < 8);
1701        Self {
1702            header: MIDI_TRACK_HEADER,
1703            unknown_1: from_fn(|_| 0),
1704            track_id: id,
1705            trig_masks: MidiTrackTrigMasks::default(),
1706            scale_per_track_mode: TrackPerTrackModeScale::default(),
1707            swing_amount: 0,
1708            pattern_settings: TrackPatternSettings::default(),
1709            plocks: MidiTrackParameterLocks::defaults(),
1710            trig_offsets_repeats_conditions: from_fn(|_| [0, 0]),
1711        }
1712    }
1713
1714    /// WARNING: This `defaults` method is not from the `Defaults` trait, as we
1715    /// cannot use a default struct instance to create an array/vector of
1716    /// machine slots data --> the individual default depends on their position
1717    /// in the final array!
1718    ///
1719    /// In the future, maybe it might be worth creating `default_with` and
1720    /// `defaults_with` methods to deal with this. But it's not clear they are
1721    /// needed just yet. 80/20.
1722    pub fn defaults<const N: usize>() -> [Self; N] {
1723        from_fn(|x| Self::default(x as u8))
1724    }
1725}
1726
1727impl CheckHeader for MidiTrackTrigs {
1728    fn check_header(&self) -> bool {
1729        self.header == MIDI_TRACK_HEADER
1730    }
1731}
1732
1733/// Pattern level scaling settings.
1734/// Some of these settings still apply when the pattern is in Per-Track scaling mode.
1735#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
1736pub struct PatternScaleSettings {
1737    /// Multiply this value by `master_len_per_track` to get
1738    /// the real Master Length in Per Track Pattern mode.
1739    ///
1740    /// This field must be set to `255` when Master Length in
1741    /// Per Track Pattern mode is set to `INF`.
1742    ///
1743    /// ```text
1744    /// 0: From 2 steps to 255 steps.
1745    /// 1: From 256 steps to 511 steps.
1746    /// 2: From 512 steps to 767 steps.
1747    /// 3: From 768 steps to 1023 steps.
1748    /// 4: 1024 steps only.
1749    /// 255: `INF`.
1750    /// ```
1751    pub master_len_per_track_multiplier: u8,
1752
1753    /// Master Length in Per Track Pattern mode.
1754    /// Must multiply this by multiplier like this `(x + 1) * (mult + 1)` to get the real number.
1755    ///
1756    /// This field must be set to `255` when Master Length in
1757    /// Per Track Pattern mode is set to `INF`.
1758    ///
1759    /// Minimum value is 2 when the multiplier equals 0.
1760    pub master_len_per_track: u8,
1761
1762    /// The Audio Track's Scale when Pattern is in Per Track mode.
1763    ///
1764    /// Options
1765    /// ```text
1766    /// 0 -> 2x
1767    /// 1 -> 3/2x
1768    /// 2 -> 1x (Default)
1769    /// 3 -> 3/4x
1770    /// 4 -> 1/2x
1771    /// 5 -> 1/4x
1772    /// 6 -> 1/8x
1773    /// ```
1774    pub master_scale_per_track: u8,
1775
1776    /// Master Pattern Length.
1777    /// Determines Pattern Length for all Tracks when NOT in Per Track mode.
1778    pub master_len: u8,
1779
1780    /// Master Pattern playback multiplier.
1781    ///
1782    /// Options
1783    /// ```text
1784    /// 0 -> 2x
1785    /// 1 -> 3/2x
1786    /// 2 -> 1x (Default)
1787    /// 3 -> 3/4x
1788    /// 4 -> 1/2x
1789    /// 5 -> 1/4x
1790    /// 6 -> 1/8x
1791    /// ```
1792    pub master_scale: u8,
1793
1794    /// Scale mode for the Pattern.
1795    ///
1796    /// Options
1797    /// ```text
1798    /// NORMAL: 0 (Default)
1799    /// PER TRACK: 1
1800    /// ```
1801    pub scale_mode: u8,
1802}
1803
1804impl Default for PatternScaleSettings {
1805    fn default() -> Self {
1806        Self {
1807            master_len_per_track_multiplier: 0,
1808            master_len_per_track: 16,
1809            master_scale_per_track: 2,
1810            master_len: 16,
1811            master_scale: 2,
1812            scale_mode: 0,
1813        }
1814    }
1815}
1816
1817/// Chaining behaviour for the pattern.
1818#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
1819pub struct PatternChainBehavior {
1820    /// When `use_project_setting` field is set to `1`/`true`
1821    /// this field should be set to `N/A` with a value of `255`.
1822    pub use_pattern_setting: u8,
1823
1824    /// Pattern Chain Behaviour -- Use the Project level setting for chain
1825    /// behaviour and disable any Pattern level chaining behaviour.
1826    /// Numeric Boolean.
1827    /// When this is `1` the `use_pattern_setting` should be set to `255`.
1828    pub use_project_setting: u8,
1829}
1830
1831// allow the verbose implementation to keep things
1832// - (a) standardized across all types
1833// - (b) easier for non-rustaceans to follow when reading through data structures
1834#[allow(clippy::derivable_impls)]
1835impl Default for PatternChainBehavior {
1836    fn default() -> Self {
1837        Self {
1838            use_pattern_setting: 0,
1839            use_project_setting: 0,
1840        }
1841    }
1842}
1843
1844/// A pattern of trigs stored in the bank.
1845#[derive(PartialEq, Debug, Serialize, Deserialize, Clone, DefaultsAsBoxedBigArray)]
1846pub struct Pattern {
1847    /// Header indicating start of pattern section
1848    ///
1849    /// example data:
1850    /// ```text
1851    /// PTRN....
1852    /// 50 54 52 4e 00 00 00 00
1853    /// ```
1854    #[serde(with = "BigArray")]
1855    pub header: [u8; 8],
1856
1857    /// Audio Track data
1858    #[serde(with = "BigArray")]
1859    pub audio_track_trigs: [AudioTrackTrigs; 8],
1860
1861    /// MIDI Track data
1862    #[serde(with = "BigArray")]
1863    pub midi_track_trigs: [MidiTrackTrigs; 8],
1864
1865    /// Pattern scaling controls and settings
1866    pub scale: PatternScaleSettings,
1867
1868    /// Pattern chaining behaviour and settings
1869    pub chain_behaviour: PatternChainBehavior,
1870
1871    /// Unknown data.
1872    pub unknown: u8,
1873
1874    /// The Part of a Bank assigned to a Pattern.
1875    /// Part 1 = 0; Part 2 = 1; Part 3 = 2; Part 4 = 3.
1876    /// Credit to [@sezare56 on elektronauts for catching this one](https://www.elektronauts.com/t/octalib-a-simple-octatrack-librarian/225192/27)
1877    pub part_assignment: u8,
1878
1879    /// Pattern setting for Tempo.
1880    ///
1881    /// The Tempo value is split across both `tempo_1` and `tempo_2`.
1882    /// Yet to figure out how they relate to each other.
1883    ///
1884    /// Value of 120 BPM is 11 for this field.
1885    /// Value of 30 BPM is 2 for this field.
1886    pub tempo_1: u8,
1887
1888    /// Pattern setting for Tempo.
1889    ///
1890    /// The Tempo value is split across both `tempo_1` and `tempo_2`.
1891    /// Tet to figure out how they relate to each other.
1892    ///
1893    /// Value of 120 BPM is `64` for this field.
1894    /// Value of 30 BPM is `208` for this field.
1895    pub tempo_2: u8,
1896}
1897
1898impl Default for Pattern {
1899    fn default() -> Self {
1900        Self {
1901            header: PATTERN_HEADER,
1902            audio_track_trigs: AudioTrackTrigs::defaults(),
1903            midi_track_trigs: MidiTrackTrigs::defaults(),
1904            scale: PatternScaleSettings::default(),
1905            chain_behaviour: PatternChainBehavior::default(),
1906            unknown: 0,
1907            part_assignment: 0,
1908            // **I believe** these two mask values make the tempo 120.0 BPM
1909            // don't quote me on that though
1910            tempo_1: 11,
1911            tempo_2: 64,
1912        }
1913    }
1914}
1915
1916impl CheckHeader for Pattern {
1917    fn check_header(&self) -> bool {
1918        self.header == PATTERN_HEADER
1919    }
1920}
1921
1922#[cfg(test)]
1923#[allow(unused_imports)]
1924mod test {
1925
1926    mod track_trig_defaults {
1927
1928        mod audio {
1929            use crate::patterns::AudioTrackTrigs;
1930            use crate::RBoxErr;
1931
1932            fn wrap_err(track_id: u8) -> RBoxErr<AudioTrackTrigs> {
1933                Ok(AudioTrackTrigs::default(track_id))
1934            }
1935
1936            #[test]
1937            fn ok_track_id_zero() {
1938                assert!(wrap_err(0).is_ok());
1939            }
1940
1941            #[test]
1942            fn ok_track_id_seven() {
1943                assert!(wrap_err(7).is_ok());
1944            }
1945
1946            // todo: proper error handling (don't use an assert!() in the default method)
1947            #[test]
1948            #[should_panic]
1949            fn err_default_track_id_eight() {
1950                assert!(wrap_err(8).is_err());
1951            }
1952        }
1953        mod midi {
1954            use crate::patterns::MidiTrackTrigs;
1955            use crate::RBoxErr;
1956
1957            fn wrap_err(track_id: u8) -> RBoxErr<MidiTrackTrigs> {
1958                Ok(MidiTrackTrigs::default(track_id))
1959            }
1960
1961            #[test]
1962            fn ok_track_id_zero() {
1963                assert!(wrap_err(0).is_ok());
1964            }
1965
1966            #[test]
1967            fn ok_track_id_seven() {
1968                assert!(wrap_err(7).is_ok());
1969            }
1970
1971            // todo: proper error handling (don't use an assert!() in the default method)
1972            #[test]
1973            #[should_panic]
1974            fn err_default_track_id_eight() {
1975                assert!(wrap_err(8).is_err());
1976            }
1977        }
1978    }
1979
1980    mod integrity {
1981        mod pattern {
1982            // valid header: [0x50, 0x54, 0x52, 0x4e, 0x00, 0x00, 0x00, 0x00];
1983            use crate::patterns::Pattern;
1984            use crate::CheckHeader;
1985
1986            #[test]
1987            fn true_valid_header() {
1988                let pattern = Pattern::default();
1989                assert!(pattern.check_header());
1990            }
1991
1992            #[test]
1993            fn false_invalid_header() {
1994                let mut pattern = Pattern::default();
1995                pattern.header[0] = 0x01;
1996                pattern.header[1] = 0x01;
1997                pattern.header[7] = 0x50;
1998                assert!(!pattern.check_header());
1999            }
2000        }
2001        mod audio_track_trigs {
2002            use crate::patterns::AudioTrackTrigs;
2003            use crate::CheckHeader;
2004
2005            #[test]
2006            fn true_valid_header() {
2007                let trigs = AudioTrackTrigs::default(0);
2008                assert!(trigs.check_header());
2009            }
2010
2011            #[test]
2012            fn false_invalid_header() {
2013                let mut trigs = AudioTrackTrigs::default(0);
2014                trigs.header[0] = 0x01;
2015                trigs.header[1] = 0x01;
2016                trigs.header[2] = 0x50;
2017                assert!(!trigs.check_header());
2018            }
2019        }
2020        mod midi_track_trigs {
2021            use crate::patterns::MidiTrackTrigs;
2022            use crate::CheckHeader;
2023
2024            #[test]
2025            fn true_valid_header() {
2026                let trigs = MidiTrackTrigs::default(0);
2027                assert!(trigs.check_header());
2028            }
2029
2030            #[test]
2031            fn false_invalid_header() {
2032                let mut trigs = MidiTrackTrigs::default(0);
2033                trigs.header[0] = 0x01;
2034                trigs.header[1] = 0x01;
2035                trigs.header[2] = 0x50;
2036                assert!(!trigs.check_header());
2037            }
2038        }
2039    }
2040}