diff options
author | Chris Xiong <chirs241097@gmail.com> | 2022-11-07 17:15:15 -0500 |
---|---|---|
committer | Chris Xiong <chirs241097@gmail.com> | 2022-11-07 17:15:15 -0500 |
commit | 7f49730fd8a6dc52fc45937e868749e5612c5234 (patch) | |
tree | 9729ec896e42279f9cc49e862c9405ac60cc6b42 /src/convert.rs | |
download | it2midi-7f49730fd8a6dc52fc45937e868749e5612c5234.tar.xz |
(internal) initial commit
Diffstat (limited to 'src/convert.rs')
-rw-r--r-- | src/convert.rs | 193 |
1 files changed, 193 insertions, 0 deletions
diff --git a/src/convert.rs b/src/convert.rs new file mode 100644 index 0000000..9ed703f --- /dev/null +++ b/src/convert.rs @@ -0,0 +1,193 @@ +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; + +struct PlayerState +{ + skip_row: u16, + skip_ord: u8, + current_ord: u8, + current_row: u16, + in_loop: bool +} + +struct Player<'a> +{ + h: Rc<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, player: &mut Player); + fn result_data(&mut self) -> Box<dyn Any>; +} + +trait EffectHandler +{ + fn process(&mut self, ch: u8, cell: itfile::Cell, chmem: &mut ChannelMemory) -> 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: utils::Rational +} + +struct ConvertCellHandler<'a> +{ + chmem: [ChannelMemory; 64], + fx_handlers: [Rc<RefCell<dyn EffectHandler + 'a>>; 32] +} + +impl<'a> Player<'a> +{ + fn new(handler: Rc<RefCell<dyn CellHandler>>, it: &'a itfile::ITFile) -> Player<'a> + { + Player + { + h: handler, + it + } + } + fn process_pattern(&mut self, pat: usize, st: PlayerState) -> PlayerState + { + let skip_row = if !st.skip_row == 0 { 0 } else { st.skip_row }; + let ret = PlayerState{ + skip_row: !0, + skip_ord: !0, + current_ord: 0, + current_row: 0, + in_loop: false + }; + self.skip_row = !0; + for r in skip_row..self.it.patterns[pat].nrows + { + self.current_row = r; + for c in 0..64 + { + let cell = *self.it.patterns[pat].cell_at(r, c); + Rc::clone(&self.h).borrow_mut().process(c as u8, cell, self); + } + Rc::clone(&self.h).borrow_mut().process(!0, itfile::Cell::default(), self); + if (!self.skip_row) != 0 || (!self.skip_ord) != 0 { return; } + } + ret + } + /// Used for effects Bxx, Cxx and SBx + /// + /// 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(&mut self, row: u16, ord: u8, in_loop: bool) + { + self.skip_row = if !row != 0 { row } else { self.skip_row }; + self.skip_ord = if !ord != 0 { ord } else { self.skip_ord }; + self.in_loop = in_loop; + } + fn process_orders(&mut self) + { + self.skip_to(!0, !0, 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; } + self.process_pattern(self.it.orders[oid].into()); + if !self.skip_ord != 0 + { + if self.skip_ord as usize <= oid && !self.in_loop + { println!("loop?"); } + else { oid = self.skip_ord as usize; } + self.skip_ord = !0; + } + else { oid += 1; } + } + } +} + +impl CellHandler for PrePassCellHandler +{ + fn process(&mut self, ch: u8, cell: itfile::Cell, _p: &mut Player) + { + if ch == 0xff { return; } + 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)); } + } + 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: Rc<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)); + } +} + +impl<'a> CellHandler for ConvertCellHandler<'a> +{ + fn process(&mut self, ch: u8, cell: itfile::Cell, player: &mut Player) + { + let itfile::Cell{mask, note, inst, vol, efx, fxp} = cell; + } + 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 mut p = Player::new(Rc::new(RefCell::new(h)), self.it); + p.process_orders(); + let Player{mut 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(); + } +} |