use crate::core::fixed::fixed::{Q15, Q8_8};
use crate::core::fixed::from_tracker::{retrig_mul_ft2, retrig_mul_s3m, retrig_volume_delta_q15};
use crate::core::fixed::units::{ChannelVolume, Finetune, Panning, PitchDelta, RetrigMul, Volume};
use crate::prelude::*;
use alloc::vec::Vec;
use serde::{Deserialize, Serialize};
fn note_retrig_volume_modifier_ft2(nibble: usize) -> NoteRetrigOperator {
note_retrig_volume_modifier_dispatch(nibble, retrig_mul_ft2(nibble as u8))
}
fn note_retrig_volume_modifier_s3m(nibble: usize) -> NoteRetrigOperator {
note_retrig_volume_modifier_dispatch(nibble, retrig_mul_s3m(nibble as u8))
}
#[inline]
fn note_retrig_volume_modifier_dispatch(nibble: usize, mul: RetrigMul) -> NoteRetrigOperator {
match nibble {
1..=5 | 9..=0xD => NoteRetrigOperator::Sum(retrig_volume_delta_q15(nibble as u8)),
6 | 7 | 0xE | 0xF => NoteRetrigOperator::Mul(mul),
_ => NoteRetrigOperator::None,
}
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq)]
pub enum TrackImportEffect {
Arpeggio(usize, usize),
ChannelVolume(ChannelVolume),
ChannelVolumeSlide0(Q15),
ChannelVolumeSlideN(Q15),
Glissando(bool),
InstrumentFineTune(Finetune),
InstrumentNewNoteAction(NewNoteAction),
InstrumentPanningEnvelopePosition(usize),
InstrumentPanningEnvelope(bool),
InstrumentPitchEnvelope(bool),
InstrumentSampleOffset(usize),
InstrumentSampleOffsetAddHigh(usize),
InstrumentSurround(bool),
InstrumentVolumeEnvelopePosition(usize),
InstrumentVolumeEnvelope(bool),
NoteCut(usize, bool),
NoteDelay(usize),
NoteFadeOut(usize, bool),
NoteOff(usize, bool),
NoteRetrig(usize),
NoteRetrigExtended(usize, usize),
NoteRetrigExtendedS3m(usize, usize),
Panbrello(Q8_8, Q15),
PanbrelloWaveform(Waveform, bool),
Panning(Panning),
PanningSlide0(Q15),
PanningSlideN(Q15),
PortamentoUp(i16),
PortamentoDown(i16),
PortamentoFineUp(i16),
PortamentoFineDown(i16),
PortamentoExtraFineUp(i16),
PortamentoExtraFineDown(i16),
TonePortamento(i16),
TonePortamentoFxVol(i16),
Tremolo(Q8_8, Q15),
TremoloWaveform(Waveform, bool),
Tremor(usize, usize),
Vibrato(Q8_8, PitchDelta),
VibratoFine(Q8_8, PitchDelta),
VibratoSpeed(Q8_8),
VibratoDepth(PitchDelta),
VibratoDepthFxVol(PitchDelta),
VibratoWaveform(Waveform, bool),
Volume(Volume, usize),
VolumeSlide0(Q15),
VolumeSlideN(Q15),
}
impl TrackImportEffect {
fn to_track_effect(self) -> Option<TrackEffect> {
match self {
TrackImportEffect::Arpeggio(h1, h2) => {
if h1 != 0 || h2 != 0 {
Some(TrackEffect::Arpeggio {
half1: h1,
half2: h2,
})
} else {
None
}
}
TrackImportEffect::Panning(pos) => Some(TrackEffect::Panning(pos)),
TrackImportEffect::ChannelVolume(pos) => Some(TrackEffect::ChannelVolume(pos)),
TrackImportEffect::Glissando(value) => Some(TrackEffect::Glissando(value)),
TrackImportEffect::InstrumentFineTune(tune) => {
Some(TrackEffect::InstrumentFineTune(tune))
}
TrackImportEffect::InstrumentNewNoteAction(nna) => {
Some(TrackEffect::InstrumentNewNoteAction(nna))
}
TrackImportEffect::InstrumentPanningEnvelopePosition(pos) => {
Some(TrackEffect::InstrumentPanningEnvelopePosition(pos))
}
TrackImportEffect::InstrumentPanningEnvelope(set) => {
Some(TrackEffect::InstrumentPanningEnvelope(set))
}
TrackImportEffect::InstrumentPitchEnvelope(set) => {
Some(TrackEffect::InstrumentPitchEnvelope(set))
}
TrackImportEffect::InstrumentSampleOffset(offset) => {
Some(TrackEffect::InstrumentSampleOffset(offset))
}
TrackImportEffect::InstrumentSurround(set) => {
Some(TrackEffect::InstrumentSurround(set))
}
TrackImportEffect::InstrumentVolumeEnvelopePosition(pos) => {
Some(TrackEffect::InstrumentVolumeEnvelopePosition(pos))
}
TrackImportEffect::InstrumentVolumeEnvelope(set) => {
Some(TrackEffect::InstrumentVolumeEnvelope(set))
}
TrackImportEffect::NoteCut(tick, past) => Some(TrackEffect::NoteCut { tick, past }),
TrackImportEffect::NoteDelay(ticks) => {
if ticks != 0 {
Some(TrackEffect::NoteDelay(ticks))
} else {
None
}
}
TrackImportEffect::NoteFadeOut(tick, past) => {
Some(TrackEffect::NoteFadeOut { tick, past })
}
TrackImportEffect::NoteOff(tick, past) => Some(TrackEffect::NoteOff { tick, past }),
TrackImportEffect::NoteRetrig(interval) => Some(TrackEffect::NoteRetrig {
speed: interval,
volume_modifier: NoteRetrigOperator::None,
}),
TrackImportEffect::NoteRetrigExtended(interval, vol) => Some(TrackEffect::NoteRetrig {
speed: interval,
volume_modifier: note_retrig_volume_modifier_ft2(vol),
}),
TrackImportEffect::NoteRetrigExtendedS3m(interval, vol) => {
Some(TrackEffect::NoteRetrig {
speed: interval,
volume_modifier: note_retrig_volume_modifier_s3m(vol),
})
}
TrackImportEffect::Tremor(on, off) => Some(TrackEffect::Tremor {
on_time: on,
off_time: off,
}),
TrackImportEffect::Volume(value, tick) => Some(TrackEffect::Volume { value, tick }),
TrackImportEffect::Panbrello(_, _)
| TrackImportEffect::PanbrelloWaveform(_, _)
| TrackImportEffect::PanningSlide0(_)
| TrackImportEffect::PanningSlideN(_)
| TrackImportEffect::ChannelVolumeSlide0(_)
| TrackImportEffect::ChannelVolumeSlideN(_)
| TrackImportEffect::PortamentoUp(_)
| TrackImportEffect::PortamentoDown(_)
| TrackImportEffect::PortamentoFineUp(_)
| TrackImportEffect::PortamentoFineDown(_)
| TrackImportEffect::PortamentoExtraFineUp(_)
| TrackImportEffect::PortamentoExtraFineDown(_)
| TrackImportEffect::TonePortamento(_)
| TrackImportEffect::TonePortamentoFxVol(_)
| TrackImportEffect::Tremolo(_, _)
| TrackImportEffect::TremoloWaveform(_, _)
| TrackImportEffect::Vibrato(_, _)
| TrackImportEffect::VibratoFine(_, _)
| TrackImportEffect::VibratoSpeed(_)
| TrackImportEffect::VibratoDepth(_)
| TrackImportEffect::VibratoDepthFxVol(_)
| TrackImportEffect::VibratoWaveform(_, _)
| TrackImportEffect::VolumeSlide0(_)
| TrackImportEffect::VolumeSlideN(_)
| TrackImportEffect::InstrumentSampleOffsetAddHigh(_) => None,
}
}
fn remove_duplicates_and_merge(mut effects: Vec<TrackEffect>) -> Vec<TrackEffect> {
let mut unique_effects: Vec<TrackEffect> = Vec::new();
for effect in effects.drain(..) {
let merge_target = unique_effects.iter().enumerate().find_map(|(idx, e)| {
if core::mem::discriminant(e) == core::mem::discriminant(&effect) {
e.merge(&effect).map(|m| (idx, m))
} else {
None
}
});
match merge_target {
Some((idx, merged)) => unique_effects[idx] = merged,
None => unique_effects.push(effect),
}
}
unique_effects
}
pub fn to_track_effects(effects: &[TrackImportEffect]) -> Vec<TrackEffect> {
let mut vte: Vec<TrackEffect> = Vec::new();
for fx in effects {
if let Some(te) = Self::to_track_effect(*fx) {
vte.push(te);
}
}
Self::remove_duplicates_and_merge(vte)
}
}