Something went wrong. Try again.
This repository has no description
Something went wrong. Try again.
9.3 kB · 250 lines
Rust
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251use crate::file::{SansIO, err};use crate::project::event_command::NoteCommand;use crate::project::note_event::{Note, NoteEvent, VolumeEffect};use crate::project::pattern::{InPatternPosition, Pattern};use core::error::Error;use core::fmt::{self, Display};
use core::mem;
#[derive(Debug, Clone, Copy)]pub struct InvalidVolumeEffect;
impl Display for InvalidVolumeEffect { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "Invalid Volume Effect") }}
impl Error for InvalidVolumeEffect {}
/// IT Tracker Format Conversion/// no way to get None, as then it just doesn't get setfn volumeeffect_try_from(value: u8) -> Result<VolumeEffect, InvalidVolumeEffect> { match value { 0..=64 => Ok(VolumeEffect::Volume(value)), 65..=74 => Ok(VolumeEffect::FineVolSlideUp(value - 65)), 75..=84 => Ok(VolumeEffect::FineVolSlideDown(value - 75)), 85..=94 => Ok(VolumeEffect::VolSlideUp(value - 85)), 95..=104 => Ok(VolumeEffect::VolSlideDown(value - 95)), 105..=114 => Ok(VolumeEffect::PitchSlideDown(value - 105)), 115..=124 => Ok(VolumeEffect::PitchSlideUp(value - 115)), 128..=192 => Ok(VolumeEffect::Panning(value - 128)), 193..=202 => Ok(VolumeEffect::SlideToNoteWithSpeed(value - 193)), 203..=212 => Ok(VolumeEffect::VibratoWithSpeed(value - 203)), _ => Err(InvalidVolumeEffect), }}
#[derive(Debug, Clone, Copy)]pub struct UnknownNoteCommand;
impl Display for UnknownNoteCommand { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "Unknown Note Command") }}
impl Error for UnknownNoteCommand {}
fn note_command_try_from( (command_type, command_value): (u8, u8),) -> Result<NoteCommand, UnknownNoteCommand> { match command_type { 0 => Ok(NoteCommand::None), 1 => Ok(NoteCommand::SetTempo(command_value)), 2 => Ok(NoteCommand::JumpToOrder(command_value)), 3 => Ok(NoteCommand::BreakToRow(command_value)), 4 => Ok(NoteCommand::VolumeSlideDown(command_value)), 5 => Ok(NoteCommand::PitchSlideDown(command_value)), 6 => Ok(NoteCommand::PitchSlideUp(command_value)), 7 => Ok(NoteCommand::SlideToNote(command_value)), 8 => Ok(NoteCommand::Vibrato(command_value)), 9 => Ok(NoteCommand::Tremor(command_value)), 10 => Ok(NoteCommand::Arpeggio(command_value)), 11 => Ok(NoteCommand::VibratoAndVolSlideDown(command_value)), 12 => Ok(NoteCommand::SlideToNoteAndVolSlideDown(command_value)), 13 => Ok(NoteCommand::SetChannelVol(command_value)), 14 => Ok(NoteCommand::ChannelVolumeSlideDown(command_value)), 15 => Ok(NoteCommand::SetSampleOffset(command_value)), 16 => Ok(NoteCommand::PanningSlide(command_value)), 17 => Ok(NoteCommand::RetriggerNote(command_value)), 18 => Ok(NoteCommand::Tremolo(command_value)), 19 => Ok(NoteCommand::AlmostEverything(command_value)), 20 => Ok(NoteCommand::TempoChange(command_value)), 21 => Ok(NoteCommand::FineVibrato(command_value)), 22 => Ok(NoteCommand::SetGlobalVolume(command_value)), 23 => Ok(NoteCommand::GlobalVolumeSlide(command_value)), 24 => Ok(NoteCommand::SetPanning(command_value)), 25 => Ok(NoteCommand::Panbrello(command_value)), 26 => Ok(NoteCommand::MIDIMacros(command_value)), _ => Err(UnknownNoteCommand), }}
pub struct PatternParser { // TODO: maybe validate that the lenght of the pattern matches the value from the pattern header // length: u64, num_rows: u16, // changed while parsing pattern: Pattern, current_row: u16, last_mask: [u8; 64], last_event: [NoteEvent; 64],}
impl PatternParser { pub const HEADER_SIZE: usize = 8;
pub fn new(header: &[u8; Self::HEADER_SIZE]) -> Result<Self, err::LoadErr> { let (length, num_rows) = ( u64::from(u16::from_le_bytes([header[0], header[1]])) + Self::HEADER_SIZE as u64, u16::from_le_bytes([header[2], header[3]]), );
// a guarantee given by the impulse tracker "specs" if length >= 64_000 { return Err(err::LoadErr::Invalid); }
if !(32..=200).contains(&num_rows) { return Err(err::LoadErr::Invalid); }
Ok(Self { num_rows, pattern: Pattern::new(num_rows), current_row: 0, last_mask: [0; _], last_event: [NoteEvent::default(); _], }) }
/// needs at most 7 bytes at once pub fn parse(&mut self, mut buf: &[u8]) -> SansIO<Pattern> { let mut read = 0; while self.current_row < self.num_rows { let Some(&channel_variable) = buf.split_off_first() else { return SansIO::Pending(read); }; if channel_variable == 0 { self.current_row += 1; // row done, commit the read byte // otherwise need to finish event, before i can commit the read read += 1; continue; } let mut uncommited_read = 1;
let channel = (channel_variable - 1) & 63; // 64 channels, 0 based let channel_id = usize::from(channel);
let maskvar = if (channel_variable & 0b10000000) != 0 { let Some(&val) = buf.split_off_first() else { return SansIO::Pending(read); }; uncommited_read += 1; self.last_mask[channel_id] = val; val } else { self.last_mask[channel_id] };
// compute how many bytes i need based on the maskvar let mut needed = 0; // Note if (maskvar & 0b0000_0001) != 0 { if (maskvar & 0b0001_0000) != 0 { // maybe report an error, as this doesn't make sense // behaviour is the same as if this bit wasn't set, so maybe it doesn't matter // same for the other three } needed += 1; } // Instrument / Sample if (maskvar & 0b0000_0010) != 0 { needed += 1; } // Volume if (maskvar & 0b0000_0100) != 0 { needed += 1; } // Effect if (maskvar & 0b0000_1000) != 0 { needed += 2; }
if buf.len() < needed { return SansIO::Pending(read); } // now i am sure that i have enough for the whole event read += needed + uncommited_read;
// i can just unwrap from here on, the buffer is big enough
let mut event = NoteEvent::default();
// Note if (maskvar & 0b0000_0001) != 0 { let note = *buf.split_off_first().unwrap(); // TODO: handle invalid values let note = Note::new(note).unwrap_or_default(); event.note = note; self.last_event[channel_id].note = note; } // Instrument / Sample if (maskvar & 0b0000_0010) != 0 { let sample_instr = *buf.split_off_first().unwrap(); event.sample_instr = sample_instr; self.last_event[channel_id].sample_instr = sample_instr; } // Volume if (maskvar & 0b0000_0100) != 0 { // TODO: handle error let vol_pan = volumeeffect_try_from(*buf.split_off_first().unwrap()).unwrap_or_default(); event.vol = vol_pan; self.last_event[channel_id].vol = vol_pan; } // Effect if (maskvar & 0b0000_1000) != 0 { let command = *buf.split_off_first().unwrap(); let cmd_val = *buf.split_off_first().unwrap(); // TODO: handle error let cmd = note_command_try_from((command, cmd_val)).unwrap_or_default(); event.command = cmd; self.last_event[channel_id].command = cmd; }
// Same note if (maskvar & 0b00010000) != 0 { event.note = self.last_event[channel_id].note; }
// Same Instr / Sample if (maskvar & 0b00100000) != 0 { event.sample_instr = self.last_event[channel_id].sample_instr; }
// Same volume if (maskvar & 0b01000000) != 0 { event.vol = self.last_event[channel_id].vol; }
// Same Command if (maskvar & 0b10000000) != 0 { event.command = self.last_event[channel_id].command; }
self.pattern.set_event( InPatternPosition { row: self.current_row, channel, }, event, ); } let finished = mem::replace(&mut self.pattern, Pattern::new(self.num_rows)); SansIO::Finished(finished, read) }}