aboutsummaryrefslogtreecommitdiff
path: root/src/convert.rs
diff options
context:
space:
mode:
authorGravatar Chris Xiong <chirs241097@gmail.com> 2022-11-07 17:15:15 -0500
committerGravatar Chris Xiong <chirs241097@gmail.com> 2022-11-07 17:15:15 -0500
commit7f49730fd8a6dc52fc45937e868749e5612c5234 (patch)
tree9729ec896e42279f9cc49e862c9405ac60cc6b42 /src/convert.rs
downloadit2midi-7f49730fd8a6dc52fc45937e868749e5612c5234.tar.xz
(internal) initial commit
Diffstat (limited to 'src/convert.rs')
-rw-r--r--src/convert.rs193
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();
+ }
+}