aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/convert.rs45
-rw-r--r--src/itfile.rs26
-rw-r--r--src/main.rs2
-rw-r--r--src/midifile.rs31
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)?;