old school music tracker audio backend
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}