old school music tracker audio backend
1use crate::file::{SansIO, err};
2use crate::project::event_command::NoteCommand;
3use crate::project::note_event::{Note, NoteEvent, VolumeEffect};
4use crate::project::pattern::{InPatternPosition, Pattern};
5use core::error::Error;
6use core::fmt::Display;
7use std::io::Read;
8
9use core::mem;
10
11/// reader should be buffered in some way and not do a syscall on every read call.
12///
13/// This function does a lot of read calls
14pub fn parse_pattern<R: Read>(reader: &mut R) -> Result<Pattern, err::LoadErr> {
15 const PATTERN_HEADER_SIZE: usize = 8;
16
17 let (length, num_rows) = {
18 let mut header = [0; PATTERN_HEADER_SIZE];
19 reader.read_exact(&mut header)?;
20 (
21 u64::from(u16::from_le_bytes([header[0], header[1]])) + PATTERN_HEADER_SIZE as u64,
22 u16::from_le_bytes([header[2], header[3]]),
23 )
24 };
25
26 // a guarantee given by the impulse tracker "specs"
27 if length >= 64_000 {
28 return Err(err::LoadErr::Invalid);
29 }
30
31 if !(32..=200).contains(&num_rows) {
32 return Err(err::LoadErr::Invalid);
33 }
34
35 let mut pattern = Pattern::new(num_rows);
36
37 let mut row_num: u16 = 0;
38
39 let mut last_mask = [0; 64];
40 let mut last_event = [NoteEvent::default(); 64];
41
42 let mut scratch = [0; 1];
43
44 while row_num < num_rows {
45 let channel_variable = scratch[0];
46
47 if channel_variable == 0 {
48 row_num += 1;
49 continue;
50 }
51
52 let channel = (channel_variable - 1) & 63; // 64 channels, 0 based
53 let channel_id = usize::from(channel);
54
55 let maskvar = if (channel_variable & 0b10000000) != 0 {
56 reader.read_exact(&mut scratch)?;
57 let val = scratch[0];
58 last_mask[channel_id] = val;
59 val
60 } else {
61 last_mask[channel_id]
62 };
63
64 let mut event = NoteEvent::default();
65
66 // Note
67 if (maskvar & 0b00000001) != 0 {
68 reader.read_exact(&mut scratch)?;
69 // let note = match Note::new(scratch[0]) {
70 // Ok(n) => n,
71 // Err(_) => {
72 // // defect_handler(LoadDefect::OutOfBoundsValue);
73 // Note::default()
74 // }
75 // };
76 let note = Note::new(scratch[0]).unwrap_or_default();
77
78 event.note = note;
79 last_event[channel_id].note = note;
80 }
81
82 // Instrument / Sample
83 if (maskvar & 0b00000010) != 0 {
84 reader.read_exact(&mut scratch)?;
85 let instrument = scratch[0];
86
87 event.sample_instr = instrument;
88 last_event[channel_id].sample_instr = instrument;
89 }
90
91 // Volume
92 if (maskvar & 0b00000100) != 0 {
93 reader.read_exact(&mut scratch)?;
94 // let vol_pan = match vol_pan_raw.try_into() {
95 // Ok(v) => v,
96 // Err(_) => {
97 // // defect_handler(LoadDefect::OutOfBoundsValue);
98 // VolumeEffect::default()
99 // }
100 // };
101 let vol_pan = volumeeffect_try_from(scratch[0]).unwrap_or_default();
102
103 last_event[channel_id].vol = vol_pan;
104 event.vol = vol_pan;
105 }
106
107 // Effect
108 if (maskvar & 0b00001000) != 0 {
109 reader.read_exact(&mut scratch)?;
110 let command = scratch[0];
111 reader.read_exact(&mut scratch)?;
112 let cmd_val = scratch[0];
113
114 // let cmd = match NoteCommand::try_from((command, cmd_val)) {
115 // Ok(cmd) => cmd,
116 // Err(_) => {
117 // // defect_handler(LoadDefect::OutOfBoundsValue);
118 // NoteCommand::default()
119 // }
120 // };
121 let cmd = note_command_try_from((command, cmd_val)).unwrap_or_default();
122
123 last_event[channel_id].command = cmd;
124 event.command = cmd;
125 }
126
127 // Same note
128 if (maskvar & 0b00010000) != 0 {
129 event.note = last_event[channel_id].note;
130 }
131
132 // Same Instr / Sample
133 if (maskvar & 0b00100000) != 0 {
134 event.sample_instr = last_event[channel_id].sample_instr;
135 }
136
137 // Same volume
138 if (maskvar & 0b01000000) != 0 {
139 event.vol = last_event[channel_id].vol;
140 }
141
142 // Same Command
143 if (maskvar & 0b10000000) != 0 {
144 event.command = last_event[channel_id].command;
145 }
146
147 pattern.set_event(
148 InPatternPosition {
149 row: row_num,
150 channel,
151 },
152 event,
153 );
154 }
155
156 Ok(pattern)
157}
158
159#[derive(Debug, Clone, Copy)]
160pub struct InvalidVolumeEffect;
161
162impl Display for InvalidVolumeEffect {
163 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
164 write!(f, "Invalid Volume Effect")
165 }
166}
167
168impl Error for InvalidVolumeEffect {}
169
170/// IT Tracker Format Conversion
171/// no way to get None, as then it just doesn't get set
172fn volumeeffect_try_from(value: u8) -> Result<VolumeEffect, InvalidVolumeEffect> {
173 match value {
174 0..=64 => Ok(VolumeEffect::Volume(value)),
175 65..=74 => Ok(VolumeEffect::FineVolSlideUp(value - 65)),
176 75..=84 => Ok(VolumeEffect::FineVolSlideDown(value - 75)),
177 85..=94 => Ok(VolumeEffect::VolSlideUp(value - 85)),
178 95..=104 => Ok(VolumeEffect::VolSlideDown(value - 95)),
179 105..=114 => Ok(VolumeEffect::PitchSlideDown(value - 105)),
180 115..=124 => Ok(VolumeEffect::PitchSlideUp(value - 115)),
181 128..=192 => Ok(VolumeEffect::Panning(value - 128)),
182 193..=202 => Ok(VolumeEffect::SlideToNoteWithSpeed(value - 193)),
183 203..=212 => Ok(VolumeEffect::VibratoWithSpeed(value - 203)),
184 _ => Err(InvalidVolumeEffect),
185 }
186}
187
188#[derive(Debug, Clone, Copy)]
189pub struct UnknownNoteCommand;
190
191impl Display for UnknownNoteCommand {
192 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
193 write!(f, "Unknown Note Command")
194 }
195}
196
197impl Error for UnknownNoteCommand {}
198
199fn note_command_try_from(
200 (command_type, command_value): (u8, u8),
201) -> Result<NoteCommand, UnknownNoteCommand> {
202 match command_type {
203 0 => Ok(NoteCommand::None),
204 1 => Ok(NoteCommand::SetTempo(command_value)),
205 2 => Ok(NoteCommand::JumpToOrder(command_value)),
206 3 => Ok(NoteCommand::BreakToRow(command_value)),
207 4 => Ok(NoteCommand::VolumeSlideDown(command_value)),
208 5 => Ok(NoteCommand::PitchSlideDown(command_value)),
209 6 => Ok(NoteCommand::PitchSlideUp(command_value)),
210 7 => Ok(NoteCommand::SlideToNote(command_value)),
211 8 => Ok(NoteCommand::Vibrato(command_value)),
212 9 => Ok(NoteCommand::Tremor(command_value)),
213 10 => Ok(NoteCommand::Arpeggio(command_value)),
214 11 => Ok(NoteCommand::VibratoAndVolSlideDown(command_value)),
215 12 => Ok(NoteCommand::SlideToNoteAndVolSlideDown(command_value)),
216 13 => Ok(NoteCommand::SetChannelVol(command_value)),
217 14 => Ok(NoteCommand::ChannelVolumeSlideDown(command_value)),
218 15 => Ok(NoteCommand::SetSampleOffset(command_value)),
219 16 => Ok(NoteCommand::PanningSlide(command_value)),
220 17 => Ok(NoteCommand::RetriggerNote(command_value)),
221 18 => Ok(NoteCommand::Tremolo(command_value)),
222 19 => Ok(NoteCommand::AlmostEverything(command_value)),
223 20 => Ok(NoteCommand::TempoChange(command_value)),
224 21 => Ok(NoteCommand::FineVibrato(command_value)),
225 22 => Ok(NoteCommand::SetGlobalVolume(command_value)),
226 23 => Ok(NoteCommand::GlobalVolumeSlide(command_value)),
227 24 => Ok(NoteCommand::SetPanning(command_value)),
228 25 => Ok(NoteCommand::Panbrello(command_value)),
229 26 => Ok(NoteCommand::MIDIMacros(command_value)),
230 _ => Err(UnknownNoteCommand),
231 }
232}
233
234pub struct PatternParser {
235 length: u64,
236 num_rows: u16,
237 // changed while parsing
238 pattern: Pattern,
239 curren_row: u16,
240 last_mask: [u8; 64],
241 last_event: [NoteEvent; 64],
242}
243
244impl PatternParser {
245 const PATTERN_HEADER_SIZE: usize = 8;
246
247 pub fn new(header: &[u8; Self::PATTERN_HEADER_SIZE]) -> Result<Self, err::LoadErr2> {
248 let (length, num_rows) = (
249 u64::from(u16::from_le_bytes([header[0], header[1]]))
250 + Self::PATTERN_HEADER_SIZE as u64,
251 u16::from_le_bytes([header[2], header[3]]),
252 );
253
254 // a guarantee given by the impulse tracker "specs"
255 if length >= 64_000 {
256 return Err(err::LoadErr2::Invalid);
257 }
258
259 if !(32..=200).contains(&num_rows) {
260 return Err(err::LoadErr2::Invalid);
261 }
262
263 Ok(Self {
264 length,
265 num_rows,
266 pattern: Pattern::new(num_rows),
267 curren_row: 0,
268 last_mask: [0; _],
269 last_event: [NoteEvent::default(); _],
270 })
271 }
272
273 /// needs at most 7 bytes at once
274 pub fn parse(&mut self, mut buf: &[u8]) -> SansIO<Pattern> {
275 let mut read = 0;
276 while self.curren_row < self.num_rows {
277 let Some(&channel_variable) = buf.split_off_first() else {
278 return SansIO::Pending(read);
279 };
280 read += 1;
281 if channel_variable == 0 {
282 self.curren_row += 1;
283 continue;
284 }
285 let channel = (channel_variable - 1) & 63; // 64 channels, 0 based
286 let channel_id = usize::from(channel);
287
288 let maskvar = if (channel_variable & 0b10000000) != 0 {
289 let Some(&val) = buf.split_off_first() else {
290 return SansIO::Pending(read);
291 };
292 read += 1;
293 self.last_mask[channel_id] = val;
294 val
295 } else {
296 self.last_mask[channel_id]
297 };
298
299 let mut event = NoteEvent::default();
300 }
301 let finished = mem::replace(&mut self.pattern, Pattern::new(self.num_rows));
302 SansIO::Finished(finished, read)
303 }
304}