aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/convert.rs107
-rw-r--r--src/itfile.rs52
-rw-r--r--src/main.rs1
-rw-r--r--src/portmod.rs34
4 files changed, 142 insertions, 52 deletions
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<RefCell<dyn EffectHandler + 'a>>; 32]
+ fx_handlers: Vec<Box<RefCell<dyn EffectHandler + 'a>>>
}
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<RefCell<dyn EffectHandler + 'a>>)
+ fn register_fx_handler(&mut self, hndlr: Box<RefCell<dyn EffectHandler + 'a>>)
{
- 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<Cell>
}
@@ -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
+ }
+ }
+}