mirror of
https://github.com/italicsjenga/agb.git
synced 2024-12-23 08:11:33 +11:00
Update tracker to support PatternBreak and PositionJump
This commit is contained in:
parent
4a3792b248
commit
da92ec3b8b
|
@ -29,6 +29,16 @@ pub struct Sample {
|
||||||
pub fadeout: Num<i32, 8>,
|
pub fadeout: Num<i32, 8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub enum Jump {
|
||||||
|
/// Jump to the given pattern position `pattern` at row index 0
|
||||||
|
Position { pattern: u8 },
|
||||||
|
/// Jump to the next pattern position, at row index `row`
|
||||||
|
PatternBreak { row: u8 },
|
||||||
|
/// Jump to the pattern position `pattern` at row index `row`
|
||||||
|
Combined { pattern: u8, row: u8 },
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Pattern {
|
pub struct Pattern {
|
||||||
pub length: usize,
|
pub length: usize,
|
||||||
|
@ -82,6 +92,7 @@ pub enum PatternEffect {
|
||||||
GlobalVolumeSlide(Num<i32, 8>),
|
GlobalVolumeSlide(Num<i32, 8>),
|
||||||
/// Increase / decrease the pitch by the specified amount immediately
|
/// Increase / decrease the pitch by the specified amount immediately
|
||||||
PitchBend(Num<u32, 8>),
|
PitchBend(Num<u32, 8>),
|
||||||
|
Jump(Jump),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
|
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
@ -92,6 +103,31 @@ pub enum Waveform {
|
||||||
Square,
|
Square,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "quote")]
|
||||||
|
impl quote::ToTokens for Jump {
|
||||||
|
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
|
||||||
|
use quote::{quote, TokenStreamExt};
|
||||||
|
|
||||||
|
let type_bit = match self {
|
||||||
|
Jump::Position { pattern } => {
|
||||||
|
quote! {Position{pattern: #pattern} }
|
||||||
|
}
|
||||||
|
Jump::PatternBreak { row } => {
|
||||||
|
quote! { PatternBreak{row: #row} }
|
||||||
|
}
|
||||||
|
Jump::Combined { pattern, row } => {
|
||||||
|
quote! {
|
||||||
|
Combined{pattern: #pattern, row: #row }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
tokens.append_all(quote! {
|
||||||
|
agb_tracker::__private::agb_tracker_interop::Jump::#type_bit
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "quote")]
|
#[cfg(feature = "quote")]
|
||||||
impl quote::ToTokens for Track {
|
impl quote::ToTokens for Track {
|
||||||
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
|
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
|
||||||
|
@ -286,6 +322,7 @@ impl quote::ToTokens for Pattern {
|
||||||
} = self;
|
} = self;
|
||||||
|
|
||||||
tokens.append_all(quote! {
|
tokens.append_all(quote! {
|
||||||
|
|
||||||
agb_tracker::__private::agb_tracker_interop::Pattern {
|
agb_tracker::__private::agb_tracker_interop::Pattern {
|
||||||
length: #length,
|
length: #length,
|
||||||
start_position: #start_position,
|
start_position: #start_position,
|
||||||
|
@ -361,6 +398,9 @@ impl quote::ToTokens for PatternEffect {
|
||||||
let amount = amount.to_raw();
|
let amount = amount.to_raw();
|
||||||
quote! { Vibrato(#waveform, agb_tracker::__private::Num::from_raw(#amount), #speed) }
|
quote! { Vibrato(#waveform, agb_tracker::__private::Num::from_raw(#amount), #speed) }
|
||||||
}
|
}
|
||||||
|
PatternEffect::Jump(jump) => {
|
||||||
|
quote! { Jump(#jump) }
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
tokens.append_all(quote! {
|
tokens.append_all(quote! {
|
||||||
|
|
|
@ -68,7 +68,7 @@ extern crate alloc;
|
||||||
mod lookups;
|
mod lookups;
|
||||||
mod mixer;
|
mod mixer;
|
||||||
|
|
||||||
use agb_tracker_interop::{PatternEffect, Sample, Waveform};
|
use agb_tracker_interop::{Jump, PatternEffect, Sample, Waveform};
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
|
|
||||||
pub use mixer::{Mixer, SoundChannel};
|
pub use mixer::{Mixer, SoundChannel};
|
||||||
|
@ -111,6 +111,7 @@ pub struct TrackerInner<'track, TChannelId> {
|
||||||
|
|
||||||
current_row: usize,
|
current_row: usize,
|
||||||
current_pattern: usize,
|
current_pattern: usize,
|
||||||
|
current_jump: Option<Jump>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
|
@ -204,6 +205,7 @@ impl<'track, TChannelId> TrackerInner<'track, TChannelId> {
|
||||||
|
|
||||||
current_pattern: 0,
|
current_pattern: 0,
|
||||||
current_row: 0,
|
current_row: 0,
|
||||||
|
current_jump: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -269,12 +271,14 @@ impl<'track, TChannelId> TrackerInner<'track, TChannelId> {
|
||||||
self.tick,
|
self.tick,
|
||||||
&mut self.global_settings,
|
&mut self.global_settings,
|
||||||
&mut self.envelopes[i],
|
&mut self.envelopes[i],
|
||||||
|
&mut self.current_jump,
|
||||||
);
|
);
|
||||||
channel.apply_effect(
|
channel.apply_effect(
|
||||||
&pattern_slot.effect2,
|
&pattern_slot.effect2,
|
||||||
self.tick,
|
self.tick,
|
||||||
&mut self.global_settings,
|
&mut self.global_settings,
|
||||||
&mut self.envelopes[i],
|
&mut self.envelopes[i],
|
||||||
|
&mut self.current_jump,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -373,16 +377,21 @@ impl<'track, TChannelId> TrackerInner<'track, TChannelId> {
|
||||||
self.frame -= self.global_settings.frames_per_tick;
|
self.frame -= self.global_settings.frames_per_tick;
|
||||||
|
|
||||||
if self.tick >= self.global_settings.ticks_per_step {
|
if self.tick >= self.global_settings.ticks_per_step {
|
||||||
self.current_row += 1;
|
if let Some(jump) = self.current_jump.take() {
|
||||||
|
self.handle_jump(jump);
|
||||||
|
} else {
|
||||||
|
self.current_row += 1;
|
||||||
|
|
||||||
if self.current_row
|
if self.current_row
|
||||||
>= self.track.patterns[self.track.patterns_to_play[self.current_pattern]].length
|
>= self.track.patterns[self.track.patterns_to_play[self.current_pattern]]
|
||||||
{
|
.length
|
||||||
self.current_pattern += 1;
|
{
|
||||||
self.current_row = 0;
|
self.current_pattern += 1;
|
||||||
|
self.current_row = 0;
|
||||||
|
|
||||||
if self.current_pattern >= self.track.patterns_to_play.len() {
|
if self.current_pattern >= self.track.patterns_to_play.len() {
|
||||||
self.current_pattern = self.track.repeat;
|
self.current_pattern = self.track.repeat;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -394,6 +403,32 @@ impl<'track, TChannelId> TrackerInner<'track, TChannelId> {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn handle_jump(&mut self, jump: Jump) {
|
||||||
|
match jump {
|
||||||
|
Jump::Position { pattern } => {
|
||||||
|
self.current_pattern = pattern as usize;
|
||||||
|
self.current_row = 0;
|
||||||
|
}
|
||||||
|
Jump::PatternBreak { row } => {
|
||||||
|
self.current_pattern += 1;
|
||||||
|
self.current_row = row as usize;
|
||||||
|
}
|
||||||
|
Jump::Combined { pattern, row } => {
|
||||||
|
self.current_pattern = pattern as usize;
|
||||||
|
self.current_row = row as usize;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if self.current_pattern >= self.track.patterns_to_play.len() {
|
||||||
|
self.current_pattern = self.track.repeat;
|
||||||
|
}
|
||||||
|
if self.current_row
|
||||||
|
>= self.track.patterns[self.track.patterns_to_play[self.current_pattern]].length
|
||||||
|
{
|
||||||
|
// TODO: reconsider this default
|
||||||
|
self.current_row = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TrackerChannel {
|
impl TrackerChannel {
|
||||||
|
@ -419,6 +454,7 @@ impl TrackerChannel {
|
||||||
tick: u32,
|
tick: u32,
|
||||||
global_settings: &mut GlobalSettings,
|
global_settings: &mut GlobalSettings,
|
||||||
envelope_state: &mut Option<EnvelopeState>,
|
envelope_state: &mut Option<EnvelopeState>,
|
||||||
|
current_jump: &mut Option<Jump>,
|
||||||
) {
|
) {
|
||||||
match effect {
|
match effect {
|
||||||
PatternEffect::None => {}
|
PatternEffect::None => {}
|
||||||
|
@ -547,6 +583,9 @@ impl TrackerChannel {
|
||||||
self.vibrato.waveform = *waveform;
|
self.vibrato.waveform = *waveform;
|
||||||
self.vibrato.enable = true;
|
self.vibrato.enable = true;
|
||||||
}
|
}
|
||||||
|
PatternEffect::Jump(jump) => {
|
||||||
|
*current_jump = Some(jump.clone());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use agb_fixnum::Num;
|
use agb_fixnum::Num;
|
||||||
use agb_tracker_interop::{PatternEffect, Waveform};
|
use agb_tracker_interop::{Jump, PatternEffect, Waveform};
|
||||||
|
|
||||||
use xmrs::prelude::*;
|
use xmrs::prelude::*;
|
||||||
|
|
||||||
|
@ -101,6 +101,9 @@ pub fn parse_module(module: &Module) -> agb_tracker_interop::Track {
|
||||||
let mut note_and_sample = vec![None; module.get_num_channels()];
|
let mut note_and_sample = vec![None; module.get_num_channels()];
|
||||||
|
|
||||||
for row in pattern.iter() {
|
for row in pattern.iter() {
|
||||||
|
// the combined jump for each row
|
||||||
|
let mut jump = None;
|
||||||
|
|
||||||
for (i, slot) in row.iter().enumerate() {
|
for (i, slot) in row.iter().enumerate() {
|
||||||
let channel_number = i % module.get_num_channels();
|
let channel_number = i % module.get_num_channels();
|
||||||
|
|
||||||
|
@ -319,6 +322,18 @@ pub fn parse_module(module: &Module) -> agb_tracker_interop::Track {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
0xB => {
|
||||||
|
let pattern_idx = slot.effect_parameter;
|
||||||
|
|
||||||
|
jump = Some((
|
||||||
|
channel_number,
|
||||||
|
Jump::Position {
|
||||||
|
pattern: pattern_idx,
|
||||||
|
},
|
||||||
|
));
|
||||||
|
|
||||||
|
PatternEffect::None
|
||||||
|
}
|
||||||
0xC => {
|
0xC => {
|
||||||
if let Some((_, sample)) = maybe_note_and_sample {
|
if let Some((_, sample)) = maybe_note_and_sample {
|
||||||
PatternEffect::Volume(
|
PatternEffect::Volume(
|
||||||
|
@ -328,6 +343,29 @@ pub fn parse_module(module: &Module) -> agb_tracker_interop::Track {
|
||||||
PatternEffect::None
|
PatternEffect::None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
0xD => {
|
||||||
|
// NOTE: this field is generally interpreted as decimal.
|
||||||
|
let first = slot.effect_parameter >> 4;
|
||||||
|
let second = slot.effect_parameter & 0xF;
|
||||||
|
let row_idx = first * 10 + second;
|
||||||
|
|
||||||
|
let pattern_break = Jump::PatternBreak { row: row_idx };
|
||||||
|
|
||||||
|
// if to the *right* of 0xD effect, make combined
|
||||||
|
if let Some((idx, Jump::Position { pattern })) = jump {
|
||||||
|
jump = Some((
|
||||||
|
idx,
|
||||||
|
Jump::Combined {
|
||||||
|
pattern,
|
||||||
|
row: row_idx,
|
||||||
|
},
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
jump = Some((channel_number, pattern_break));
|
||||||
|
}
|
||||||
|
|
||||||
|
PatternEffect::None
|
||||||
|
}
|
||||||
0xE => match slot.effect_parameter >> 4 {
|
0xE => match slot.effect_parameter >> 4 {
|
||||||
0x1 => {
|
0x1 => {
|
||||||
let c4_speed: Num<u32, 12> =
|
let c4_speed: Num<u32, 12> =
|
||||||
|
@ -433,6 +471,16 @@ pub fn parse_module(module: &Module) -> agb_tracker_interop::Track {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// At the last channel, evaluate the combined jump,
|
||||||
|
// and place at the first jump effect channel index
|
||||||
|
if let Some((jump_channel, jump)) = jump.take() {
|
||||||
|
let jump_effect = PatternEffect::Jump(jump);
|
||||||
|
let pattern_data_idx =
|
||||||
|
pattern_data.len() - module.get_num_channels() + jump_channel;
|
||||||
|
if let Some(data) = pattern_data.get_mut(pattern_data_idx) {
|
||||||
|
data.effect2 = jump_effect;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
patterns.push(agb_tracker_interop::Pattern {
|
patterns.push(agb_tracker_interop::Pattern {
|
||||||
|
|
Loading…
Reference in a new issue