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