old school music tracker audio backend
at main 160 lines 4.6 kB view raw
1use crate::file::err; 2use crate::file::err::LoadDefect; 3use crate::project::event_command::NoteCommand; 4use crate::project::note_event::{Note, NoteEvent, VolumeEffect}; 5use crate::project::pattern::{InPatternPosition, Pattern}; 6 7/// reader should be buffered in some way and not do a syscall on every read call. 8/// 9/// This function does a lot of read calls 10pub fn parse_pattern<R: std::io::Read + std::io::Seek, H: FnMut(LoadDefect)>( 11 reader: &mut R, 12 defect_handler: &mut H, 13) -> Result<Pattern, err::LoadErr> { 14 const PATTERN_HEADER_SIZE: usize = 8; 15 16 let read_start = reader.stream_position()?; 17 18 let (length, num_rows) = { 19 let mut header = [0; PATTERN_HEADER_SIZE]; 20 reader.read_exact(&mut header)?; 21 ( 22 u64::from(u16::from_le_bytes([header[0], header[1]])) + PATTERN_HEADER_SIZE as u64, 23 u16::from_le_bytes([header[2], header[3]]), 24 ) 25 }; 26 27 // a guarantee given by the impulse tracker "specs" 28 if length >= 64_000 { 29 return Err(err::LoadErr::Invalid); 30 } 31 32 if !(32..=200).contains(&num_rows) { 33 return Err(err::LoadErr::Invalid); 34 } 35 36 let mut pattern = Pattern::new(num_rows); 37 38 let mut row_num: u16 = 0; 39 40 let mut last_mask = [0; 64]; 41 let mut last_event = [NoteEvent::default(); 64]; 42 43 let mut scratch = [0; 1]; 44 45 while row_num < num_rows && reader.stream_position()? - read_start < length { 46 let channel_variable = scratch[0]; 47 48 if channel_variable == 0 { 49 row_num += 1; 50 continue; 51 } 52 53 let channel = (channel_variable - 1) & 63; // 64 channels, 0 based 54 let channel_id = usize::from(channel); 55 56 let maskvar = if (channel_variable & 0b10000000) != 0 { 57 reader.read_exact(&mut scratch)?; 58 let val = scratch[0]; 59 last_mask[channel_id] = val; 60 val 61 } else { 62 last_mask[channel_id] 63 }; 64 65 let mut event = NoteEvent::default(); 66 67 // Note 68 if (maskvar & 0b00000001) != 0 { 69 reader.read_exact(&mut scratch)?; 70 let note = match Note::new(scratch[0]) { 71 Ok(n) => n, 72 Err(_) => { 73 defect_handler(LoadDefect::OutOfBoundsValue); 74 Note::default() 75 } 76 }; 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_raw = scratch[0]; 95 let vol_pan = match vol_pan_raw.try_into() { 96 Ok(v) => v, 97 Err(_) => { 98 defect_handler(LoadDefect::OutOfBoundsValue); 99 VolumeEffect::default() 100 } 101 }; 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 122 last_event[channel_id].command = cmd; 123 event.command = cmd; 124 } 125 126 // Same note 127 if (maskvar & 0b00010000) != 0 { 128 event.note = last_event[channel_id].note; 129 } 130 131 // Same Instr / Sample 132 if (maskvar & 0b00100000) != 0 { 133 event.sample_instr = last_event[channel_id].sample_instr; 134 } 135 136 // Same volume 137 if (maskvar & 0b01000000) != 0 { 138 event.vol = last_event[channel_id].vol; 139 } 140 141 // Same Command 142 if (maskvar & 0b10000000) != 0 { 143 event.command = last_event[channel_id].command; 144 } 145 146 pattern.set_event( 147 InPatternPosition { 148 row: row_num, 149 channel, 150 }, 151 event, 152 ); 153 } 154 155 if pattern.row_count() == row_num { 156 Ok(pattern) 157 } else { 158 Err(err::LoadErr::BufferTooShort) 159 } 160}