Implement F command

This commit is contained in:
Gwilym Inzani 2023-08-04 22:30:49 +01:00
parent 43d7350c2f
commit fa4b2ebd5d
3 changed files with 77 additions and 8 deletions

View file

@ -54,6 +54,8 @@ pub enum PatternEffect {
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>),
SetTicksPerStep(u32),
SetFramesPerTick(Num<u32, 8>),
} }
#[cfg(feature = "quote")] #[cfg(feature = "quote")]
@ -223,6 +225,13 @@ impl quote::ToTokens for PatternEffect {
let target = target.to_raw(); let target = target.to_raw();
quote! { TonePortamento(agb_tracker::__private::Num::from_raw(#amount), agb_tracker::__private::Num::from_raw(#target))} quote! { TonePortamento(agb_tracker::__private::Num::from_raw(#amount), agb_tracker::__private::Num::from_raw(#target))}
} }
PatternEffect::SetTicksPerStep(new_ticks) => {
quote! { SetTicksPerStep(#new_ticks) }
}
PatternEffect::SetFramesPerTick(new_frames_per_tick) => {
let amount = new_frames_per_tick.to_raw();
quote! { SetFramesPerTick(agb_tracker::__private::Num::from_raw(#amount)) }
}
}; };
tokens.append_all(quote! { tokens.append_all(quote! {

View file

@ -95,6 +95,8 @@ pub struct Tracker {
tick: u32, tick: u32,
first: bool, first: bool,
global_settings: GlobalSettings,
current_row: usize, current_row: usize,
current_pattern: usize, current_pattern: usize,
} }
@ -105,6 +107,13 @@ struct TrackerChannel {
volume: Num<i32, 8>, volume: Num<i32, 8>,
} }
#[derive(Clone)]
struct GlobalSettings {
ticks_per_step: u32,
frames_per_tick: Num<u32, 8>,
}
impl Tracker { impl Tracker {
/// Create a new tracker playing a specified track. See the [example](crate#example) for how to use the tracker. /// Create a new tracker playing a specified track. See the [example](crate#example) for how to use the tracker.
pub fn new(track: &'static Track<'static>) -> Self { pub fn new(track: &'static Track<'static>) -> Self {
@ -115,6 +124,11 @@ impl Tracker {
volume: 0.into(), volume: 0.into(),
}); });
let global_settings = GlobalSettings {
ticks_per_step: track.ticks_per_step,
frames_per_tick: track.frames_per_tick,
};
Self { Self {
track, track,
channels, channels,
@ -123,8 +137,10 @@ impl Tracker {
first: true, first: true,
tick: 0, tick: 0,
current_row: 0, global_settings,
current_pattern: 0, current_pattern: 0,
current_row: 0,
} }
} }
@ -153,8 +169,18 @@ impl Tracker {
channel.set_speed(mixer, pattern_slot.speed.change_base()); channel.set_speed(mixer, pattern_slot.speed.change_base());
} }
channel.apply_effect(mixer, &pattern_slot.effect1, self.tick); channel.apply_effect(
channel.apply_effect(mixer, &pattern_slot.effect2, self.tick); mixer,
&pattern_slot.effect1,
self.tick,
&mut self.global_settings,
);
channel.apply_effect(
mixer,
&pattern_slot.effect2,
self.tick,
&mut self.global_settings,
);
} }
self.increment_step(); self.increment_step();
@ -168,11 +194,11 @@ impl Tracker {
self.frame += 1; self.frame += 1;
if self.frame >= self.track.frames_per_tick { if self.frame >= self.global_settings.frames_per_tick {
self.tick += 1; self.tick += 1;
self.frame -= self.track.frames_per_tick; self.frame -= self.global_settings.frames_per_tick;
if self.tick == self.track.ticks_per_step { if self.tick >= self.global_settings.ticks_per_step {
self.current_row += 1; self.current_row += 1;
if self.current_row if self.current_row
@ -236,7 +262,13 @@ impl TrackerChannel {
} }
} }
fn apply_effect(&mut self, mixer: &mut Mixer<'_>, effect: &PatternEffect, tick: u32) { fn apply_effect(
&mut self,
mixer: &mut Mixer<'_>,
effect: &PatternEffect,
tick: u32,
global_settings: &mut GlobalSettings,
) {
if let Some(channel) = self if let Some(channel) = self
.channel_id .channel_id
.as_ref() .as_ref()
@ -298,8 +330,25 @@ impl TrackerChannel {
channel.playback(self.base_speed.change_base()); channel.playback(self.base_speed.change_base());
} }
// These are global effects handled below
PatternEffect::SetTicksPerStep(_) | PatternEffect::SetFramesPerTick(_) => {}
} }
} }
// Some effects have to happen regardless of if we're actually playing anything
match effect {
PatternEffect::SetTicksPerStep(amount) => {
if tick == 0 {
global_settings.ticks_per_step = *amount;
}
}
PatternEffect::SetFramesPerTick(new_frames_per_tick) => {
if tick == 0 {
global_settings.frames_per_tick = *new_frames_per_tick;
}
}
_ => {}
}
} }
} }

View file

@ -323,6 +323,13 @@ pub fn parse_module(module: &Module) -> TokenStream {
0xC => PatternEffect::NoteCut((slot.effect_parameter & 0xf).into()), 0xC => PatternEffect::NoteCut((slot.effect_parameter & 0xf).into()),
_ => PatternEffect::None, _ => PatternEffect::None,
}, },
0xF => {
if slot.effect_parameter < 0x20 {
PatternEffect::SetTicksPerStep(slot.effect_parameter as u32)
} else {
PatternEffect::None
}
}
_ => PatternEffect::None, _ => PatternEffect::None,
}; };
@ -379,7 +386,7 @@ pub fn parse_module(module: &Module) -> TokenStream {
.collect::<Vec<_>>(); .collect::<Vec<_>>();
// Number 150 here deduced experimentally // Number 150 here deduced experimentally
let frames_per_tick = Num::<u32, 8>::new(150) / module.default_bpm as u32; let frames_per_tick = bpm_to_frames_per_tick(module.default_bpm as u32);
let ticks_per_step = module.default_tempo; let ticks_per_step = module.default_tempo;
let interop = agb_tracker_interop::Track { let interop = agb_tracker_interop::Track {
@ -397,6 +404,10 @@ pub fn parse_module(module: &Module) -> TokenStream {
quote!(#interop) quote!(#interop)
} }
fn bpm_to_frames_per_tick(bpm: u32) -> Num<u32, 8> {
Num::<u32, 8>::new(150) / bpm
}
fn note_to_speed( fn note_to_speed(
note: Note, note: Note,
fine_tune: f64, fine_tune: f64,