pub struct AudioTrackTrigs {
pub header: [u8; 4],
pub unknown_1: [u8; 4],
pub track_id: u8,
pub trig_masks: AudioTrackTrigMasks,
pub scale_per_track_mode: TrackPerTrackModeScale,
pub swing_amount: u8,
pub pattern_settings: TrackPatternSettings,
pub unknown_2: u8,
pub plocks: AudioTrackParameterLocksArray,
pub unknown_3: [u8; 64],
pub trig_offsets_repeats_conditions: [[u8; 2]; 64],
}Expand description
Track trigs assigned on an Audio Track within a Pattern
Fields§
§header: [u8; 4]Header data section
example data:
TRAC
54 52 41 43unknown_1: [u8; 4]Unknown data.
track_id: u8The zero indexed track number
trig_masks: AudioTrackTrigMasksTrig masks contain the Trig step locations for different trig types
scale_per_track_mode: TrackPerTrackModeScaleThe scale of this Audio Track in Per Track Pattern mode.
swing_amount: u8Amount of swing when a Swing Trig is active for the Track.
Maximum is 30 (80 on device), minimum is 0 (50 on device).
pattern_settings: TrackPatternSettingsPattern settings for this Audio Track
unknown_2: u8Unknown data.
plocks: AudioTrackParameterLocksArrayParameter-Lock data for all Trigs.
unknown_3: [u8; 64]What the hell is this field?!?! It has to be something to do with trigs, but i have no idea what it could be.
trig_offsets_repeats_conditions: [[u8; 2]; 64]§Trig Offsets, Trig Counts and Trig Conditions
This is ….. slightly frustrating.
This 64 length array consisting of a pair of bytes for each array element hold three data references… Trig Cunts and Trig Conditions use the two bytes independently, so they’re easier to explain first
§Trig Counts and Trig Conditions
Trig Counts and Trig Conditions data is interleaved for each trig. For Trig position 1, array index 0 is the count value and array index 1 is the Trig Condition.
For trig counts (1st byte), the value (zero-indexed) is multiplied by 32.
- 8 trig counts (7 repeats) –> 7 * 3 = 224
- 4 trig counts (3 repeats) – 3 * 32 = 96
- 1 trig counts (0 repeats) – 0 * 32 = 0
For conditionals, see the TrigCondition enum and associated traits for more details.
The maximum value for a Trig Condition byte is 64.
// no trig micro-timings at all
[
// trig 1
[
0, // trig counts (number)
0, // trig condition (enum rep)
],
// trig 2
[
224, // trig counts (max value)
64, // trig condition (max value)
],
// trig 3
[
32, // trig counts (minimum non-zero value)
1, // trig condition (minimum non-zero value)
],
// ... and so on
];§Trig Offsets
Trig Offset values use both of these interleaved bytes on top of the trig repeat and trig condition values… Which makes life more complex and somewhat frustrating.
Inspected values
- -23/384 -> 1st byte 20, 2nd byte 128
- -1/32 -> 1st byte 26, 2nd byte 0
- -1/64 -> 1st byte 29, 2nd byte 0
- -1/128 -> 1st byte 30, 2nd byte 128
- 1/128 -> 1st byte 1, 2nd byte 128
- 1/64 -> 1st byte 3, 2nd byte 0
- 1/32 -> 1st byte 6, 2nd byte 0
- 23/384 -> 1st byte 11, 2nd byte 128
§1st byte
The 1st byte only has 31 possible values: 255 - 224 (trig count max) = 31. So it makes sense sort of that this is a mask? I guess?
§2nd byte
From what I can tell, the second offset byte is either 0 or 128.
So a 2nd byte for an offset adjusted trig with a 8:8 trig condition is either
- 128 + 64 = 192
- 0 + 64 = 64
So you will need to a x.rem_euclid(128) somewhere if you want to parse this.
Combining the trig offset with trig count and trig conditions, we end up with
[
// trig one, -23/384 offset with 1x trig count and None condition
[
20, // 20 + (32 * 0)
128, // 128 + 0
],
// trig two, -23/384 offset with 2x trig count and Fill condition
[
52, // 20 + (32 * 1)
129, // 128 + 1
],
// trig three, -23/384 offset with 3x trig count and Fill condition
[
84, // 20 + (32 * 2)
129, // 128 + 1
],
// trig four, -23/384 offset with 3x trig count and NotFill condition
[
84, // 20 + (32 * 2)
130, // 128 + 2
],
// trig five, +1/32 offset with 2x trig count and Fill condition
[
38, // 6 + (32 * 1)
1, // 0 + 1
],
// trig six, +1/32 offset with 3x trig count and Fill condition
[
70, // 6 + (32 * 2)
1, // 0 + 1
],
// trig seven, +1/32 offset with 3x trig count and NotFill condition
[
70, // 6 + (32 * 2)
2, // 0 + 2
],
// .... and so on
];§Extending pages and offsets
If you have a trig offset on Trig 1 with only one pattern page activated, the trig offsets for Trig 1 are replicated over the relevant trig positions for each first trig in the inactive pages in this array.
So, for a 1/32 offset on trig 1 with only one page active, you get the following values showing up in this array:
- pair of bytes at array index 15 -> 1/32
- pair of bytes at array index 31 -> 1/32
- pair of bytes at array index 47 -> 1/32
This does not happen for offset values at any other trig position (from what I can tell in my limited testing – trig values 2-4 and 9-11 inclusive are not replicated in the same way).
This ‘replicating trig offset values over unused pages’ behaviour does not happen for trig counts. I haven’t tested whether this applies to trig conditions yet.
It seems that this behaviour could be to make sure the octatrack plays correctly offset trigs when you extend a page live, i.e. when extending a one-page pattern to a two-page pattern, if there is a negative offset value there the octatrack will need to play the offset trig before the first page has completed.
Or it could be a bug :shrug:
Trait Implementations§
Source§impl Clone for AudioTrackTrigs
impl Clone for AudioTrackTrigs
Source§fn clone(&self) -> AudioTrackTrigs
fn clone(&self) -> AudioTrackTrigs
1.0.0 · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreSource§impl Debug for AudioTrackTrigs
impl Debug for AudioTrackTrigs
Source§impl Default for AudioTrackTrigs
impl Default for AudioTrackTrigs
Source§impl<const N: usize> Defaults<[AudioTrackTrigs; N]> for AudioTrackTrigs
impl<const N: usize> Defaults<[AudioTrackTrigs; N]> for AudioTrackTrigs
Source§impl<const N: usize> Defaults<Box<Array<AudioTrackTrigs, N>>> for AudioTrackTrigs
impl<const N: usize> Defaults<Box<Array<AudioTrackTrigs, N>>> for AudioTrackTrigs
Source§impl<'de> Deserialize<'de> for AudioTrackTrigs
impl<'de> Deserialize<'de> for AudioTrackTrigs
Source§fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
Source§impl HasHeaderField for AudioTrackTrigs
impl HasHeaderField for AudioTrackTrigs
Source§fn check_header(&self) -> Result<bool, OtToolsIoError>
fn check_header(&self) -> Result<bool, OtToolsIoError>
Source§impl PartialEq for AudioTrackTrigs
impl PartialEq for AudioTrackTrigs
Source§impl Serialize for AudioTrackTrigs
impl Serialize for AudioTrackTrigs
impl StructuralPartialEq for AudioTrackTrigs
Auto Trait Implementations§
impl Freeze for AudioTrackTrigs
impl RefUnwindSafe for AudioTrackTrigs
impl Send for AudioTrackTrigs
impl Sync for AudioTrackTrigs
impl Unpin for AudioTrackTrigs
impl UnwindSafe for AudioTrackTrigs
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more