use crate::effect::MidiMacroType;
use crate::import::patternslot::PatternSlot;
use crate::import::track_import_effect::TrackImportEffect;
use crate::import::track_import_unit::TrackImportUnit;
use crate::prelude::*;
use alloc::vec;
use alloc::vec::Vec;
pub struct ItEffect;
impl ItEffect {
fn it_volume_slide(fx: u8) -> Option<TrackImportEffect> {
let nibble_high = (fx & 0xF0) >> 4;
let nibble_low = fx & 0x0F;
match (nibble_high, nibble_low) {
(f, 0) => Some(TrackImportEffect::VolumeSlideN(f as f32 / 64.0)),
(0, f) => Some(TrackImportEffect::VolumeSlideN(-(f as f32) / 64.0)),
(f, 0xF) => Some(TrackImportEffect::VolumeSlide0(f as f32 / 64.0)),
(0xF, f) => Some(TrackImportEffect::VolumeSlide0(-(f as f32) / 64.0)),
_ => None,
}
}
pub fn parse_it_effect(
freq_type: FrequencyType,
current: &PatternSlot,
) -> Option<Vec<TrackImportEffect>> {
match current.effect_type {
0x04 => Self::it_volume_slide(current.effect_parameter).map(|tie| vec![tie]),
0x05 => {
let fx = current.effect_parameter;
let nibble_high = (fx & 0xF0) >> 4;
let nibble_low = fx & 0x0F;
match (nibble_high, nibble_low) {
(0xE, low) => {
Some(vec![TrackImportEffect::PortamentoExtraFineDown(low as f32)])
}
(0xF, low) => Some(vec![TrackImportEffect::PortamentoFineDown(
4.0 * low as f32,
)]),
(_high, _low) => Some(vec![TrackImportEffect::PortamentoDown(4.0 * fx as f32)]),
}
}
0x06 => {
let fx = current.effect_parameter;
let nibble_high = (fx & 0xF0) >> 4;
let nibble_low = fx & 0x0F;
match (nibble_high, nibble_low) {
(0xE, low) => Some(vec![TrackImportEffect::PortamentoExtraFineUp(
-(low as f32),
)]),
(0xF, low) => {
Some(vec![TrackImportEffect::PortamentoFineUp(-4.0 * low as f32)])
}
(_high, _low) => Some(vec![TrackImportEffect::PortamentoUp(-4.0 * fx as f32)]),
}
}
0x07 => {
let param = current.effect_parameter as f32;
let speed = match freq_type {
FrequencyType::LinearFrequencies => 4.0 * param,
FrequencyType::AmigaFrequencies => param,
};
Some(vec![TrackImportEffect::TonePortamento(speed)])
}
0x08 => {
let param = current.effect_parameter;
let speed = ((param & 0xF0) >> 4) as f32 / 64.0;
let depth = (param & 0x0F) as f32 * 4.0 / 16.0;
Some(vec![TrackImportEffect::Vibrato(speed, depth)])
}
0x09 => {
let param = current.effect_parameter;
let on_time = param >> 4;
let off_time = param & 0x0F;
Some(vec![TrackImportEffect::Tremor(
on_time as usize,
off_time as usize,
)])
}
0x0A => {
let param = current.effect_parameter;
if param > 0 {
let v1 = (param >> 4) as usize;
let v2 = (param & 0x0F) as usize;
Some(vec![TrackImportEffect::Arpeggio(v1, v2)])
} else {
None
}
}
0x0B => {
if let Some(vste) = Self::it_volume_slide(current.effect_parameter) {
Some(vec![TrackImportEffect::Vibrato(0.0, 0.0), vste])
} else {
Some(vec![
TrackImportEffect::Vibrato(0.0, 0.0),
TrackImportEffect::VolumeSlideN(0.0),
])
}
}
0x0C => {
if let Some(vste) = Self::it_volume_slide(current.effect_parameter) {
Some(vec![TrackImportEffect::TonePortamento(0.0), vste])
} else {
Some(vec![
TrackImportEffect::TonePortamento(0.0),
TrackImportEffect::VolumeSlideN(0.0),
])
}
}
0x0D => {
let volume = current.effect_parameter.min(64) as f32 / 64.0;
Some(vec![TrackImportEffect::ChannelVolume(volume)])
}
0x0E => {
let fx = current.effect_parameter;
let nibble_high = (fx & 0xF0) >> 4;
let nibble_low = fx & 0x0F;
match (nibble_high, nibble_low) {
(f, 0) => Some(vec![TrackImportEffect::ChannelVolumeSlideN(
f as f32 / 64.0,
)]),
(0, low) => Some(vec![TrackImportEffect::ChannelVolumeSlideN(
-(low as f32) / 64.0,
)]),
(f, 0xF) => Some(vec![TrackImportEffect::ChannelVolumeSlide0(
f as f32 / 64.0,
)]),
(0xF, low) => Some(vec![TrackImportEffect::ChannelVolumeSlide0(
-(low as f32) / 64.0,
)]),
_ => None,
}
}
0x0F => Some(vec![TrackImportEffect::InstrumentSampleOffset(
current.effect_parameter as usize * 256,
)]),
0x10 => {
let fx = current.effect_parameter;
let nibble_high = (fx & 0xF0) >> 4;
let nibble_low = fx & 0x0F;
match (nibble_high, nibble_low) {
(f, 0) => Some(vec![TrackImportEffect::PanningSlideN(f as f32 / 16.0)]),
(0, f) => Some(vec![TrackImportEffect::PanningSlideN(-(f as f32) / 16.0)]),
(f, 0xF) => Some(vec![TrackImportEffect::PanningSlide0(f as f32 / 16.0)]),
(0xF, f) => Some(vec![TrackImportEffect::PanningSlide0(-(f as f32) / 16.0)]),
_ => None,
}
}
0x11 => {
let param = current.effect_parameter;
let vol = param >> 4;
let speed = param & 0x0F;
Some(vec![TrackImportEffect::NoteRetrigExtendedS3m(
speed as usize,
vol as usize,
)])
}
0x12 => {
let param = current.effect_parameter;
let speed = ((param & 0xF0) >> 4) as f32 / 64.0;
let depth = (param & 0x0F) as f32 / 16.0;
Some(vec![TrackImportEffect::Tremolo(speed, depth)])
}
0x13 => {
let fx = current.effect_parameter >> 4;
let param = current.effect_parameter & 0xF;
match fx {
0x3 => {
let waveform = match param & 0b0000_0011 {
1 => Waveform::RampDown,
2 => Waveform::Square,
3 => Waveform::Random,
_ => Waveform::Sine,
};
Some(vec![TrackImportEffect::VibratoWaveform(waveform, true)])
}
0x4 => {
let waveform = match param & 0b0000_0011 {
1 => Waveform::RampDown,
2 => Waveform::Square,
3 => Waveform::Random,
_ => Waveform::Sine,
};
Some(vec![TrackImportEffect::TremoloWaveform(waveform, true)])
}
0x5 => {
let waveform = match param & 0b0000_0011 {
1 => Waveform::RampDown,
2 => Waveform::Square,
3 => Waveform::Random,
_ => Waveform::Sine,
};
Some(vec![TrackImportEffect::PanbrelloWaveform(waveform, true)])
}
0x7 => {
match param {
0x0 => Some(vec![TrackImportEffect::NoteCut(0, true)]),
0x1 => Some(vec![TrackImportEffect::NoteOff(0, true)]),
0x2 => Some(vec![TrackImportEffect::NoteFadeOut(0, true)]),
0x3 => Some(vec![TrackImportEffect::InstrumentNewNoteAction(
NewNoteAction::NoteCut,
)]),
0x4 => Some(vec![TrackImportEffect::InstrumentNewNoteAction(
NewNoteAction::Continue,
)]),
0x5 => Some(vec![TrackImportEffect::InstrumentNewNoteAction(
NewNoteAction::NoteOff,
)]),
0x6 => Some(vec![TrackImportEffect::InstrumentNewNoteAction(
NewNoteAction::NoteFadeOut,
)]),
0x7 => Some(vec![TrackImportEffect::InstrumentVolumeEnvelope(true)]),
0x8 => Some(vec![TrackImportEffect::InstrumentVolumeEnvelope(false)]),
0x9 => Some(vec![TrackImportEffect::InstrumentPanningEnvelope(true)]),
0xA => Some(vec![TrackImportEffect::InstrumentPanningEnvelope(false)]),
0xB => Some(vec![TrackImportEffect::InstrumentPitchEnvelope(true)]),
0xC => Some(vec![TrackImportEffect::InstrumentPitchEnvelope(false)]),
_ => None,
}
}
0x8 => Some(vec![TrackImportEffect::Panning((param as f32) / 15.0)]),
0x9 => Some(vec![TrackImportEffect::InstrumentSurround(param == 1)]),
0xA => Some(vec![TrackImportEffect::InstrumentSampleOffsetAddHigh(
param as usize * 65536,
)]),
0xC => Some(vec![TrackImportEffect::NoteCut(param as usize, false)]),
0xD => Some(vec![TrackImportEffect::NoteDelay(param as usize)]),
_ => None,
}
}
0x15 => {
let param = current.effect_parameter;
let speed = ((param & 0xF0) >> 4) as f32 / 64.0;
let depth = (param & 0x0F) as f32 / 16.0;
Some(vec![TrackImportEffect::Vibrato(speed, depth)])
}
0x18 => Some(vec![TrackImportEffect::Panning(
(current.effect_parameter as f32) / 255.0,
)]),
0x19 => {
let param = current.effect_parameter;
let speed = ((param & 0xF0) >> 4) as f32 / 256.0;
let depth = (param & 0x0F) as f32 * 4.0 / 16.0;
Some(vec![TrackImportEffect::Panbrello(speed, depth)])
}
_ => None,
}
}
pub fn parse_global_it_effect(current: &PatternSlot) -> Option<GlobalEffect> {
match current.effect_type {
0x01 => {
let speed = current.effect_parameter as usize;
if speed == 0 {
None
} else {
Some(GlobalEffect::Speed(speed))
}
}
0x02 => Some(GlobalEffect::PositionJump(
current.effect_parameter as usize,
)),
0x03 => Some(GlobalEffect::PatternBreak(
current.effect_parameter as usize,
)),
0x13 => {
let fx = current.effect_parameter >> 4;
let param = current.effect_parameter & 0xF;
match fx {
0x6 => Some(GlobalEffect::PatternDelay {
quantity: param as usize,
tempo: false,
}),
0xB => Some(GlobalEffect::PatternLoop(param as usize)),
0xE => Some(GlobalEffect::PatternDelay {
quantity: param as usize,
tempo: true,
}),
0xF => Some(GlobalEffect::MidiMacro(MidiMacroType::SelectParametric(
param as usize,
))),
_ => None,
}
}
0x14 => {
let param = current.effect_parameter;
let upper_nibble = param >> 4;
let lower_nibble = param & 0x0F;
match (upper_nibble, lower_nibble) {
(0, f) => Some(GlobalEffect::BpmSlide(-(f as isize))),
(1, f) => Some(GlobalEffect::BpmSlide(f as isize)),
_ => Some(GlobalEffect::Bpm(param as usize)),
}
}
0x16 => Some(GlobalEffect::Volume(
(current.effect_parameter.min(128) as f32) / 128.0,
)),
0x17 => {
let fx = current.effect_parameter;
let nibble_high = (fx & 0xF0) >> 4;
let nibble_low = fx & 0x0F;
match (nibble_high, nibble_low) {
(f, 0) => Some(GlobalEffect::VolumeSlide {
speed: f as f32 / 128.0,
fine: false,
}),
(0, f) => Some(GlobalEffect::VolumeSlide {
speed: -(f as f32) / 128.0,
fine: false,
}),
(f, 0xF) => Some(GlobalEffect::VolumeSlide {
speed: f as f32 / 128.0,
fine: true,
}),
(0xF, f) => Some(GlobalEffect::VolumeSlide {
speed: -(f as f32) / 128.0,
fine: true,
}),
_ => None,
}
}
0x1A => {
let param = current.effect_parameter;
if param & 0x80 == 0 {
Some(GlobalEffect::MidiMacro(MidiMacroType::Parametric(param)))
} else {
Some(GlobalEffect::MidiMacro(MidiMacroType::Fixed {
idx: (param - 0x80) as usize,
z: param,
}))
}
}
_ => None,
}
}
fn parse_it_volume(freq_type: FrequencyType, slot: &PatternSlot) -> Option<TrackImportEffect> {
if slot.volume > 212 {
return None;
}
if slot.volume <= 64 {
return Some(TrackImportEffect::Volume(slot.volume as f32 / 64.0, 0));
}
if (slot.volume & 0x7F) <= 64 {
let p = (slot.volume & 0x7F) as f32 / 64.0;
return Some(TrackImportEffect::Panning(p));
}
let temp = if slot.volume & 0x80 == 0 {
slot.volume - b'A'
} else {
(slot.volume & 0x7F) - 5
};
let fx = temp / 10;
let param = temp % 10;
match fx {
0x0 => return Some(TrackImportEffect::VolumeSlide0(param as f32 / 64.0)),
0x1 => return Some(TrackImportEffect::VolumeSlide0(-(param as f32) / 64.0)),
0x2 => return Some(TrackImportEffect::VolumeSlideN(param as f32 / 64.0)),
0x3 => return Some(TrackImportEffect::VolumeSlideN(-(param as f32) / 64.0)),
0x4 => return Some(TrackImportEffect::PortamentoDown(16.0 * param as f32)),
0x5 => return Some(TrackImportEffect::PortamentoUp(-16.0 * param as f32)),
0x6 => {
let param = match param {
1 => 1,
2 => 4,
3 => 8,
4 => 16,
5 => 32,
6 => 64,
7 => 96,
8 => 128,
9 => 255,
_ => 0,
} as f32;
let speed = match freq_type {
FrequencyType::LinearFrequencies => 4.0 * param,
FrequencyType::AmigaFrequencies => param,
};
return Some(TrackImportEffect::TonePortamento(speed));
}
0x7 => {
let depth = (param & 0x0F) as f32 * 4.0 / 16.0;
return Some(TrackImportEffect::Vibrato(0.0, depth));
}
_ => {}
}
None
}
pub fn it_unpack_pattern(
freq_type: FrequencyType,
pattern: &[Vec<PatternSlot>],
) -> Vec<Vec<TrackImportUnit>> {
pattern
.iter()
.map(|channel| {
channel
.iter()
.map(|slot| {
let te = Self::parse_it_effect(freq_type, slot);
let ve = Self::parse_it_volume(freq_type, slot);
let cc = Self::parse_global_it_effect(slot);
let mut tiu = TrackImportUnit::default();
tiu.note = slot.note;
tiu.instrument = slot.instrument;
if let Some(e) = ve {
tiu.effects.push(e);
}
if let Some(e) = te {
tiu.effects.extend(e)
}
if let Some(c) = cc {
tiu.global_effects.push(c);
}
tiu
})
.collect::<Vec<TrackImportUnit>>() })
.collect::<Vec<Vec<TrackImportUnit>>>() }
}