1mod settings;
9mod tracks;
10
11use crate::{Defaults, HasHeaderField, OtToolsIoError};
12use ot_tools_io_derive::{
13 ArrayDefaults, AsMutDerive, AsRefDerive, BoxedArrayDefaults, IsDefaultCheck,
14};
15pub use settings::{
16 PatternChainBehavior, PatternScaleSettings, TrackPatternSettings, TrackPerTrackModeScale,
17};
18use std::array::from_fn;
19pub use tracks::{
20 audio::AudioTrackParameterLockPlayback, audio::AudioTrackParameterLocks,
21 audio::AudioTrackTrigMasks, audio::AudioTrackTrigs, midi::MidiTrackParameterLocks,
22 midi::MidiTrackTrigMasks, midi::MidiTrackTrigs, TrigRepeatsConditionsAndOffsets,
23};
24
25use crate::generics::Tracks;
26use serde::{Deserialize, Serialize};
27use serde_big_array::BigArray;
28
29const PATTERN_HEADER: [u8; 8] = [0x50, 0x54, 0x52, 0x4e, 0x00, 0x00, 0x00, 0x00];
30
31#[derive(
33 Clone,
34 Debug,
35 Eq,
36 Hash,
37 Ord,
38 PartialEq,
39 PartialOrd,
40 Serialize,
41 Deserialize,
42 AsMutDerive,
43 AsRefDerive,
44 ArrayDefaults,
45 BoxedArrayDefaults,
46 IsDefaultCheck,
47)]
48pub struct Pattern {
49 #[serde(with = "BigArray")]
57 pub header: [u8; 8],
58
59 pub audio_track_trigs: Tracks<AudioTrackTrigs>,
61
62 pub midi_track_trigs: Tracks<MidiTrackTrigs>,
64
65 pub scale: PatternScaleSettings,
67
68 pub chain_behaviour: PatternChainBehavior,
70
71 pub unknown: u8,
73
74 pub part_assignment: u8,
78
79 pub tempo_1: u8,
87
88 pub tempo_2: u8,
96}
97
98impl Default for Pattern {
99 fn default() -> Self {
100 Self {
101 header: PATTERN_HEADER,
102 audio_track_trigs: Tracks::<AudioTrackTrigs>::default(),
103 midi_track_trigs: Tracks::<MidiTrackTrigs>::default(),
104 scale: PatternScaleSettings::default(),
105 chain_behaviour: PatternChainBehavior::default(),
106 unknown: 0,
107 part_assignment: 0,
108 tempo_1: 11,
111 tempo_2: 64,
112 }
113 }
114}
115
116impl HasHeaderField for Pattern {
117 fn check_header(&self) -> Result<bool, OtToolsIoError> {
118 Ok(self.header == PATTERN_HEADER)
119 }
120}
121
122#[cfg(test)]
123mod pattern_header {
124 use crate::{
125 patterns::Pattern, test_utils::get_blank_proj_dirpath, BankFile, HasHeaderField,
126 OctatrackFileIO, OtToolsIoError,
127 };
128 #[test]
129 fn file_read_valid() -> Result<(), OtToolsIoError> {
130 let path = get_blank_proj_dirpath().join("bank01.work");
131 let pattern = BankFile::from_data_file(&path)?.patterns[0].clone();
132 assert!(pattern.check_header()?);
133 Ok(())
134 }
135
136 #[test]
137 fn file_read_invalid() -> Result<(), OtToolsIoError> {
138 let path = get_blank_proj_dirpath().join("bank01.work");
139 let mut pattern = BankFile::from_data_file(&path)?.patterns[0].clone();
140 pattern.header[0] = 254;
141 pattern.header[1] = 254;
142 pattern.header[2] = 254;
143 pattern.header[3] = 254;
144 assert!(!pattern.check_header()?);
145 Ok(())
146 }
147
148 #[test]
149 fn default_valid() -> Result<(), OtToolsIoError> {
150 let pattern = Pattern::default();
151 assert!(pattern.check_header()?);
152 Ok(())
153 }
154
155 #[test]
156 fn default_invalid() -> Result<(), OtToolsIoError> {
157 let mut pattern = Pattern::default();
158 pattern.header[0] = 0x01;
159 pattern.header[1] = 0x01;
160 pattern.header[7] = 0x50;
161 assert!(!pattern.check_header()?);
162 Ok(())
163 }
164}