torque_tracker_engine/file/impulse_format/
sample.rs

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