diff options
-rw-r--r-- | src/convert.rs | 45 | ||||
-rw-r--r-- | src/itfile.rs | 26 | ||||
-rw-r--r-- | src/main.rs | 2 | ||||
-rw-r--r-- | src/midifile.rs | 31 |
4 files changed, 81 insertions, 23 deletions
diff --git a/src/convert.rs b/src/convert.rs index a683772..56ab4f7 100644 --- a/src/convert.rs +++ b/src/convert.rs @@ -3,7 +3,7 @@ use std::cell::RefCell; use std::rc::Rc; use crate::itfile::{ITFile, Cell}; use crate::utils::Rational; -use crate::midifile::{MidiEvent, TimedMidiEvent, MidiTrack, MidiFile}; +use crate::midifile::{MidiEvent, TimedMidiEvent, MidiTrack, MidiFile, lint_track}; use crate::portmod::Effect; #[derive(Debug)] @@ -25,7 +25,7 @@ struct PlayerState } 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)>; +type EfxHandlerFn<'a> = dyn 'a + FnMut(u8, Cell, Rc<RefCell<ChannelMemory>>, PlayerState, Rational) -> Vec<(TimedMidiEvent, (u8, u8))>; struct Player<'a, 'b> { @@ -33,17 +33,33 @@ struct Player<'a, 'b> it: &'a ITFile } -#[derive(Default)] struct ChannelMemory { note: u8, postnote: u8, inst: u8, + postinst: u8, vol: u8, efxmem: [u8; 32], pitch: Rational } +impl Default for ChannelMemory +{ + fn default() -> ChannelMemory + { + ChannelMemory { + note: 0xff, + postnote: 0xff, + inst: 0xff, + postinst: 0xff, + vol: 0, + efxmem: Default::default(), + pitch: 0u32.into() + } + } +} + pub struct Converter<'a> { miditick: Rational, @@ -206,15 +222,19 @@ impl<'a> Converter<'a> 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; + let Cell { mask, note, mut inst, .. } = cell; if mask & 0x11 != 0 && ps.current_tick == 0 { + if mask & 0x22 == 0 + { + inst = chmem.borrow().postinst; + } 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)); + ret.push((TimedMidiEvent{t: t.as_int_trunc() as u32, e: MidiEvent::NoteOff{ch: 0, key: chmem.borrow().postnote, vel: 0x40}}, (ch, inst))); chmem.borrow_mut().postnote = 0xff; }, 0 => (), @@ -222,10 +242,11 @@ impl<'a> Converter<'a> { 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::NoteOff{ch: 0, key: chmem.borrow().postnote, vel: 0x40}}, (ch, chmem.borrow().postinst))); } - ret.push((TimedMidiEvent{t: t.as_int_trunc() as u32, e: MidiEvent::NoteOn{ch: 0, key: note, vel: 0x40}}, false)); + ret.push((TimedMidiEvent{t: t.as_int_trunc() as u32, e: MidiEvent::NoteOn{ch: 0, key: note, vel: 0x40}}, (ch, inst))); chmem.borrow_mut().postnote = note; + chmem.borrow_mut().postinst = inst; } } } @@ -269,7 +290,7 @@ impl<'a> Converter<'a> 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); + let tn = format!("{} (instr #{}) @ ch{}", it.insts[(inst - 1) as usize].inst_name() , *inst, *ch); self.trks[*trkn].push(TimedMidiEvent{ t:0, e: MidiEvent::MetaTrackName(tn) }); } @@ -287,9 +308,9 @@ impl<'a> Converter<'a> |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()); + |(e, (ch, inst))| { + let target_track = instch.get(&(*ch, *inst)).unwrap_or(&0); + self.trks[*target_track].push(e.clone()); } ); }); @@ -302,6 +323,8 @@ impl<'a> Converter<'a> 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})); + + self.trks.iter().for_each(|t| { lint_track(t); }); } pub fn result(self) -> MidiFile { diff --git a/src/itfile.rs b/src/itfile.rs index 57d235d..e744ce3 100644 --- a/src/itfile.rs +++ b/src/itfile.rs @@ -4,6 +4,8 @@ use std::io::SeekFrom; use std::fs::File; use std::mem::{size_of, zeroed}; +use crate::portmod::{Effect, Slide}; + macro_rules! zero_default { ($t:ty) => { @@ -283,18 +285,20 @@ impl Patt } else { print!(".. "); } if mask & 0x44 != 0 { - match vol + match Effect::from_it_vol(*vol) { - 0..=64 => print!("v{:02} ", vol), - 65..=74 => print!("a{:02} ", vol - 65), - 75..=84 => print!("b{:02} ", vol - 75), - 85..=94 => print!("c{:02} ", vol - 85), - 95..=104 => print!("d{:02} ", vol - 95), - 105..=114 => print!("e{:02} ", vol - 105), - 115..=124 => print!("f{:02} ", vol - 115), - 128..=192 => print!("p{:02} ", vol - 128), - 193..=202 => print!("g{:02} ", vol - 193), - 203..=212 => print!("h{:02} ", vol - 203), + Effect::SetVolume(vol) => print!("v{:02} ", vol), + Effect::VolumeSlide(s) => match s { + Slide::FineUp(x) => print!("a{:02} ", x), + Slide::FineDown(x) => print!("b{:02} ", x), + Slide::Up(x) => print!("c{:02} ", x), + Slide::Down(x) => print!("d{:02} ", x), + }, + Effect::PortaDown(x) => print!("e{:02} ", x / 4), + Effect::PortaUp(x) => print!("f{:02} ", x / 4), + Effect::SetPan(p) => print!("p{:02} ", p), + Effect::TonePorta(_) => print!("g{:02} ", vol - 193), + Effect::Vibrato(_, x) => print!("h{:02} ", x), _ => print!("??? ") } } else { print!("... "); } diff --git a/src/main.rs b/src/main.rs index 728c3e2..8280e5e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,7 +5,7 @@ mod convert; mod utils; fn main() -> Result<(), itfile::Error> { - match itfile::load("/home/chrisoft/Music/mods/p/rr_e.it") + match itfile::load("/home/chrisoft/Music/mods/nb_tear of the sun.it") { Ok(f) => { diff --git a/src/midifile.rs b/src/midifile.rs index 219a931..aaa2dfd 100644 --- a/src/midifile.rs +++ b/src/midifile.rs @@ -134,6 +134,37 @@ fn write_track<W>(f: &mut W, trk: &MidiTrack) -> io::Result<()> where W: Write Ok(()) } +pub fn lint_track(trk: &MidiTrack) -> bool +{ + let mut ret = true; + let mut notest = [false; 128]; + for te in trk + { + let TimedMidiEvent{t, e} = te; + match e + { + MidiEvent::NoteOff{ch: _, key: n, vel: _} | MidiEvent::NoteOn{ch: _, key: n, vel: 0} => { + if !notest[*n as usize] { ret = false; println!("Invalid note off {} @ {}", n, t); } + notest[*n as usize] = false; + }, + MidiEvent::NoteOn{ch: _, key: n, vel: _} => { + if notest[*n as usize] { ret = false; println!("Unterminated note {} @ {}", n, t); } + notest[*n as usize] = true; + }, + MidiEvent::MetaTrackName(s) => { + println!("Linting track {}", s); + }, + _ => {} + } + } + if ! matches!(trk.last().unwrap_or(&TimedMidiEvent{t: 0, e: MidiEvent::NoteOn{ch: 0, key: 0, vel: 0}}).e , MidiEvent::MetaEndOfTrack) + { + println!("Missing end of track event"); + ret = false; + } + ret +} + pub fn write_file(filename: &str, mf: &MidiFile) -> io::Result<()> { let mut f = File::create(filename)?; |