mod settings;
mod tracks;
use crate::{Defaults, HasHeaderField, OtToolsIoError};
use ot_tools_io_derive::{
ArrayDefaults, AsMutDerive, AsRefDerive, BoxedArrayDefaults, IsDefaultCheck,
};
pub use settings::{
PatternChainBehavior, PatternScaleSettings, TrackPatternSettings, TrackPerTrackModeScale,
};
use std::array::from_fn;
pub use tracks::{
audio::AudioTrackParameterLockPlayback, audio::AudioTrackParameterLocks,
audio::AudioTrackTrigMasks, audio::AudioTrackTrigs, midi::MidiTrackParameterLocks,
midi::MidiTrackTrigMasks, midi::MidiTrackTrigs, TrigRepeatsConditionsAndOffsets,
};
use crate::generics::Tracks;
use serde::{Deserialize, Serialize};
use serde_big_array::BigArray;
const PATTERN_HEADER: [u8; 8] = [0x50, 0x54, 0x52, 0x4e, 0x00, 0x00, 0x00, 0x00];
#[derive(
Clone,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd,
Serialize,
Deserialize,
AsMutDerive,
AsRefDerive,
ArrayDefaults,
BoxedArrayDefaults,
IsDefaultCheck,
)]
pub struct Pattern {
#[serde(with = "BigArray")]
pub header: [u8; 8],
pub audio_track_trigs: Tracks<AudioTrackTrigs>,
pub midi_track_trigs: Tracks<MidiTrackTrigs>,
pub scale: PatternScaleSettings,
pub chain_behaviour: PatternChainBehavior,
pub unknown: u8,
pub part_assignment: u8,
pub tempo_1: u8,
pub tempo_2: u8,
}
impl Default for Pattern {
fn default() -> Self {
Self {
header: PATTERN_HEADER,
audio_track_trigs: Tracks::<AudioTrackTrigs>::default(),
midi_track_trigs: Tracks::<MidiTrackTrigs>::default(),
scale: PatternScaleSettings::default(),
chain_behaviour: PatternChainBehavior::default(),
unknown: 0,
part_assignment: 0,
tempo_1: 11,
tempo_2: 64,
}
}
}
impl HasHeaderField for Pattern {
fn check_header(&self) -> Result<bool, OtToolsIoError> {
Ok(self.header == PATTERN_HEADER)
}
}
#[cfg(test)]
mod pattern_header {
use crate::{
patterns::Pattern, 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 pattern = BankFile::from_data_file(&path)?.patterns[0].clone();
assert!(pattern.check_header()?);
Ok(())
}
#[test]
fn file_read_invalid() -> Result<(), OtToolsIoError> {
let path = get_blank_proj_dirpath().join("bank01.work");
let mut pattern = BankFile::from_data_file(&path)?.patterns[0].clone();
pattern.header[0] = 254;
pattern.header[1] = 254;
pattern.header[2] = 254;
pattern.header[3] = 254;
assert!(!pattern.check_header()?);
Ok(())
}
#[test]
fn default_valid() -> Result<(), OtToolsIoError> {
let pattern = Pattern::default();
assert!(pattern.check_header()?);
Ok(())
}
#[test]
fn default_invalid() -> Result<(), OtToolsIoError> {
let mut pattern = Pattern::default();
pattern.header[0] = 0x01;
pattern.header[1] = 0x01;
pattern.header[7] = 0x50;
assert!(!pattern.check_header()?);
Ok(())
}
}