mirror of
https://github.com/italicsjenga/agb.git
synced 2024-12-23 08:11:33 +11:00
Implement F command
This commit is contained in:
parent
43d7350c2f
commit
fa4b2ebd5d
|
@ -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! {
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
|
|
Loading…
Reference in a new issue