From af733bc521d1f8578e4ec4d704c3e776be185603 Mon Sep 17 00:00:00 2001 From: Chris Xiong Date: Thu, 10 Nov 2022 01:54:26 -0500 Subject: Handle timing effects. Shitton of changes to make it compile. Also redesigned some shit just in case I want to reuse the code for a tracker module playback engine. --- src/convert.rs | 107 +++++++++++++++++++++++++++++++++++++++++++-------------- src/itfile.rs | 52 ++++++++++++++-------------- src/main.rs | 1 + src/portmod.rs | 34 ++++++++++++++++++ 4 files changed, 142 insertions(+), 52 deletions(-) create mode 100644 src/portmod.rs diff --git a/src/convert.rs b/src/convert.rs index 5ae394c..05866f3 100644 --- a/src/convert.rs +++ b/src/convert.rs @@ -1,18 +1,23 @@ use std::collections::{BTreeMap, BTreeSet}; use std::any::Any; use std::cell::RefCell; -use std::rc::Rc; use crate::itfile; use crate::utils; use crate::midifile; +use crate::portmod::Effect; struct PlayerState { - skip_row: u16, + skip_row: u8, skip_ord: u8, current_ord: u8, - current_row: u16, - in_loop: bool + current_row: u8, + current_tick: u8, + speed: u8, + tempo: u8, + loop_start: u8, + loop_ctr: u8, + in_rep: bool } struct Player<'a> @@ -57,7 +62,7 @@ struct ChannelMemory struct ConvertCellHandler<'a> { chmem: [ChannelMemory; 64], - fx_handlers: [Rc>; 32] + fx_handlers: Vec>> } impl<'a> Player<'a> @@ -70,23 +75,70 @@ impl<'a> Player<'a> it } } + fn process_timingfx(&self, cell: itfile::Cell, ps: PlayerState) -> PlayerState + { + let e = Effect::from_it_efx((cell.efx, cell.fxp)); + if ps.current_tick == 0 + { + match e + { + Effect::SetSpeed(s) => PlayerState{speed: s, ..ps}, + Effect::PattLoopStart => PlayerState{loop_start: ps.current_row, ..ps}, + Effect::SetTempo(t) => PlayerState{tempo: t, ..ps}, + _ => ps + } + } + else if ps.current_tick == ps.speed - 1 + { + match e + { + Effect::PosJump(p) => self.skip_to(!0, p, ps), + Effect::PattBreak(r) => self.skip_to(r, !0, ps), + Effect::PattLoop(c) => + match ps.loop_ctr + { + u8::MAX => PlayerState{loop_ctr: 1, .. self.skip_to(ps.loop_start, ps.current_ord, ps)}, + _ if ps.loop_ctr >= c => PlayerState{loop_ctr: !0, ..ps}, + _ => PlayerState{loop_ctr: ps.loop_start + 1, .. self.skip_to(ps.loop_start, ps.current_ord, ps)} + }, + Effect::PattDelay(c) => + match ps.loop_ctr + { + u8::MAX => PlayerState{loop_ctr: 1, in_rep: true, .. self.skip_to(ps.current_row, ps.current_ord, ps)}, + _ if ps.loop_ctr >= c => PlayerState{loop_ctr: !0, in_rep: false, ..ps}, + _ => PlayerState{loop_ctr: ps.loop_start + 1, .. self.skip_to(ps.current_row, ps.current_ord, ps)} + }, + Effect::TempoSlideDown(v) => PlayerState{tempo: ps.tempo - v, ..ps}, + Effect::TempoSlideUp(v) => PlayerState{tempo: ps.tempo + v, ..ps}, + _ => ps + } + } + else { + match e + { + Effect::TempoSlideDown(v) => PlayerState{tempo: ps.tempo - v, ..ps}, + Effect::TempoSlideUp(v) => PlayerState{tempo: ps.tempo + v, ..ps}, + _ => ps + } + } + } fn process_pattern(&self, pat: usize, st: PlayerState) -> PlayerState { let skip_row = if !st.skip_row == 0 { 0 } else { st.skip_row }; - let mut ret = PlayerState { - skip_row: !0, - skip_ord: !0, - current_ord: 0, - current_row: 0, - in_loop: false - }; + let mut ret = st; for r in skip_row..self.it.patterns[pat].nrows { ret.current_row = r; - for c in 0..64 + ret.current_tick = 0; + while ret.current_tick < ret.speed { - let cell = *self.it.patterns[pat].cell_at(r, c); - ret = (&self.h).borrow_mut().process(c as u8, cell, ret); + for c in 0..64 + { + let cell = *self.it.patterns[pat].cell_at(r, c); + ret = (&self.h).borrow_mut().process(c as u8, cell, ret); + ret = self.process_timingfx(cell, ret); + } + ret.current_tick += 1; } ret = (&self.h).borrow_mut().process(!0, itfile::Cell::default(), ret); if (!ret.skip_row) != 0 || (!ret.skip_ord) != 0 { return ret; } @@ -98,12 +150,13 @@ impl<'a> Player<'a> /// passing !0 to row or ord if it's unused /// /// in_loop == true inhibits loop detection and should only be used for SBx - fn skip_to(&self, row: u16, ord: u8, in_loop: bool, ps: PlayerState) -> PlayerState + fn skip_to(&self, row: u8, ord: u8, ps: PlayerState) -> PlayerState { PlayerState { skip_row: if !row != 0 { row } else { ps.skip_row }, - skip_ord: if !ord != 0 { ord } else { ps.skip_ord }, - in_loop, + skip_ord: if !ord != 0 { ord } else { + if (!row != 0) && (!ps.skip_ord == 0) { ps.current_ord + 1 } else { ps.skip_ord } + }, ..ps } } @@ -114,7 +167,12 @@ impl<'a> Player<'a> skip_ord: !0, current_ord: 0, current_row: 0, - in_loop: false + current_tick: 0, + speed: self.it.header.speed, + tempo: self.it.header.tempo, + loop_start: 0, + loop_ctr: !0, + in_rep: false }; let mut oid = 0; @@ -127,7 +185,7 @@ impl<'a> Player<'a> ps = self.process_pattern(self.it.orders[oid].into(), ps); if !ps.skip_ord != 0 { - if ps.skip_ord as usize <= oid && !ps.in_loop + if ps.skip_ord as usize <= oid && !ps.loop_ctr == 0 { println!("loop?"); } else { oid = ps.skip_ord as usize; } ps.skip_ord = !0; @@ -141,7 +199,7 @@ impl CellHandler for PrePassCellHandler { fn process(&mut self, ch: u8, cell: itfile::Cell, ps: PlayerState) -> PlayerState { - if ch == 0xff { return ps; } + if (ch == 0xff) || (ps.current_tick != 0) { return ps; } let itfile::Cell{mask, mut inst, ..} = cell; if mask & 0x22 != 0 { self.chinst[ch as usize] = inst; } @@ -157,12 +215,9 @@ impl CellHandler for PrePassCellHandler impl<'a> ConvertCellHandler<'a> { - fn register_fx_handler(&mut self, hndlr: Rc>) + fn register_fx_handler(&mut self, hndlr: Box>) { - let efxs = hndlr.borrow().handled_effects(); - efxs.iter() - .filter(|e| **e < 32) - .for_each(|e| self.fx_handlers[*e as usize] = Rc::clone(&hndlr)); + self.fx_handlers.push(hndlr); } } diff --git a/src/itfile.rs b/src/itfile.rs index 03c79ee..76c1997 100644 --- a/src/itfile.rs +++ b/src/itfile.rs @@ -18,28 +18,28 @@ macro_rules! zero_default { #[repr(C, packed)] pub struct Header { - magic: [u8; 4], - song_name: [u8; 26], - pat_highlight: u16, //PHiligt - nord: u16, //OrdNum - ninst: u16, //InsNum - nsamp: u16, //SmpNum - npatt: u16, //PatNum - tracker_version: u16, //Cwt/v - format_version: u16, //Cmwt - flags: u16, //Flags - special: u16, //Special - global_volume: u8, //GV - mix_volume: u8, //MV - speed: u8, //IS - tempo: u8, //IT - stereo_seperation: u8, //Sep - wheel_depth: u8, //PWD - msg_length: u16, //MsgLgth - msg_offset: u32, //Message Offset - reserved: u32, - channel_pan: [u8; 64], - channel_vol: [u8; 64] + pub magic: [u8; 4], + pub song_name: [u8; 26], + pub pat_highlight: u16, //PHiligt + pub nord: u16, //OrdNum + pub ninst: u16, //InsNum + pub nsamp: u16, //SmpNum + pub npatt: u16, //PatNum + pub tracker_version: u16, //Cwt/v + pub format_version: u16, //Cmwt + pub flags: u16, //Flags + pub special: u16, //Special + pub global_volume: u8, //GV + pub mix_volume: u8, //MV + pub speed: u8, //IS + pub tempo: u8, //IT + pub stereo_seperation: u8, //Sep + pub wheel_depth: u8, //PWD + pub msg_length: u16, //MsgLgth + pub msg_offset: u32, //Message Offset + pub reserved: u32, + pub channel_pan: [u8; 64], + pub channel_vol: [u8; 64] } zero_default!{Header} @@ -182,7 +182,7 @@ pub struct Cell pub struct Patt { - pub nrows: u16, + pub nrows: u8, pub nch: u8, pub data: Vec } @@ -233,11 +233,11 @@ impl Patt { return Err(self::Error::CorruptPatternData); } - Ok(Patt{nrows, nch:64, data}) + Ok(Patt{nrows: nrows as u8, nch:64, data}) } - pub fn cell_at(&self, r: u16, c: u8) -> &Cell + pub fn cell_at(&self, r: u8, c: u8) -> &Cell { - &self.data[((r * self.nch as u16) + c as u16) as usize] + &self.data[((r as u16 * self.nch as u16) + c as u16) as usize] } pub fn dump(&self) { diff --git a/src/main.rs b/src/main.rs index 9c90d96..ff3c025 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,6 @@ mod itfile; mod midifile; +mod portmod; mod convert; mod utils; diff --git a/src/portmod.rs b/src/portmod.rs new file mode 100644 index 0000000..52c27ee --- /dev/null +++ b/src/portmod.rs @@ -0,0 +1,34 @@ +pub enum Effect +{ + SetSpeed(u8), + PosJump(u8), + PattBreak(u8), + PattLoopStart, + PattLoop(u8), + PattDelay(u8), + SetTempo(u8), + TempoSlideDown(u8), + TempoSlideUp(u8), + NoEffect +} + +impl Effect +{ + pub fn from_it_efx(f: (u8, u8)) -> Effect + { + let (efx, fxp) = f; + match (efx as char, fxp) + { + ('A', _) => Effect::SetSpeed(fxp), + ('B', _) => Effect::PosJump(fxp), + ('C', _) => Effect::PattBreak(fxp), + ('S', 0xb0) => Effect::PattLoopStart, + ('S', _) if fxp & 0xf0 == 0xb0 => Effect::PattLoop(fxp & 0x0f), + ('S', _) if fxp & 0xf0 == 0xe0 => Effect::PattDelay(fxp & 0x0f), + ('T', _) if fxp & 0xf0 == 0x00 => Effect::TempoSlideDown(fxp & 0x0f), + ('T', _) if fxp & 0xf0 == 0x10 => Effect::TempoSlideUp(fxp & 0x0f), + ('T', _) => Effect::SetTempo(fxp), + _ => Effect::NoEffect + } + } +} -- cgit v1.2.3