diff --git a/tracker/agb-tracker-interop/src/lib.rs b/tracker/agb-tracker-interop/src/lib.rs index 38388b96..4e787bdc 100644 --- a/tracker/agb-tracker-interop/src/lib.rs +++ b/tracker/agb-tracker-interop/src/lib.rs @@ -50,6 +50,8 @@ pub enum PatternEffect { VolumeSlide(Num), NoteCut(u32), Portamento(Num), + /// Slide each tick the first amount to at most the second amount + TonePortamento(Num, Num), } #[cfg(feature = "quote")] @@ -208,6 +210,11 @@ impl quote::ToTokens for PatternEffect { let amount = amount.to_raw(); quote! { Portamento(agb_tracker::__private::Num::from_raw(#amount))} } + PatternEffect::TonePortamento(amount, target) => { + let amount = amount.to_raw(); + let target = target.to_raw(); + quote! { TonePortamento(agb_tracker::__private::Num::from_raw(#amount), agb_tracker::__private::Num::from_raw(#target))} + } }; tokens.append_all(quote! { diff --git a/tracker/agb-tracker/src/lib.rs b/tracker/agb-tracker/src/lib.rs index 4a349e3d..1f4faca4 100644 --- a/tracker/agb-tracker/src/lib.rs +++ b/tracker/agb-tracker/src/lib.rs @@ -124,7 +124,7 @@ impl Tracker { tick: 0, current_row: 0, - current_pattern: 2, + current_pattern: 6, } } @@ -278,13 +278,19 @@ impl TrackerChannel { } } PatternEffect::Portamento(amount) => { - let mut new_speed = self.base_speed; - - for _ in 0..tick { - new_speed *= amount.change_base(); + if tick != 0 { + self.base_speed *= amount.change_base(); + channel.playback(self.base_speed); + } + } + PatternEffect::TonePortamento(amount, target) => { + if *amount < 1.into() { + self.base_speed = + (self.base_speed * amount.change_base()).max(target.change_base()); + } else { + self.base_speed = + (self.base_speed * amount.change_base()).min(target.change_base()); } - - channel.playback(new_speed); } } } diff --git a/tracker/agb-xm-core/src/lib.rs b/tracker/agb-xm-core/src/lib.rs index 68455a56..553179f7 100644 --- a/tracker/agb-xm-core/src/lib.rs +++ b/tracker/agb-xm-core/src/lib.rs @@ -137,6 +137,7 @@ pub fn parse_module(module: &Module) -> TokenStream { let mut effect1 = PatternEffect::None; + let previous_note_and_sample = note_and_sample[channel_number]; let maybe_note_and_sample = if matches!(slot.note, Note::KeyOff) { effect1 = PatternEffect::Stop; note_and_sample[channel_number] = None; @@ -234,6 +235,40 @@ pub fn parse_module(module: &Module) -> TokenStream { PatternEffect::Portamento(portamento_amount.try_change_base().unwrap()) } + 0x3 => { + if let Some((previous_note, sample)) = previous_note_and_sample { + // we want to pitch slide to at most the current note by the parameter amount + let c4_speed = note_to_speed(Note::C4, 0.0, 0, module.frequency_type); + + let direction = if (previous_note as usize) < slot.note as usize { + -1.0 + } else { + 1.0 + }; + let speed = note_to_speed( + Note::C4, + effect_parameter as f64 * direction, + 0, + module.frequency_type, + ); + + let portamento_amount = speed / c4_speed; + + let target_speed = note_to_speed( + slot.note, + sample.fine_tune, + sample.relative_note, + module.frequency_type, + ); + + PatternEffect::TonePortamento( + portamento_amount.try_change_base().unwrap(), + target_speed.try_change_base().unwrap(), + ) + } else { + PatternEffect::None + } + } 0x8 => { PatternEffect::Panning(Num::new(slot.effect_parameter as i16 - 128) / 128) } @@ -356,7 +391,9 @@ fn note_to_frequency_linear(note: Note, fine_tune: f64, relative_note: i8) -> f6 } fn note_to_frequency_amega(note: Note, fine_tune: f64, relative_note: i8) -> f64 { - let note = (note as usize) + relative_note as usize; + let note = (note as usize) + .checked_add_signed(relative_note as isize) + .expect("Note gone negative"); let pos = (note % 12) * 8 + (fine_tune / 16.0) as usize; let frac = (fine_tune / 16.0) - (fine_tune / 16.0).floor();