torque_tracker_engine/file/impulse_format/
instrument.rs1use std::array;
2
3use crate::file::err;
4use crate::file::err::LoadDefect;
5
6#[derive(Debug, Default)]
7pub enum NewNoteAction {
8 #[default]
9 Cut = 0,
10 Continue = 1,
11 NoteOff = 2,
12 NoteFade = 3,
13}
14
15impl TryFrom<u8> for NewNoteAction {
16 type Error = u8;
17
18 fn try_from(value: u8) -> Result<Self, Self::Error> {
19 match value {
20 0 => Ok(Self::Cut),
21 1 => Ok(Self::Continue),
22 2 => Ok(Self::NoteOff),
23 3 => Ok(Self::NoteFade),
24 _ => Err(value),
25 }
26 }
27}
28
29#[derive(Debug, Default)]
30pub enum DuplicateCheckType {
31 #[default]
32 Off = 0,
33 Note = 1,
34 Sample = 2,
35 Instrument = 3,
36}
37
38impl TryFrom<u8> for DuplicateCheckType {
39 type Error = ();
40
41 fn try_from(value: u8) -> Result<Self, Self::Error> {
42 match value {
43 0 => Ok(Self::Off),
44 1 => Ok(Self::Note),
45 2 => Ok(Self::Sample),
46 3 => Ok(Self::Instrument),
47 _ => Err(()),
48 }
49 }
50}
51
52#[derive(Debug, Default)]
53pub enum DuplicateCheckAction {
54 #[default]
55 Cut = 0,
56 NoteOff = 1,
57 NoteFade = 2,
58}
59
60impl TryFrom<u8> for DuplicateCheckAction {
61 type Error = ();
62
63 fn try_from(value: u8) -> Result<Self, Self::Error> {
64 match value {
65 0 => Ok(Self::Cut),
66 1 => Ok(Self::NoteOff),
67 2 => Ok(Self::NoteFade),
68 _ => Err(()),
69 }
70 }
71}
72
73#[derive(Debug)]
74pub struct ImpulseInstrument {
75 pub dos_file_name: [u8; 12],
76 pub new_note_action: NewNoteAction,
77 pub duplicate_check_type: DuplicateCheckType,
78 pub duplicate_check_action: DuplicateCheckAction,
79 pub fade_out: u16,
80 pub pitch_pan_seperation: i8,
81 pub pitch_pan_center: u8,
82 pub global_volume: u8,
83 pub default_pan: Option<u8>,
84 pub random_volume: u8,
85 pub random_pan: u8,
86 pub created_with: u16,
87 pub number_of_samples: u8,
88 pub name: String,
89 pub initial_filter_cutoff: u8,
90 pub initial_filter_resonance: u8,
91 pub midi_channel: u8,
92 pub midi_program: u8,
93 pub midi_bank: u16,
94 pub note_sample_table: [(u8, u8); 120],
95 pub volume_envelope: ImpulseEnvelope,
96 pub pan_envelope: ImpulseEnvelope,
97 pub pitch_envelope: ImpulseEnvelope,
98}
99
100impl ImpulseInstrument {
101 const SIZE: usize = 554;
102
103 pub fn parse<H: FnMut(LoadDefect)>(
104 buf: &[u8; Self::SIZE],
105 defect_handler: &mut H,
106 ) -> Result<Self, err::LoadErr> {
107 if !buf.starts_with(b"IMPI") {
108 return Err(err::LoadErr::Invalid);
109 }
110
111 let dos_file_name: [u8; 12] = buf[0x04..=0x0F].try_into().unwrap();
113
114 if buf[10] != 0 {
115 return Err(err::LoadErr::Invalid);
116 }
117 let new_note_action = match NewNoteAction::try_from(buf[0x11]) {
118 Ok(nna) => nna,
119 Err(_) => {
120 defect_handler(LoadDefect::OutOfBoundsValue);
121 NewNoteAction::default()
122 }
123 };
124 let duplicate_check_type = match DuplicateCheckType::try_from(buf[0x12]) {
125 Ok(dct) => dct,
126 Err(_) => {
127 defect_handler(LoadDefect::OutOfBoundsValue);
128 DuplicateCheckType::default()
129 }
130 };
131 let duplicate_check_action = match DuplicateCheckAction::try_from(buf[0x13]) {
132 Ok(dca) => dca,
133 Err(_) => {
134 defect_handler(LoadDefect::OutOfBoundsValue);
135 DuplicateCheckAction::default()
136 }
137 };
138 let fade_out = u16::from_le_bytes([buf[0x14], buf[0x15]]);
139 let pitch_pan_seperation = {
140 let tmp = i8::from_le_bytes([buf[0x16]]);
141 if !(-32..=32).contains(&tmp) {
142 defect_handler(LoadDefect::OutOfBoundsValue);
143 0
144 } else {
145 tmp
146 }
147 };
148 let pitch_pan_center = if buf[0x17] <= 119 {
149 buf[0x17]
150 } else {
151 defect_handler(LoadDefect::OutOfBoundsValue);
152 59
153 };
154 let global_volume = if buf[0x18] <= 128 {
155 buf[0x18]
156 } else {
157 defect_handler(LoadDefect::OutOfBoundsValue);
158 64
159 };
160
161 let default_pan = if buf[0x19] == 128 {
162 None
163 } else if buf[0x19] > 64 {
164 defect_handler(LoadDefect::OutOfBoundsValue);
165 Some(32)
166 } else {
167 Some(buf[0x19])
168 };
169
170 let random_volume = buf[0x1A];
171 assert!(random_volume <= 100);
172 let random_pan = buf[0x1B];
173 assert!(random_pan <= 100);
174 let created_with = u16::from_le_bytes([buf[0x1C], buf[0x1D]]);
175 let number_of_samples = buf[0x1E];
176
177 let name = String::from_utf8(
178 buf[0x20..=0x39]
179 .split(|b| *b == 0)
180 .next()
181 .unwrap()
182 .to_owned(),
183 )
184 .unwrap();
185
186 let initial_filter_cutoff = buf[0x3A];
187 let initial_filter_resonance = buf[0x3B];
188 let midi_channel = buf[0x3C];
189 let midi_program = buf[0x3D];
190 let midi_bank = u16::from_le_bytes([buf[0x3E], buf[0x3F]]);
191 let note_sample_table: [(u8, u8); 120] = buf[0x030..0x130]
192 .chunks_exact(2)
193 .map(|chunk| (chunk[0], chunk[1]))
194 .collect::<Vec<(u8, u8)>>()
195 .try_into()
196 .unwrap();
197
198 let volume_envelope = ImpulseEnvelope::load(
199 &buf[0x130..0x130 + ImpulseEnvelope::SIZE]
200 .try_into()
201 .unwrap(),
202 );
203 let pan_envelope = ImpulseEnvelope::load(
204 &buf[0x182..0x182 + ImpulseEnvelope::SIZE]
205 .try_into()
206 .unwrap(),
207 );
208 let pitch_envelope = ImpulseEnvelope::load(
209 &buf[0x1D4..0x1D4 + ImpulseEnvelope::SIZE]
210 .try_into()
211 .unwrap(),
212 );
213
214 Ok(Self {
215 dos_file_name,
216 new_note_action,
217 duplicate_check_type,
218 duplicate_check_action,
219 fade_out,
220 pitch_pan_seperation,
221 pitch_pan_center,
222 global_volume,
223 default_pan,
224 random_volume,
225 random_pan,
226 created_with,
227 number_of_samples,
228 name,
229 initial_filter_cutoff,
230 initial_filter_resonance,
231 midi_channel,
232 midi_program,
233 midi_bank,
234 note_sample_table,
235 volume_envelope,
236 pan_envelope,
237 pitch_envelope,
238 })
239 }
240}
241
242#[derive(Debug)]
245pub struct ImpulseEnvelope {
246 flags: u8,
247 num_node_points: u8,
248 loop_start: u8,
249 loop_end: u8,
250 sustain_loop_start: u8,
251 sustain_loop_end: u8,
252 nodes: [(u8, u16); 25],
253}
254
255impl ImpulseEnvelope {
256 const SIZE: usize = 81; fn load(buf: &[u8; Self::SIZE]) -> Self {
259 let flags = buf[0];
260 let num_node_points = buf[1];
261 let loop_start = buf[2];
262 let loop_end = buf[3];
263 let sustain_loop_start = buf[4];
264 let sustain_loop_end = buf[5];
265
266 let nodes = array::from_fn(|idx| {
267 let chunk = 6 + idx * 3;
268 (
269 buf[chunk],
270 u16::from_le_bytes([buf[chunk + 1], buf[chunk + 2]]),
271 )
272 });
273
274 Self {
275 flags,
276 num_node_points,
277 loop_start,
278 loop_end,
279 sustain_loop_start,
280 sustain_loop_end,
281 nodes,
282 }
283 }
284}