Add note delay and fix envelopes (#698)

Adds support for the effect EDx (note delay) although it isn't
completely correct. And envelopes were too short so I've fixed that too.

- [x] no changelog update needed
This commit is contained in:
Gwilym Inzani 2024-05-16 23:12:00 +01:00 committed by GitHub
commit bcafe7be75
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 44 additions and 9 deletions

View file

@ -62,6 +62,7 @@ pub enum PatternEffect {
VolumeSlide(Num<i16, 8>), VolumeSlide(Num<i16, 8>),
FineVolumeSlide(Num<i16, 8>), FineVolumeSlide(Num<i16, 8>),
NoteCut(u32), NoteCut(u32),
NoteDelay(u32),
Portamento(Num<u16, 12>), Portamento(Num<u16, 12>),
/// Slide each tick the first amount to at most the second amount /// Slide each tick the first amount to at most the second amount
TonePortamento(Num<u16, 12>, Num<u16, 12>), TonePortamento(Num<u16, 12>, Num<u16, 12>),
@ -289,6 +290,7 @@ impl quote::ToTokens for PatternEffect {
quote! { FineVolumeSlide(agb_tracker::__private::Num::from_raw(#amount))} quote! { FineVolumeSlide(agb_tracker::__private::Num::from_raw(#amount))}
} }
PatternEffect::NoteCut(wait) => quote! { NoteCut(#wait) }, PatternEffect::NoteCut(wait) => quote! { NoteCut(#wait) },
PatternEffect::NoteDelay(wait) => quote! { NoteDelay(#wait) },
PatternEffect::Portamento(amount) => { PatternEffect::Portamento(amount) => {
let amount = amount.to_raw(); let amount = amount.to_raw();
quote! { Portamento(agb_tracker::__private::Num::from_raw(#amount))} quote! { Portamento(agb_tracker::__private::Num::from_raw(#amount))}

View file

@ -405,6 +405,20 @@ impl TrackerChannel {
} }
} }
} }
PatternEffect::NoteDelay(wait) => {
if tick < *wait {
channel.pause();
}
if tick == *wait {
channel.resume();
channel.volume(
(self.volume * global_settings.volume)
.try_change_base()
.unwrap(),
);
}
}
PatternEffect::Portamento(amount) => { PatternEffect::Portamento(amount) => {
if tick != 0 { if tick != 0 {
self.base_speed *= amount.change_base(); self.base_speed *= amount.change_base();

View file

@ -71,7 +71,7 @@ pub fn parse_module(module: &Module) -> TokenStream {
let envelope = &instrument.volume_envelope; let envelope = &instrument.volume_envelope;
let envelope_id = if envelope.enabled { let envelope_id = if envelope.enabled {
let envelope: EnvelopeData = envelope.as_ref().into(); let envelope = EnvelopeData::new(envelope, module.default_bpm as u32);
let id = existing_envelopes let id = existing_envelopes
.entry(envelope) .entry(envelope)
.or_insert_with_key(|envelope| { .or_insert_with_key(|envelope| {
@ -347,6 +347,7 @@ pub fn parse_module(module: &Module) -> TokenStream {
-Num::new((slot.effect_parameter & 0xf) as i16) / 128, -Num::new((slot.effect_parameter & 0xf) as i16) / 128,
), ),
0xC => PatternEffect::NoteCut((slot.effect_parameter & 0xf).into()), 0xC => PatternEffect::NoteCut((slot.effect_parameter & 0xf).into()),
0xD => PatternEffect::NoteDelay((slot.effect_parameter & 0xf).into()),
_ => PatternEffect::None, _ => PatternEffect::None,
}, },
0xF => match slot.effect_parameter { 0xF => match slot.effect_parameter {
@ -519,13 +520,12 @@ struct EnvelopeData {
loop_end: Option<usize>, loop_end: Option<usize>,
} }
impl From<&xmrs::envelope::Envelope> for EnvelopeData { impl EnvelopeData {
fn from(e: &xmrs::envelope::Envelope) -> Self { fn new(e: &xmrs::envelope::Envelope, bpm: u32) -> Self {
let mut amounts = vec![]; let mut amounts = vec![];
// it should be sampled at 50fps, but we're sampling at 60fps, so need to do a bit of cheating here. for frame in 0..=(Self::envelope_frame_to_gba_frame(e.point.last().unwrap().frame, bpm)) {
for frame in 0..(e.point.last().unwrap().frame * 60 / 50) { let xm_frame = Self::gba_frame_to_envelope_frame(frame, bpm);
let xm_frame = frame * 50 / 60;
let index = e let index = e
.point .point
.iter() .iter()
@ -542,14 +542,23 @@ impl From<&xmrs::envelope::Envelope> for EnvelopeData {
} }
let sustain = if e.sustain_enabled { let sustain = if e.sustain_enabled {
Some(e.point[e.sustain_point as usize].frame as usize * 60 / 50) Some(
Self::envelope_frame_to_gba_frame(e.point[e.sustain_point as usize].frame, bpm)
as usize,
)
} else { } else {
None None
}; };
let (loop_start, loop_end) = if e.loop_enabled { let (loop_start, loop_end) = if e.loop_enabled {
( (
Some(e.point[e.loop_start_point as usize].frame as usize * 60 / 50), Some(Self::envelope_frame_to_gba_frame(
Some(e.point[e.loop_end_point as usize].frame as usize * 60 / 50), e.point[e.loop_start_point as usize].frame,
bpm,
) as usize),
Some(Self::envelope_frame_to_gba_frame(
e.point[e.loop_end_point as usize].frame,
bpm,
) as usize),
) )
} else { } else {
(None, None) (None, None)
@ -562,4 +571,14 @@ impl From<&xmrs::envelope::Envelope> for EnvelopeData {
loop_end, loop_end,
} }
} }
fn envelope_frame_to_gba_frame(envelope_frame: u16, bpm: u32) -> u16 {
// FT2 manual says number of ticks / second = BPM * 0.4
// somehow this works as a good approximation :/
(envelope_frame as u32 * 250 / bpm) as u16
}
fn gba_frame_to_envelope_frame(gba_frame: u16, bpm: u32) -> u16 {
(gba_frame as u32 * bpm / 250) as u16
}
} }