old school music tracker audio backend
at dev 307 lines 8.5 kB view raw
1// look at player/csndfile.c csf_read_sample 2#![allow(dead_code)] // not nearly done 3 4use std::fmt::Debug; 5use std::num::NonZeroU32; 6 7use crate::{ 8 file::{InFilePtr, err::LoadErr}, 9 sample::VibratoWave, 10}; 11 12use super::header; 13 14fn vibratowave_try_from(value: u8) -> Option<VibratoWave> { 15 match value { 16 0 => Some(VibratoWave::Sine), 17 1 => Some(VibratoWave::RampDown), 18 2 => Some(VibratoWave::Square), 19 3 => Some(VibratoWave::Random), 20 _ => None, 21 } 22} 23 24enum ImpulseSampleBitWidth { 25 Seven, 26 Eight, 27 Sixteen, 28 TwentyFour, 29 ThirtyTwo, 30} 31 32impl From<ImpulseSampleBitWidth> for usize { 33 fn from(value: ImpulseSampleBitWidth) -> Self { 34 match value { 35 ImpulseSampleBitWidth::Seven => 7, 36 ImpulseSampleBitWidth::Eight => 8, 37 ImpulseSampleBitWidth::Sixteen => 16, 38 ImpulseSampleBitWidth::TwentyFour => 24, 39 ImpulseSampleBitWidth::ThirtyTwo => 32, 40 } 41 } 42} 43 44enum ImpulseSampleChannels { 45 Mono, 46 StereoInterleaved, 47 StereoSplit, 48} 49 50enum ImpulseSampleEndianness { 51 Little, 52 Big, 53} 54 55/// don't know what most of them are. Just took them from include/sndfile.h of schism tracker 56enum ImpulseSampleEncoding { 57 PCMSigned, 58 PCMUnsigned, 59 PCMDelta, 60 IT2_14Comp, 61 IT2_15Comp, 62 Ams, 63 Dmf, 64 Mdl, 65 Ptm, 66 PCM16, 67} 68 69/// don't understand what bit 3 is supposed to do 70#[derive(Copy, Clone)] 71pub struct SampleFormatConvert(u8); 72 73impl SampleFormatConvert { 74 // alternative is unsigned 75 pub fn is_signed(self) -> bool { 76 (self.0 & 0x1) != 0 77 } 78 /// should only be true for very old files (isn't even supported in C schism) 79 pub fn is_big_endian(self) -> bool { 80 (self.0 & 0x2) != 0 81 } 82 /// alternative is PCM samples 83 pub fn delta_samples(self) -> bool { 84 (self.0 & 0x4) != 0 85 } 86 /// not supported 87 pub fn byte_delta(self) -> bool { 88 (self.0 & 0x8) != 0 89 } 90 /// don't support in first step 91 pub fn tx_wave_12bit(self) -> bool { 92 (self.0 & 0x10) != 0 93 } 94 /// don't know if this also means that the sample is stereo 95 pub fn should_show_stereo_prompt(self) -> bool { 96 (self.0 & 0x20) != 0 97 } 98} 99 100impl Debug for SampleFormatConvert { 101 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 102 f.debug_struct("SampleFormatConvert") 103 .field("is_signed", &self.is_signed()) 104 .field("is_big_endian", &self.is_big_endian()) 105 .field("delta_sample", &self.delta_samples()) 106 .field("byte_delta", &self.byte_delta()) 107 .field("tx_wave_12bit", &self.tx_wave_12bit()) 108 .field( 109 "should_show_stereo_prompt", 110 &self.should_show_stereo_prompt(), 111 ) 112 .finish() 113 } 114} 115 116#[derive(Copy, Clone)] 117pub struct SampleFormatFlags(u8); 118 119impl SampleFormatFlags { 120 pub fn has_sample(self) -> bool { 121 (self.0 & 0x01) != 0 122 } 123 pub fn is_16bit(self) -> bool { 124 (self.0 & 0x02) != 0 125 } 126 pub fn is_8bit(self) -> bool { 127 !self.is_16bit() 128 } 129 pub fn is_stereo(self) -> bool { 130 (self.0 & 0x04) != 0 131 } 132 pub fn is_compressed(self) -> bool { 133 (self.0 & 0x08) != 0 134 } 135 pub fn uses_loop(self) -> bool { 136 (self.0 & 0x10) != 0 137 } 138 pub fn uses_sustain_loop(self) -> bool { 139 (self.0 & 0x20) != 0 140 } 141 pub fn ping_pong_loop(self) -> bool { 142 (self.0 & 0x40) != 0 143 } 144 pub fn forward_loop(self) -> bool { 145 !self.ping_pong_loop() 146 } 147 pub fn ping_pong_sustain_loop(self) -> bool { 148 (self.0 & 0x80) != 0 149 } 150 pub fn forward_sustain_loop(self) -> bool { 151 !self.ping_pong_sustain_loop() 152 } 153} 154 155impl Debug for SampleFormatFlags { 156 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 157 let mut fmt = f.debug_struct("SampleFormatFlags"); 158 fmt.field("has_sample", &self.has_sample()); 159 fmt.field("is_16bit", &self.is_16bit()); 160 fmt.field("is_stereo", &self.is_stereo()); 161 fmt.field("is_compressed", &self.is_compressed()); 162 fmt.field("uses_loop", &self.uses_loop()); 163 fmt.field("uses_sustain_loop", &self.uses_sustain_loop()); 164 fmt.field("ping_pong_loop", &self.ping_pong_loop()); 165 fmt.field("ping_pong_sustain_loop", &self.ping_pong_sustain_loop()); 166 fmt.finish() 167 } 168} 169 170#[derive(Debug)] 171pub struct ImpulseSampleHeader { 172 pub dos_filename: Box<[u8]>, 173 pub sample_name: String, 174 pub global_volume: u8, 175 pub flags: SampleFormatFlags, 176 pub default_volume: u8, 177 pub convert: SampleFormatConvert, 178 pub default_pan: u8, 179 // frame count 180 pub length: u32, 181 pub loop_start: u32, 182 pub loop_end: u32, 183 pub c5_speed: u32, 184 pub sustain_start: u32, 185 pub sustain_end: u32, 186 pub data_ptr: InFilePtr, 187 pub vibrato_speed: u8, 188 pub vibrato_depth: u8, 189 pub vibrato_type: VibratoWave, 190 pub vibrato_rate: u8, 191} 192 193impl ImpulseSampleHeader { 194 pub const SIZE: usize = 80; 195 196 pub fn parse(buf: &[u8; Self::SIZE]) -> Result<Self, LoadErr> { 197 if !buf.starts_with(b"IMPS") { 198 return Err(LoadErr::Invalid); 199 } 200 201 let dos_filename = buf[0x4..=0xF].to_vec().into_boxed_slice(); 202 if buf[0x10] != 0 { 203 return Err(LoadErr::Invalid); 204 } 205 206 let global_volume = if buf[0x11] > 64 { 207 // defect_handler(LoadDefect::OutOfBoundsValue); 208 64 209 } else { 210 buf[0x11] 211 }; 212 213 let flags = SampleFormatFlags(buf[0x12]); 214 let default_volume = buf[0x13]; 215 let sample_name = { 216 let str = buf[0x14..=0x2D].split(|b| *b == 0).next().unwrap().to_vec(); 217 let str = String::from_utf8(str); 218 if str.is_err() { 219 // defect_handler(LoadDefect::InvalidText); 220 } 221 str.unwrap_or_default() 222 }; 223 224 let convert = SampleFormatConvert(buf[0x2E]); 225 226 let default_pan = buf[0x2F]; 227 228 // in samples, not bytes 229 let length = u32::from_le_bytes([buf[0x30], buf[0x31], buf[0x32], buf[0x33]]); 230 let loop_start = u32::from_le_bytes([buf[0x34], buf[0x35], buf[0x36], buf[0x37]]); 231 let loop_end = u32::from_le_bytes([buf[0x38], buf[0x39], buf[0x3A], buf[0x3B]]); 232 233 // bytes per second at c5 234 let c5_speed = { 235 let speed = u32::from_le_bytes([buf[0x3C], buf[0x3D], buf[0x3E], buf[0x3F]]); 236 if speed > 9999999 { 237 // defect_handler(LoadDefect::OutOfBoundsValue); 238 // no idea what is a good default here 239 9999999 / 2 240 } else { 241 speed 242 } 243 }; 244 245 // in samples, not bytes 246 let sustain_start = u32::from_le_bytes([buf[0x40], buf[0x41], buf[0x42], buf[0x43]]); 247 let sustain_end = u32::from_le_bytes([buf[0x44], buf[0x45], buf[0x46], buf[0x47]]); 248 249 let data_ptr = { 250 let value = u32::from_le_bytes([buf[0x48], buf[0x49], buf[0x4A], buf[0x4B]]); 251 if value < header::ImpulseHeader::BASE_SIZE as u32 { 252 return Err(LoadErr::Invalid); 253 } 254 InFilePtr(NonZeroU32::new(value).unwrap()) 255 }; 256 257 let vibrato_speed = if buf[0x4C] > 64 { 258 // defect_handler(LoadDefect::OutOfBoundsValue); 259 32 260 } else { 261 buf[0x4C] 262 }; 263 264 let vibrato_depth = if buf[0x4D] > 64 { 265 // defect_handler(LoadDefect::OutOfBoundsValue); 266 32 267 } else { 268 buf[0x4D] 269 }; 270 271 let vibrato_rate = if buf[0x4E] > 64 { 272 // defect_handler(LoadDefect::OutOfBoundsValue); 273 32 274 } else { 275 buf[0x4E] 276 }; 277 278 let vibrato_type = { 279 let wave = vibratowave_try_from(buf[0x4F]); 280 if wave.is_none() { 281 // defect_handler(LoadDefect::OutOfBoundsValue); 282 } 283 wave.unwrap_or_default() 284 }; 285 286 Ok(Self { 287 dos_filename, 288 sample_name, 289 global_volume, 290 flags, 291 default_volume, 292 convert, 293 default_pan, 294 length, 295 loop_start, 296 loop_end, 297 c5_speed, 298 sustain_start, 299 sustain_end, 300 data_ptr, 301 vibrato_speed, 302 vibrato_depth, 303 vibrato_type, 304 vibrato_rate, 305 }) 306 } 307}