From a1823d3f9568aac9eeafc91e444744906440ded8 Mon Sep 17 00:00:00 2001 From: Chris Xiong Date: Wed, 21 Dec 2022 22:46:55 -0500 Subject: Convert tempo effects. Introducing an it2midi-specific swing on/off command to aid real bpm calculation when legacy swing technique is used in the file. --- src/convert.rs | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++- src/utils.rs | 14 ++++++++++++++ 2 files changed, 72 insertions(+), 1 deletion(-) diff --git a/src/convert.rs b/src/convert.rs index a8aeb1a..c864400 100644 --- a/src/convert.rs +++ b/src/convert.rs @@ -18,6 +18,7 @@ struct PlayerState speed: u8, tempo: u8, rpb: u8, + swing_speed: Option, loop_start: u8, loop_ctr: u8, row_extension: u8, @@ -92,6 +93,31 @@ impl<'a, 'b> Player<'a, 'b> Effect::PattLoopStart => PlayerState{loop_start: ps.current_row, ..ps}, Effect::SetTempo(t) => PlayerState{tempo: t, ..ps}, Effect::RowExtention(t) => PlayerState{row_extension: ps.row_extension + t, ..ps}, + Effect::SetVibWaveform(0xe) => { //proprietary command: enter swing mode + let mut ticks = 0; + let mut ts = ps.speed; + for r in ps.current_row..ps.current_row + ps.rpb + { + let pat = &self.it.patterns[self.it.orders[ps.current_ord as usize] as usize]; + for c in 0..pat.nch + { + let rcell = pat.cell_at(r, c); + if rcell.mask & 0x88 != 0 + { + let fx = Effect::from_it_efx((rcell.efx, rcell.fxp)); + match fx + { + Effect::SetSpeed(a) => ts = a, + _ => () + } + } + } + ticks += ts; + } + let swing_speed = Some(Rational::from(ticks) / Rational::from(ps.rpb)); + PlayerState{swing_speed, ..ps} + }, + Effect::SetVibWaveform(0xf) => PlayerState{swing_speed: None, ..ps}, //proprietary command: exit swing mode _ => ps } } @@ -178,6 +204,7 @@ impl<'a, 'b> Player<'a, 'b> speed: self.it.header.speed, tempo: self.it.header.tempo, rpb: self.it.time_signature().unwrap_or((4, 16)).0 as u8, + swing_speed: None, loop_start: 0, loop_ctr: !0, row_extension: 0, @@ -273,6 +300,32 @@ impl<'a, 'b> Converter<'a, 'b> } ret }; + let tempofx = |ch: u8, cell, _chmem: Rc>, ps: PlayerState, t: Rational| { + if !ch == 0 || ps.in_rep { return Vec::new(); } + let mut ret = Vec::new(); + let Cell { mask, efx, fxp, .. } = cell; + let fx = Effect::from_it_efx((efx, fxp)); + if mask & 0x88 != 0 + { + match fx + { + Effect::SetSpeed(_) | Effect::SetTempo(_) | + Effect::TempoSlideDown(_) | Effect::TempoSlideUp(_) | + Effect::SetVibWaveform(0xe) | Effect::SetVibWaveform(0xf) => + { + match ps.swing_speed + { + None => ret.push((TimedMidiEvent{ t: t.as_int_trunc() as u32, e: MidiEvent::MetaTempo(ps.tempo as f64 * 24. / ps.rpb as f64 / ps.speed as f64)}, (!0, !0))), + Some(ss) => ret.push((TimedMidiEvent{ t: t.as_int_trunc() as u32, e: MidiEvent::MetaTempo((Rational::from(ps.tempo as u16 * 24) / Rational::from(ps.rpb) / ss).into())}, (!0, !0))) + } + } + _ => () + } + } + ret + }; + + self.fx_handlers.push(Box::new(RefCell::new(tempofx))); self.fx_handlers.push(Box::new(RefCell::new(nonfx))); } fn pre_pass(it: &ITFile) -> BTreeMap<(u8, u8), usize> @@ -322,7 +375,11 @@ impl<'a, 'b> Converter<'a, 'b> let Cell{mask, note, inst, vol: _, efx: _, fxp: _} = cell; if !ch == 0 { - self.miditick += >::from(960) / (ps.rpb as u16).into(); + match ps.swing_speed + { + None => self.miditick += Rational::from(960u32) / (ps.rpb as u16).into(), + Some(ss) => self.miditick += Rational::from(960u32) / (ps.rpb as u16).into() * (Rational::from(ps.speed) / ss) + } } 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; } diff --git a/src/utils.rs b/src/utils.rs index cbe9716..0fc4328 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,4 +1,5 @@ use std::ops::*; +use std::fmt::{Debug, Formatter, Error}; #[derive(Copy, Clone)] pub struct Rational @@ -15,6 +16,14 @@ impl Default for Rational fn default() -> Rational { Rational::from_int(0) } } +impl Debug for Rational +{ + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> + { + f.write_fmt(format_args!("{}/{}", self.n, self.d)) + } +} + impl Add for Rational { type Output = Self; @@ -70,6 +79,11 @@ impl From for Rational fn from(v: u8) -> Self { Rational::from_int(v as i64) } } +impl From for f64 +{ + fn from(v: Rational) -> Self { v.n as f64 / v.d as f64 } +} + impl Rational { fn from_int(v: i64) -> Rational {Rational{n: v, d: 1}} -- cgit v1.2.3