#[derive(Debug)] pub enum Slide { Up(u8), Down(u8), FineUp(u8), FineDown(u8) } #[derive(Debug)] pub enum Effect { SetSpeed(u8), PosJump(u8), PattBreak(u8), SetVolume(u8), VolumeSlide(Slide), PortaDown(u8), PortaFineDown(u8), PortaExFineDown(u8), PortaUp(u8), PortaFineUp(u8), PortaExFineUp(u8), TonePorta(u8), Vibrato(u8, u8), Tremor(u8, u8), Arpeggio(u8, u8), VolumeSlideVib(Slide), VolumeSlideTPorta(Slide), SetChannelVolume(u8), ChVolSlide(Slide), SampleOffset(u8), PanSlide(Slide), Retrigger(u8, u8), Tremolo(u8, u8), GlissandoCtrl(bool), SetFinetune(u8), SetVibWaveform(u8), SetTremWaveform(u8), SetPanWaveform(u8), RowExtention(u8), VirtChanOp(u8), NNAOverride(u8), VolumeEnvEnable(bool), PanEnvEnable(bool), PitchEnvEnable(bool), SoundCtrl(u8), SampleOffsetHigh(u8), PattLoopStart, PattLoop(u8), NoteCut(u8), NoteDelay(u8), PattDelay(u8), SetActiveMacro(u8), SetTempo(u8), TempoSlideDown(u8), TempoSlideUp(u8), VibFine(u8, u8), SetGlobalVolume(u8), GlobalVolumeSlide(Slide), SetPan(u8), AutoPan(u8, u8), MidiMacro(u8), SmoothMidiMacro(u8), NoEffect } impl Effect { pub fn from_it_efx(f: (u8, u8)) -> Effect { let (efx, fxp) = f; match (efx + 'A' as u8 - 1) as char { 'A' => Effect::SetSpeed(fxp), 'B' => Effect::PosJump(fxp), 'C' => Effect::PattBreak(fxp), 'D' => match (fxp >> 4, fxp & 0x0f) { (0, y) => Effect::VolumeSlide(Slide::Down(y)), (x, 0) => Effect::VolumeSlide(Slide::Up(x)), (0xf, 0xf) => Effect::NoEffect, (0xf, y) => Effect::VolumeSlide(Slide::FineDown(y)), (x, 0xf) => Effect::VolumeSlide(Slide::FineUp(x)), _ => Effect::NoEffect }, 'E' => match fxp & 0xf0 { 0xF0 => Effect::PortaFineDown(fxp & 0x0f), 0xE0 => Effect::PortaExFineDown(fxp & 0x0f), _ => Effect::PortaDown(fxp) }, 'F' => match fxp & 0xf0 { 0xF0 => Effect::PortaFineUp(fxp & 0x0f), 0xE0 => Effect::PortaExFineUp(fxp & 0x0f), _ => Effect::PortaUp(fxp) }, 'G' => Effect::TonePorta(fxp), 'H' => Effect::Vibrato(fxp >> 4, fxp & 0x0f), 'I' => Effect::Tremor(fxp >> 4, fxp & 0x0f), 'J' => Effect::Arpeggio(fxp >> 4, fxp & 0x0f), 'K' => match (fxp >> 4, fxp & 0x0f) { (0, y) => Effect::VolumeSlideVib(Slide::Down(y)), (x, 0) => Effect::VolumeSlideVib(Slide::Up(x)), (0xf, 0xf) => Effect::NoEffect, (0xf, y) => Effect::VolumeSlideVib(Slide::FineDown(y)), (x, 0xf) => Effect::VolumeSlideVib(Slide::FineUp(x)), _ => Effect::NoEffect }, 'L' => match (fxp >> 4, fxp & 0x0f) { (0, y) => Effect::VolumeSlideTPorta(Slide::Down(y)), (x, 0) => Effect::VolumeSlideTPorta(Slide::Up(x)), (0xf, 0xf) => Effect::NoEffect, (0xf, y) => Effect::VolumeSlideTPorta(Slide::FineDown(y)), (x, 0xf) => Effect::VolumeSlideTPorta(Slide::FineUp(x)), _ => Effect::NoEffect }, 'M' => Effect::SetChannelVolume(fxp), 'N' => match (fxp >> 4, fxp & 0x0f) { (0, y) => Effect::ChVolSlide(Slide::Down(y)), (x, 0) => Effect::ChVolSlide(Slide::Up(x)), (0xf, 0xf) => Effect::NoEffect, (0xf, y) => Effect::ChVolSlide(Slide::FineDown(y)), (x, 0xf) => Effect::ChVolSlide(Slide::FineUp(x)), _ => Effect::NoEffect }, 'O' => Effect::SampleOffset(fxp), 'P' => match (fxp >> 4, fxp & 0x0f) { (0, y) => Effect::PanSlide(Slide::Up(y)), (x, 0) => Effect::PanSlide(Slide::Down(x)), (0xf, 0xf) => Effect::NoEffect, (0xf, y) => Effect::PanSlide(Slide::FineUp(y)), (x, 0xf) => Effect::PanSlide(Slide::FineDown(x)), _ => Effect::NoEffect }, 'Q' => Effect::Retrigger(fxp >> 4, fxp & 0x0f), 'R' => Effect::Tremolo(fxp >> 4, fxp & 0x0f), 'S' => match (fxp >> 4, fxp & 0x0f) { (0x1, e) => Effect::GlissandoCtrl(e != 0), (0x2, t) => Effect::SetFinetune(t), (0x3, w) => Effect::SetVibWaveform(w), (0x4, w) => Effect::SetTremWaveform(w), (0x5, w) => Effect::SetPanWaveform(w), (0x6, d) => Effect::RowExtention(d), (0x7, x @ 0..=2) => Effect::VirtChanOp(x), (0x7, x @ 3..=6) => Effect::NNAOverride(x - 3), (0x7, x @ 7..=8) => Effect::VolumeEnvEnable(x == 8), (0x7, x @ 9..=10) => Effect::PanEnvEnable(x == 10), (0x7, x @ 11..=12) => Effect::PitchEnvEnable(x == 10), (0x8, p) => Effect::SetPan(p * (255 / 15)), (0x9, c) => Effect::SoundCtrl(c), (0xa, o) => Effect::SampleOffsetHigh(o), (0xb, 0x0) => Effect::PattLoopStart, (0xb, r) => Effect::PattLoop(r), (0xc, t) => Effect::NoteCut(t), (0xd, t) => Effect::NoteDelay(t), (0xe, d) => Effect::PattDelay(d), (0xf, m) => Effect::SetActiveMacro(m), _ => Effect::NoEffect }, 'T' => match (fxp >> 4, fxp & 0x0f) { (0x0, v) => Effect::TempoSlideDown(v), (0x1, v) => Effect::TempoSlideUp(v), _ => Effect::SetTempo(fxp) }, 'U' => Effect::VibFine(fxp >> 4, fxp & 0x0f), 'V' => Effect::SetGlobalVolume(fxp), 'W' => match (fxp >> 4, fxp & 0x0f) { (0, y) => Effect::GlobalVolumeSlide(Slide::Down(y)), (x, 0) => Effect::GlobalVolumeSlide(Slide::Up(x)), (0xf, 0xf) => Effect::NoEffect, (0xf, y) => Effect::GlobalVolumeSlide(Slide::FineDown(y)), (x, 0xf) => Effect::GlobalVolumeSlide(Slide::FineUp(x)), _ => Effect::NoEffect }, 'X' => Effect::SetPan(fxp), 'Y' => Effect::AutoPan(fxp >> 4, fxp & 0x0f), 'Z' => Effect::MidiMacro(fxp), '\\' => Effect::SmoothMidiMacro(fxp), _ => Effect::NoEffect } } pub fn from_it_vol(v: u8) -> Effect { const GMAP: [u8; 10] = [0x00, 0x01, 0x04, 0x08, 0x10, 0x20, 0x40, 0x60, 0x80, 0xFF]; match v { 0..=64 => Effect::SetVolume(v), 65..=74 => Effect::VolumeSlide(Slide::FineUp(v - 65)), 75..=84 => Effect::VolumeSlide(Slide::FineDown(v - 75)), 85..=94 => Effect::VolumeSlide(Slide::Up(v - 85)), 95..=104 => Effect::VolumeSlide(Slide::Down(v - 95)), 105..=114 => Effect::PortaDown((v - 105) * 4), 115..=124 => Effect::PortaUp((v - 115) * 4), 128..=192 => Effect::SetPan(v - 128), 193..=202 => Effect::TonePorta(GMAP[(v - 193) as usize]), 203..=212 => Effect::Vibrato(0, v - 203), _ => Effect::NoEffect } } }