aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Chris Xiong <chirs241097@gmail.com> 2022-12-21 22:46:55 -0500
committerGravatar Chris Xiong <chirs241097@gmail.com> 2022-12-21 22:46:55 -0500
commita1823d3f9568aac9eeafc91e444744906440ded8 (patch)
treef1c63f72458095a2322d0def48ba7ecdde689845
parent09b01cef6389b1354073c2846303d027e4a73043 (diff)
downloadit2midi-a1823d3f9568aac9eeafc91e444744906440ded8.tar.xz
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.
-rw-r--r--src/convert.rs59
-rw-r--r--src/utils.rs14
2 files changed, 72 insertions, 1 deletions
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<Rational>,
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<RefCell<ChannelMemory>>, 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 += <Rational as From<u32>>::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<u8> for Rational
fn from(v: u8) -> Self { Rational::from_int(v as i64) }
}
+impl From<Rational> 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}}