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