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>),
/// Slide each tick the first amount to at most the second amount
TonePortamento(Num<u16, 12>, Num<u16, 12>),
SetTicksPerStep(u32),
SetFramesPerTick(Num<u32, 8>),
}
#[cfg(feature = "quote")]
@ -223,6 +225,13 @@ impl quote::ToTokens for PatternEffect {
let target = target.to_raw();
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! {

View file

@ -95,6 +95,8 @@ pub struct Tracker {
tick: u32,
first: bool,
global_settings: GlobalSettings,
current_row: usize,
current_pattern: usize,
}
@ -105,6 +107,13 @@ struct TrackerChannel {
volume: Num<i32, 8>,
}
#[derive(Clone)]
struct GlobalSettings {
ticks_per_step: u32,
frames_per_tick: Num<u32, 8>,
}
impl 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 {
@ -115,6 +124,11 @@ impl Tracker {
volume: 0.into(),
});
let global_settings = GlobalSettings {
ticks_per_step: track.ticks_per_step,
frames_per_tick: track.frames_per_tick,
};
Self {
track,
channels,
@ -123,8 +137,10 @@ impl Tracker {
first: true,
tick: 0,
current_row: 0,
global_settings,
current_pattern: 0,
current_row: 0,
}
}
@ -153,8 +169,18 @@ impl Tracker {
channel.set_speed(mixer, pattern_slot.speed.change_base());
}
channel.apply_effect(mixer, &pattern_slot.effect1, self.tick);
channel.apply_effect(mixer, &pattern_slot.effect2, self.tick);
channel.apply_effect(
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();
@ -168,11 +194,11 @@ impl Tracker {
self.frame += 1;
if self.frame >= self.track.frames_per_tick {
if self.frame >= self.global_settings.frames_per_tick {
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;
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
.channel_id
.as_ref()
@ -298,8 +330,25 @@ impl TrackerChannel {
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()),
_ => PatternEffect::None,
},
0xF => {
if slot.effect_parameter < 0x20 {
PatternEffect::SetTicksPerStep(slot.effect_parameter as u32)
} else {
PatternEffect::None
}
}
_ => PatternEffect::None,
};
@ -379,7 +386,7 @@ pub fn parse_module(module: &Module) -> TokenStream {
.collect::<Vec<_>>();
// 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 interop = agb_tracker_interop::Track {
@ -397,6 +404,10 @@ pub fn parse_module(module: &Module) -> TokenStream {
quote!(#interop)
}
fn bpm_to_frames_per_tick(bpm: u32) -> Num<u32, 8> {
Num::<u32, 8>::new(150) / bpm
}
fn note_to_speed(
note: Note,
fine_tune: f64,