use crate::generics::{Tracks, Trigs};
use crate::identifiers::TrackId;
use crate::parts::{AudioTrackAmpParamsValues, AudioTrackFxParamsValues, LfoParamsValues};
use crate::patterns::settings::{TrackPatternSettings, TrackPerTrackModeScale};
use crate::patterns::tracks::TrigRepeatsConditionsAndOffsets;
use crate::settings::TrigCondition;
use crate::{Defaults, HasHeaderField, OtToolsIoError};
use itertools::Itertools;
use ot_tools_io_derive::{
ArrayDefaults, AsMutDerive, AsRefDerive, BoxedArrayDefaults, IsDefaultCheck,
};
use serde::{Deserialize, Serialize};
use serde_big_array::BigArray;
use std::array::from_fn;
const AUDIO_TRACK_HEADER: [u8; 4] = [0x54, 0x52, 0x41, 0x43];
#[derive(
Copy,
Clone,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd,
Serialize,
Deserialize,
AsMutDerive,
AsRefDerive,
)]
pub struct AudioTrackParameterLockPlayback {
pub param1: u8,
pub param2: u8,
pub param3: u8,
pub param4: u8,
pub param5: u8,
pub param6: u8,
}
impl Default for AudioTrackParameterLockPlayback {
fn default() -> Self {
Self {
param1: 255,
param2: 255,
param3: 255,
param4: 255,
param5: 255,
param6: 255,
}
}
}
#[derive(
Copy,
Clone,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd,
Serialize,
Deserialize,
AsMutDerive,
AsRefDerive,
ArrayDefaults,
BoxedArrayDefaults,
IsDefaultCheck,
)]
pub struct AudioTrackParameterLocks {
pub machine: AudioTrackParameterLockPlayback,
pub lfo: LfoParamsValues,
pub amp: AudioTrackAmpParamsValues,
pub fx1: AudioTrackFxParamsValues,
pub fx2: AudioTrackFxParamsValues,
pub flex_slot_id: u8,
pub static_slot_id: u8,
}
impl Default for AudioTrackParameterLocks {
fn default() -> Self {
Self {
machine: AudioTrackParameterLockPlayback {
param1: 255,
param2: 255,
param3: 255,
param4: 255,
param5: 255,
param6: 255,
},
lfo: LfoParamsValues {
spd1: 255,
spd2: 255,
spd3: 255,
dep1: 255,
dep2: 255,
dep3: 255,
},
amp: AudioTrackAmpParamsValues {
atk: 255,
hold: 255,
rel: 255,
vol: 255,
bal: 255,
f: 255,
},
fx1: AudioTrackFxParamsValues {
param_1: 255,
param_2: 255,
param_3: 255,
param_4: 255,
param_5: 255,
param_6: 255,
},
fx2: AudioTrackFxParamsValues {
param_1: 255,
param_2: 255,
param_3: 255,
param_4: 255,
param_5: 255,
param_6: 255,
},
static_slot_id: 255,
flex_slot_id: 255,
}
}
}
#[derive(
Copy,
Clone,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd,
Serialize,
Deserialize,
AsMutDerive,
AsRefDerive,
)]
pub struct AudioTrackTrigMasks {
pub trigger: Tracks<u8>,
pub trigless: Tracks<u8>,
pub plock: Tracks<u8>,
pub oneshot: Tracks<u8>,
#[serde(with = "BigArray")]
pub recorder: [u8; 32],
pub swing: Tracks<u8>,
pub slide: Tracks<u8>,
}
impl Default for AudioTrackTrigMasks {
fn default() -> Self {
Self {
trigger: Tracks::new(from_fn(|_| 0)),
trigless: Tracks::new(from_fn(|_| 0)),
plock: Tracks::new(from_fn(|_| 0)),
oneshot: Tracks::new(from_fn(|_| 0)),
recorder: from_fn(|_| 0),
swing: Tracks::new(from_fn(|_| 170)),
slide: Tracks::new(from_fn(|_| 0)),
}
}
}
#[derive(
Clone,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd,
Serialize,
Deserialize,
AsMutDerive,
AsRefDerive,
BoxedArrayDefaults,
)]
pub struct AudioTrackTrigs {
#[serde(with = "BigArray")]
pub header: [u8; 4],
#[serde(with = "BigArray")]
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: Trigs<AudioTrackParameterLocks>,
pub unknown_3: Trigs<u8>,
pub trig_offsets_repeats_conditions: Trigs<TrigRepeatsConditionsAndOffsets>,
}
impl AudioTrackTrigs {
pub fn track_id(&self) -> Option<TrackId> {
TrackId::try_from(self.track_id).ok()
}
pub fn trigs(self) -> AudioTrackTrigMasks {
self.trig_masks
}
pub fn trigs_ref(&self) -> &AudioTrackTrigMasks {
&self.trig_masks
}
pub fn trigs_mut(&mut self) -> &mut AudioTrackTrigMasks {
&mut self.trig_masks
}
pub fn swing_amount(&self) -> u8 {
self.swing_amount + 50
}
#[allow(dead_code)]
fn trig_offsets(&self) -> [u8; 64] {
todo!()
}
#[allow(dead_code)]
fn trig_counts(&self) -> [u8; 64] {
todo!()
}
pub fn trig_conditions(&self) -> Trigs<TrigCondition> {
let trigs = self
.trig_offsets_repeats_conditions
.iter()
.map(|x| x.condition)
.map(|x| TrigCondition::try_from(x).unwrap())
.collect_array()
.unwrap();
Trigs::new(trigs)
}
}
impl Default for AudioTrackTrigs {
fn default() -> Self {
Self {
header: AUDIO_TRACK_HEADER,
unknown_1: from_fn(|_| 0),
track_id: 0,
trig_masks: AudioTrackTrigMasks::default(),
scale_per_track_mode: TrackPerTrackModeScale::default(),
swing_amount: 0,
pattern_settings: TrackPatternSettings::default(),
unknown_2: 0,
plocks: Trigs::<AudioTrackParameterLocks>::default(),
unknown_3: Trigs::<u8>::new(from_fn(|_| 0)),
trig_offsets_repeats_conditions: Trigs::<TrigRepeatsConditionsAndOffsets>::default(),
}
}
}
impl<const N: usize> Defaults<[Self; N]> for AudioTrackTrigs {
fn defaults() -> [Self; N]
where
Self: Default,
{
from_fn(|i| Self {
track_id: i as u8,
..Default::default()
})
}
}
#[cfg(test)]
mod audio_track_trigs_defaults {
use super::AudioTrackTrigs;
use crate::Defaults;
fn defs() -> [AudioTrackTrigs; 8] {
AudioTrackTrigs::defaults()
}
#[test]
fn ok_track_ids() -> Result<(), ()> {
for i in 0..8 {
println!("Track: {} Track ID: {i}", i + 1);
assert_eq!(defs()[i].track_id, i as u8);
}
Ok(())
}
}
impl HasHeaderField for AudioTrackTrigs {
fn check_header(&self) -> Result<bool, OtToolsIoError> {
Ok(self.header == AUDIO_TRACK_HEADER)
}
}
#[cfg(test)]
mod audio_track_trigs_header {
use super::AudioTrackTrigs;
use crate::{
test_utils::get_blank_proj_dirpath, BankFile, HasHeaderField, OctatrackFileIO,
OtToolsIoError,
};
#[test]
fn file_read_valid() -> Result<(), OtToolsIoError> {
let path = get_blank_proj_dirpath().join("bank01.work");
let x = BankFile::from_data_file(&path)?.patterns[0]
.clone()
.audio_track_trigs;
assert!(x[0].check_header()?);
Ok(())
}
#[test]
fn file_read_invalid() -> Result<(), OtToolsIoError> {
let path = get_blank_proj_dirpath().join("bank01.work");
let x = BankFile::from_data_file(&path)?.patterns[0]
.clone()
.audio_track_trigs;
let mut trigs = x[0].clone();
trigs.header[0] = 254;
trigs.header[1] = 254;
trigs.header[2] = 254;
trigs.header[3] = 254;
assert!(!trigs.check_header()?);
Ok(())
}
#[test]
fn default_valid() -> Result<(), OtToolsIoError> {
let trigs = AudioTrackTrigs::default();
assert!(trigs.check_header()?);
Ok(())
}
#[test]
fn default_invalid() -> Result<(), OtToolsIoError> {
let mut trigs = AudioTrackTrigs::default();
trigs.header[0] = 0x01;
trigs.header[1] = 0x01;
trigs.header[2] = 0x50;
assert!(!trigs.check_header()?);
Ok(())
}
}