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 octatack 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 tring 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) standardised 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}