use std::collections::{BTreeMap, BTreeSet};
use std::any::Any;
use std::cell::RefCell;
use crate::itfile;
use crate::utils::Rational;
use crate::midifile;
use crate::portmod::Effect;
struct PlayerState
{
skip_row: u8,
skip_ord: u8,
current_ord: u8,
current_row: u8,
current_tick: u8,
speed: u8,
tempo: u8,
rpb: u8,
loop_start: u8,
loop_ctr: u8,
row_extension: u8,
in_rep: bool
}
struct Player<'a>
{
h: Box<RefCell<dyn CellHandler>>,
it: &'a itfile::ITFile
}
pub struct Converter<'a>
{
instch: Option<BTreeMap<(u8, u8), usize>>,
it: &'a itfile::ITFile
}
trait CellHandler
{
fn process(&mut self, ch: u8, cell: itfile::Cell, ps: PlayerState) -> PlayerState;
fn result_data(&mut self) -> Box<dyn Any>;
}
trait EffectHandler
{
fn process(&mut self, ch: u8, cell: itfile::Cell, chmem: &mut ChannelMemory, ps: PlayerState, mt: Rational) -> Vec<midifile::TimedMidiEvent>;
fn handled_effects(&self) -> Vec<u8>;
}
struct PrePassCellHandler
{
instchmap: Option<BTreeSet<(u8, u8)>>,
chinst: [u8; 64]
}
#[derive(Default)]
struct ChannelMemory
{
note: u8,
vol: u8,
efxmem: [u8; 32],
pitch: Rational
}
struct ConvertCellHandler<'a>
{
miditick: Rational,
chmem: [ChannelMemory; 64],
fx_handlers: Vec<Box<RefCell<dyn EffectHandler + 'a>>>
}
impl<'a> Player<'a>
{
fn new(handler: Box<RefCell<dyn CellHandler>>, it: &'a itfile::ITFile) -> Player<'a>
{
Player
{
h: handler,
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},
Effect::RowExtention(t) => PlayerState{row_extension: ps.row_extension + 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 = st;
for r in skip_row..self.it.patterns[pat].nrows
{
ret.current_row = r;
ret.current_tick = 0;
while ret.current_tick < ret.speed + ret.row_extension
{
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);
ret.row_extension = 0;
if (!ret.skip_row) != 0 || (!ret.skip_ord) != 0 { return ret; }
}
ret
}
/// Used for effects Bxx, Cxx and SBx
///
/// passing !0 to row or ord if it's unused
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 {
if (!row != 0) && (!ps.skip_ord == 0) { ps.current_ord + 1 } else { ps.skip_ord }
},
..ps
}
}
fn process_orders(&self)
{
let mut ps = PlayerState {
skip_row: !0,
skip_ord: !0,
current_ord: 0,
current_row: 0,
current_tick: 0,
speed: self.it.header.speed,
tempo: self.it.header.tempo,
rpb: self.it.time_signature().unwrap_or((4, 16)).0 as u8,
loop_start: 0,
loop_ctr: !0,
row_extension: 0,
in_rep: false
};
let mut oid = 0;
loop
{
if oid >= self.it.orders.len() { break; }
if self.it.orders[oid] == 0xff { break; }
if self.it.orders[oid] == 0xfe { continue; }
ps.current_ord = oid as u8;
ps = self.process_pattern(self.it.orders[oid].into(), ps);
if !ps.skip_ord != 0
{
if ps.skip_ord as usize <= oid && !ps.loop_ctr == 0
{ println!("loop?"); }
else { oid = ps.skip_ord as usize; }
ps.skip_ord = !0;
}
else { oid += 1; }
}
}
}
impl CellHandler for PrePassCellHandler
{
fn process(&mut self, ch: u8, cell: itfile::Cell, ps: PlayerState) -> PlayerState
{
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; }
else
{ inst = self.chinst[ch as usize]; }
if mask & 0x11 != 0
{ self.instchmap.as_mut().unwrap().insert((ch, inst)); }
ps
}
fn result_data(&mut self) -> Box<dyn Any>
{ Box::new(self.instchmap.take().unwrap()) }
}
impl<'a> ConvertCellHandler<'a>
{
fn register_fx_handler(&mut self, hndlr: Box<RefCell<dyn EffectHandler + 'a>>)
{
self.fx_handlers.push(hndlr);
}
}
impl<'a> CellHandler for ConvertCellHandler<'a>
{
fn process(&mut self, ch: u8, cell: itfile::Cell, ps: PlayerState) -> PlayerState
{
let itfile::Cell{mask, note, inst, vol, efx, fxp} = cell;
if !ch == 0
{
self.miditick += <Rational as From<u32>>::from(960) / (ps.speed as u16 * ps.rpb as u16).into();
}
ps
}
fn result_data(&mut self) -> Box<dyn Any>
{ Box::new(()) }
}
impl<'a> Converter<'a>
{
pub fn new(it: &itfile::ITFile) -> Converter
{
Converter{instch: None, it}
}
fn pre_pass(&'a mut self)
{
let h = PrePassCellHandler{instchmap: Some(BTreeSet::new()), chinst: [0; 64]};
let p = Player::new(Box::new(RefCell::new(h)), self.it);
p.process_orders();
let Player{h, ..} = p;
if let Ok(m) = h.borrow_mut().result_data().downcast::<BTreeSet<(u8, u8)>>()
{
let mut instch = BTreeMap::new();
m.iter().enumerate().for_each(
|(i, p)| { instch.insert(*p, i + 1); } );
self.instch = Some(instch);
}
println!("{:?}", self.instch);
}
pub fn convert(&'a mut self)
{
self.pre_pass();
}
}