Skip to main content

ot_tools_io/patterns/
tracks.rs

1/*
2SPDX-License-Identifier: GPL-3.0-or-later
3Copyright © 2024 Mike Robeson [dijksterhuis]
4*/
5
6use crate::Defaults;
7use ot_tools_io_derive::{ArrayDefaults, AsMutDerive, AsRefDerive, BoxedArrayDefaults};
8use serde::{Deserialize, Serialize};
9use std::array::from_fn;
10
11pub(crate) mod audio;
12pub(crate) mod midi;
13
14/// # Trig Repeats, Conditions and Offsets
15///
16/// This is ..... a slightly frustrating implementation.
17///
18/// There is a 64 length array consisting of a pair of bytes.
19/// Each pair of elements actually represents three different types of data...
20/// Trig Counts and Trig Conditions use the two bytes independently,
21/// so they're easier to explain first.
22///
23/// After that -- see the section on Trig Offsets below, which makes everything more complicated.
24///
25/// ## Trig Counts and Trig Conditions
26///
27/// Trig Counts and Trig Conditions data is interleaved for each trig.
28/// For Trig position 1, array index 0 is the count value and array index 1 is the Trig
29/// Condition.
30///
31/// For trig counts (1st byte), the value (zero-indexed) is multiplied by 32.
32/// - 8 trig counts (7 repeats) --> 7 * 3 = 224
33/// - 4 trig counts (3 repeats) -- 3 * 32 = 96
34/// - 1 trig counts (0 repeats) -- 0 * 32 = 0
35///
36/// For conditionals, see the `TrigCondition` enum and associated traits for more details.
37/// The maximum value for a Trig Condition byte is 64.
38///
39/// ```rust
40/// // no trig micro-timings at all
41/// [
42///     // trig 1
43///     [
44///         0,   // trig counts (number)
45///         0,   // trig condition (enum rep)
46///     ],
47///     // trig 2
48///     [
49///         224, // trig counts (max value)
50///         64,  // trig condition (max value)
51///     ],
52///     // trig 3
53///     [
54///         32,  // trig counts (minimum non-zero value)
55///         1,   // trig condition (minimum non-zero value)
56///     ],
57///     // ... and so on
58/// ];
59/// ```
60///
61/// ## Trig Offsets
62///
63/// Trig Offset values use both of these interleaved bytes on top of the
64/// trig repeat and trig condition values... Which makes life more complex
65/// and somewhat frustrating.
66///
67/// Inspected values
68/// - -23/384 -> 1st byte 20, 2nd byte 128
69/// - -1/32 -> 1st byte 26, 2nd byte 0
70/// - -1/64 -> 1st byte 29, 2nd byte 0
71/// - -1/128 -> 1st byte 30, 2nd byte 128
72/// - 1/128 -> 1st byte 1, 2nd byte 128
73/// - 1/64 -> 1st byte 3, 2nd byte 0
74/// - 1/32 -> 1st byte 6, 2nd byte 0
75/// - 23/384 -> 1st byte 11, 2nd byte 128
76///
77/// ### 1st byte
78/// The 1st byte only has 31 possible values: 255 - 224 (trig count max) = 31.
79/// So it makes sense sort of that this is a mask? I guess?
80///
81/// ### 2nd byte
82/// From what I can tell, the second offset byte is either 0 or 128.
83/// So a 2nd byte for an offset adjusted trig with a `8:8` trig condition is either
84/// - 128 + 64 = 192
85/// - 0 + 64 = 64
86///
87/// So you will need to a `x.rem_euclid(128)` somewhere if you want to parse this.
88///
89/// Combining the trig offset with trig count and trig conditions, we end up with
90/// ```rust
91/// [
92///     // trig one, -23/384 offset with 1x trig count and None condition
93///     [
94///         20,  // 20 + (32 * 0)
95///         128, // 128 + 0
96///     ],
97///     // trig two, -23/384 offset with 2x trig count and Fill condition
98///     [
99///         52,  // 20 + (32 * 1)
100///         129, // 128 + 1
101///     ],
102///     // trig three, -23/384 offset with 3x trig count and Fill condition
103///     [
104///         84,  // 20 + (32 * 2)
105///         129, // 128 + 1
106///     ],
107///     // trig four, -23/384 offset with 3x trig count and NotFill condition
108///     [
109///         84,  // 20 + (32 * 2)
110///         130, // 128 + 2
111///     ],
112///     // trig five, +1/32 offset with 2x trig count and Fill condition
113///     [
114///         38,  // 6 + (32 * 1)
115///         1,   // 0 + 1
116///     ],
117///     // trig six, +1/32 offset with 3x trig count and Fill condition
118///     [
119///         70,  // 6 + (32 * 2)
120///         1,   // 0 + 1
121///     ],
122///     // trig seven, +1/32 offset with 3x trig count and NotFill condition
123///     [
124///         70,  // 6 + (32 * 2)
125///         2,   // 0 + 2
126///     ],
127///     // .... and so on
128/// ];
129/// ```
130///
131/// ### Extending pages and offsets
132///
133/// If you have a trig offset on Trig 1 with only one pattern page activated,
134/// the trig offsets for Trig 1 are replicated over the relevant trig
135/// positions for each first trig in the inactive pages in this array.
136///
137/// So, for a 1/32 offset on trig 1 with only one page active, you get the
138/// following values showing up in this array:
139/// - pair of bytes at array index 15 -> 1/32
140/// - pair of bytes at array index 31 -> 1/32
141/// - pair of bytes at array index 47 -> 1/32
142///
143/// This does not happen for offset values at any other trig position
144/// (from what I can tell in my limited testing -- trig values 2-4 and 9-11
145/// inclusive are not replicated in the same way).
146///
147/// This 'replicating trig offset values over unused pages' behaviour does
148/// not happen for trig counts. I haven't tested whether this applies to trig
149/// conditions yet.
150///
151/// It seems that this behaviour could be to make sure the octatrack plays
152/// correctly offset trigs when you extend a page live, i.e. when extending
153/// a one-page pattern to a two-page pattern, if there is a negative offset
154/// value there the octatrack will need to play the offset trig before the
155/// first page has completed.
156///
157/// Or it could be a bug :shrug:
158#[derive(
159    Clone,
160    Debug,
161    Default,
162    Eq,
163    Hash,
164    Ord,
165    PartialEq,
166    PartialOrd,
167    Serialize,
168    Deserialize,
169    AsMutDerive,
170    AsRefDerive,
171    ArrayDefaults,
172    BoxedArrayDefaults,
173)]
174pub struct TrigRepeatsConditionsAndOffsets {
175    /// Number of trig repeats -- WARNING: also contains interleaved data for trig offsets!
176    pub count: u8,
177    /// Trig trigger probability condition -- WARNING: also contains interleaved data for trig offsets!
178    pub condition: u8,
179}