aboutsummaryrefslogtreecommitdiff
path: root/src/convert.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/convert.rs')
-rw-r--r--src/convert.rs219
1 files changed, 130 insertions, 89 deletions
diff --git a/src/convert.rs b/src/convert.rs
index 884708d..a683772 100644
--- a/src/convert.rs
+++ b/src/convert.rs
@@ -1,11 +1,13 @@
use std::collections::{BTreeMap, BTreeSet};
-use std::any::Any;
use std::cell::RefCell;
-use crate::itfile;
+use std::rc::Rc;
+use crate::itfile::{ITFile, Cell};
use crate::utils::Rational;
-use crate::midifile;
+use crate::midifile::{MidiEvent, TimedMidiEvent, MidiTrack, MidiFile};
use crate::portmod::Effect;
+#[derive(Debug)]
+#[derive(Clone)]
struct PlayerState
{
skip_row: u8,
@@ -22,55 +24,37 @@ struct PlayerState
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
-}
+type CellHandlerFn<'a> = dyn 'a + FnMut(u8, Cell, PlayerState) -> PlayerState;
+type EfxHandlerFn<'a> = dyn 'a + FnMut(u8, Cell, Rc<RefCell<ChannelMemory>>, PlayerState, Rational) -> Vec<(TimedMidiEvent, bool)>;
-trait CellHandler
+struct Player<'a, 'b>
{
- 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]
+ h: Box<RefCell<CellHandlerFn<'b>>>,
+ it: &'a ITFile
}
#[derive(Default)]
struct ChannelMemory
{
note: u8,
+ postnote: u8,
+ inst: u8,
vol: u8,
efxmem: [u8; 32],
pitch: Rational
}
-struct ConvertCellHandler<'a>
+pub struct Converter<'a>
{
miditick: Rational,
- chmem: [ChannelMemory; 64],
- fx_handlers: Vec<Box<RefCell<dyn EffectHandler + 'a>>>
+ chmem: Vec<Rc<RefCell<ChannelMemory>>>,
+ trks: Vec<MidiTrack>,
+ fx_handlers: Vec<Box<RefCell<EfxHandlerFn<'a>>>>
}
-impl<'a> Player<'a>
+impl<'a, 'b> Player<'a, 'b>
{
- fn new(handler: Box<RefCell<dyn CellHandler>>, it: &'a itfile::ITFile) -> Player<'a>
+ fn new(handler: Box<RefCell<CellHandlerFn<'b>>>, it: &'a ITFile) -> Player<'a, 'b>
{
Player
{
@@ -78,7 +62,7 @@ impl<'a> Player<'a>
it
}
}
- fn process_timingfx(&self, cell: itfile::Cell, ps: PlayerState) -> PlayerState
+ fn process_timingfx(&self, cell: Cell, ps: PlayerState) -> PlayerState
{
let e = Effect::from_it_efx((cell.efx, cell.fxp));
if ps.current_tick == 0
@@ -129,7 +113,7 @@ impl<'a> Player<'a>
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;
+ let mut ret = PlayerState{skip_row: !0, ..st};
for r in skip_row..self.it.patterns[pat].nrows
{
ret.current_row = r;
@@ -139,12 +123,12 @@ impl<'a> Player<'a>
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.h.borrow_mut())(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 = (self.h.borrow_mut())(!0, Cell::default(), ret);
ret.row_extension = 0;
if (!ret.skip_row) != 0 || (!ret.skip_ord) != 0 { return ret; }
}
@@ -155,6 +139,7 @@ impl<'a> Player<'a>
/// passing !0 to row or ord if it's unused
fn skip_to(&self, row: u8, ord: u8, ps: PlayerState) -> PlayerState
{
+ println!("skip to row {} of ord #{}", row, ord);
PlayerState {
skip_row: if !row != 0 { row } else { ps.skip_row },
skip_ord: if !ord != 0 { ord } else {
@@ -200,70 +185,126 @@ impl<'a> Player<'a>
}
}
-impl CellHandler for PrePassCellHandler
+impl<'a> Converter<'a>
{
- fn process(&mut self, ch: u8, cell: itfile::Cell, ps: PlayerState) -> PlayerState
+ pub fn new() -> Converter<'a>
{
- 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
+ let mut ret = Converter
+ {
+ miditick: 0u32.into(),
+ chmem: Vec::new(),
+ trks: Vec::new(),
+ fx_handlers: Vec::new()
+ };
+ for _ in 0..256
+ { ret.chmem.push(Rc::new(RefCell::new(Default::default()))); }
+ ret.setup_fx_handlers();
+ ret
}
- 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>>)
+ fn setup_fx_handlers<'x, 'y>(&mut self)
{
- self.fx_handlers.push(hndlr);
+ let nonfx = |ch: u8, cell, chmem: Rc<RefCell<ChannelMemory>>, ps: PlayerState, t: Rational| {
+ if !ch == 0 { return Vec::new(); }
+ let mut ret = Vec::new();
+ let Cell { mask, note, .. } = cell;
+ if mask & 0x11 != 0 && ps.current_tick == 0
+ {
+ match note
+ {
+ 0x78..=0xff =>
+ if chmem.borrow().postnote != 0xff
+ {
+ ret.push((TimedMidiEvent{t: t.as_int_trunc() as u32, e: MidiEvent::NoteOff{ch: 0, key: chmem.borrow().postnote, vel: 0x40}}, false));
+ chmem.borrow_mut().postnote = 0xff;
+ },
+ 0 => (),
+ _ =>
+ {
+ if chmem.borrow().postnote != 0xff
+ {
+ ret.push((TimedMidiEvent{t: t.as_int_trunc() as u32, e: MidiEvent::NoteOff{ch: 0, key: chmem.borrow().postnote, vel: 0x40}}, false));
+ }
+ ret.push((TimedMidiEvent{t: t.as_int_trunc() as u32, e: MidiEvent::NoteOn{ch: 0, key: note, vel: 0x40}}, false));
+ chmem.borrow_mut().postnote = note;
+ }
+ }
+ }
+ ret
+ };
+ self.fx_handlers.push(Box::new(RefCell::new(nonfx)));
}
-}
-
-impl<'a> CellHandler for ConvertCellHandler<'a>
-{
- fn process(&mut self, ch: u8, cell: itfile::Cell, ps: PlayerState) -> PlayerState
+ fn pre_pass(it: &ITFile) -> BTreeMap<(u8, u8), usize>
{
- let itfile::Cell{mask, note, inst, vol, efx, fxp} = cell;
- if !ch == 0
+ let mut chinst = [0u8; 64];
+ let mut instchmap: BTreeSet<(u8, u8)> = BTreeSet::new();
{
- self.miditick += <Rational as From<u32>>::from(960) / (ps.speed as u16 * ps.rpb as u16).into();
+ let h = |ch, cell, ps: PlayerState| {
+ if (ch == 0xff) || (ps.current_tick != 0) { return ps; }
+ let Cell{mask, mut inst, ..} = cell;
+ if mask & 0x22 != 0
+ { chinst[ch as usize] = inst; }
+ else
+ { inst = chinst[ch as usize]; }
+ if mask & 0x11 != 0
+ { instchmap.insert((ch, inst)); }
+ ps
+ };
+ let p = Player::new(Box::new(RefCell::new(h)), it);
+ p.process_orders();
}
- ps
+ let mut instch: BTreeMap<(u8, u8), usize> = BTreeMap::new();
+ instchmap.iter().enumerate().for_each(
+ |(i, p)| { instch.insert(*p, i + 1); } );
+ instch
}
- 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)
+ pub fn convert(&mut self, it: &ITFile)
{
- 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 instch = Converter::pre_pass(it);
+ println!("{:?}", instch);
+ self.trks.resize_with(instch.len() + 1, Default::default);
+ self.trks[0].push(TimedMidiEvent{ t: 0, e: MidiEvent::MetaTimeSig { n: 1, d_pot: 2 }});
+ let initspd = it.header.speed as f64;
+ let inittpo = it.header.tempo as f64;
+ let rpb = it.time_signature().unwrap_or((4, 4)).0 as f64;
+ self.trks[0].push(TimedMidiEvent{ t: 0, e: MidiEvent::MetaTempo(inittpo * 24. / rpb / initspd)});
+ for ((ch, inst), trkn) in instch.iter()
+ {
+ let tn = format!("instr #{} @ ch{}", *inst, *ch);
+ self.trks[*trkn].push(TimedMidiEvent{ t:0, e: MidiEvent::MetaTrackName(tn) });
+ }
+
{
- let mut instch = BTreeMap::new();
- m.iter().enumerate().for_each(
- |(i, p)| { instch.insert(*p, i + 1); } );
- self.instch = Some(instch);
+ let h = |ch:u8, cell, ps: PlayerState|
+ {
+ let Cell{mask, note, inst, vol: _, efx: _, fxp: _} = cell;
+ if !ch == 0
+ {
+ self.miditick += <Rational as From<u32>>::from(960) / (ps.rpb as u16).into();
+ }
+ if mask & 0x11 != 0 { self.chmem[ch as usize].borrow_mut().note = note; }
+ if mask & 0x22 != 0 { self.chmem[ch as usize].borrow_mut().inst = inst; }
+ self.fx_handlers.iter().for_each(
+ |h| {
+ let ev = (h.borrow_mut())(ch, cell, self.chmem[ch as usize].clone(), ps.clone(), self.miditick);
+ ev.iter().for_each(
+ |(e, is_common)| {
+ let target_track = instch.get(&(ch, self.chmem[ch as usize].borrow().inst)).unwrap();
+ (if *is_common { &mut self.trks[0] } else { &mut self.trks[*target_track] }).push(e.clone());
+ }
+ );
+ });
+ ps
+ };
+ let p = Player::new(Box::new(RefCell::new(h)), it);
+ p.process_orders();
}
- println!("{:?}", self.instch);
+
+ self.trks.iter_mut().for_each(|t| t.push(TimedMidiEvent{
+ t: t.last().unwrap_or(&TimedMidiEvent{t: 0, e: MidiEvent::MetaEndOfTrack}).t,
+ e: MidiEvent::MetaEndOfTrack}));
}
- pub fn convert(&'a mut self)
+ pub fn result(self) -> MidiFile
{
- self.pre_pass();
+ MidiFile{div: 960, tracks: self.trks}
}
}