Actually make arpeggios work

This commit is contained in:
Gwilym Inzani 2023-07-17 00:27:20 +01:00
parent 9b94b2a2cb
commit 203b1a4026
2 changed files with 97 additions and 23 deletions

View file

@ -31,6 +31,7 @@ pub struct Tracker {
frame: Num<u16, 8>, frame: Num<u16, 8>,
tick: u16, tick: u16,
first: bool,
current_row: usize, current_row: usize,
current_pattern: usize, current_pattern: usize,
@ -38,18 +39,23 @@ pub struct Tracker {
struct TrackerChannel { struct TrackerChannel {
channel_id: Option<ChannelId>, channel_id: Option<ChannelId>,
base_speed: Num<u32, 8>,
} }
impl Tracker { impl Tracker {
pub fn new(track: &'static Track<'static>) -> Self { pub fn new(track: &'static Track<'static>) -> Self {
let mut channels = Vec::new(); let mut channels = Vec::new();
channels.resize_with(track.num_channels, || TrackerChannel { channel_id: None }); channels.resize_with(track.num_channels, || TrackerChannel {
channel_id: None,
base_speed: 0.into(),
});
Self { Self {
track, track,
channels, channels,
frame: 0.into(), frame: 0.into(),
first: true,
tick: 0, tick: 0,
current_row: 0, current_row: 0,
@ -58,9 +64,8 @@ impl Tracker {
} }
pub fn step(&mut self, mixer: &mut Mixer) { pub fn step(&mut self, mixer: &mut Mixer) {
if self.tick != 0 { if !self.increment_frame() {
self.increment_step(); return;
return; // TODO: volume / pitch slides
} }
let pattern_to_play = self.track.patterns_to_play[self.current_pattern]; let pattern_to_play = self.track.patterns_to_play[self.current_pattern];
@ -72,7 +77,7 @@ impl Tracker {
&self.track.pattern_data[pattern_data_pos..pattern_data_pos + self.track.num_channels]; &self.track.pattern_data[pattern_data_pos..pattern_data_pos + self.track.num_channels];
for (channel, pattern_slot) in self.channels.iter_mut().zip(pattern_slots) { for (channel, pattern_slot) in self.channels.iter_mut().zip(pattern_slots) {
if pattern_slot.sample != 0 { if pattern_slot.sample != 0 && self.tick == 0 {
let sample = &self.track.samples[pattern_slot.sample - 1]; let sample = &self.track.samples[pattern_slot.sample - 1];
channel.play_sound(mixer, sample); channel.play_sound(mixer, sample);
} }
@ -84,13 +89,17 @@ impl Tracker {
self.increment_step(); self.increment_step();
} }
fn increment_step(&mut self) { fn increment_frame(&mut self) -> bool {
if self.first {
self.first = false;
return true;
}
self.frame += 1; self.frame += 1;
if self.frame >= self.track.frames_per_tick { if self.frame >= self.track.frames_per_tick {
self.tick += 1; self.tick += 1;
self.frame -= self.track.frames_per_tick; self.frame -= self.track.frames_per_tick;
}
if self.tick == self.track.ticks_per_step { if self.tick == self.track.ticks_per_step {
self.current_row += 1; self.current_row += 1;
@ -108,9 +117,16 @@ impl Tracker {
self.tick = 0; self.tick = 0;
} }
true
} else {
false
} }
} }
fn increment_step(&mut self) {}
}
impl TrackerChannel { impl TrackerChannel {
fn play_sound(&mut self, mixer: &mut Mixer<'_>, sample: &Sample<'static>) { fn play_sound(&mut self, mixer: &mut Mixer<'_>, sample: &Sample<'static>) {
self.channel_id self.channel_id
@ -143,6 +159,7 @@ impl TrackerChannel {
{ {
if speed != 0.into() { if speed != 0.into() {
channel.playback(speed); channel.playback(speed);
self.base_speed = speed;
} }
match effect { match effect {
@ -150,7 +167,17 @@ impl TrackerChannel {
PatternEffect::Stop => { PatternEffect::Stop => {
channel.stop(); channel.stop();
} }
PatternEffect::Arpeggio(_, _) => todo!(), PatternEffect::Arpeggio(first, second) => {
let first: Num<u32, 8> = first.change_base();
let second: Num<u32, 8> = second.change_base();
match tick % 3 {
0 => channel.playback(self.base_speed),
1 => channel.playback(self.base_speed + first),
2 => channel.playback(self.base_speed + second),
_ => unreachable!(),
};
}
PatternEffect::Panning(panning) => { PatternEffect::Panning(panning) => {
channel.panning(*panning); channel.panning(*panning);
} }

View file

@ -104,7 +104,11 @@ pub fn parse_module(module: &Module) -> TokenStream {
let start_pos = pattern_data.len(); let start_pos = pattern_data.len();
for row in pattern.iter() { for row in pattern.iter() {
for slot in row { let mut notes = vec![None; module.get_num_channels()];
for (i, slot) in row.iter().enumerate() {
let channel_number = i % module.get_num_channels();
let sample = if slot.instrument == 0 { let sample = if slot.instrument == 0 {
0 0
} else { } else {
@ -133,7 +137,54 @@ pub fn parse_module(module: &Module) -> TokenStream {
_ => PatternEffect::None, _ => PatternEffect::None,
}; };
if matches!(slot.note, Note::KeyOff) {
effect1 = PatternEffect::Stop;
notes[channel_number] = None;
} else {
notes[channel_number] = Some(slot.note);
}
let effect2 = match slot.effect_type { let effect2 = match slot.effect_type {
0x0 => {
if slot.effect_parameter == 0 {
PatternEffect::None
} else if let Some(note) = notes[channel_number] {
let first_arpeggio = slot.effect_parameter >> 4;
let second_arpeggio = slot.effect_parameter & 0xF;
let note_speed = note_to_speed(note, 0.0, 0, module.frequency_type);
let note = note as u8;
let first_arpeggio: Note = (note + first_arpeggio)
.try_into()
.expect("Note out of bounds");
let second_arpeggio: Note = (note + second_arpeggio)
.try_into()
.expect("Note out of bounds");
let first_arpeggio_speed =
note_to_speed(first_arpeggio, 0.0, 0, module.frequency_type);
let second_arpeggio_speed =
note_to_speed(second_arpeggio, 0.0, 0, module.frequency_type);
let first_arpeggio_difference = first_arpeggio_speed - note_speed;
let second_arpeggio_difference = second_arpeggio_speed - note_speed;
let first_arpeggio_difference = first_arpeggio_difference
.try_change_base()
.expect("Arpeggio difference too large");
let second_arpeggio_difference = second_arpeggio_difference
.try_change_base()
.expect("Arpeggio difference too large");
PatternEffect::Arpeggio(
first_arpeggio_difference,
second_arpeggio_difference,
)
} else {
PatternEffect::None
}
}
0x8 => { 0x8 => {
PatternEffect::Panning(Num::new(slot.effect_parameter as i16 - 128) / 128) PatternEffect::Panning(Num::new(slot.effect_parameter as i16 - 128) / 128)
} }
@ -141,10 +192,6 @@ pub fn parse_module(module: &Module) -> TokenStream {
_ => PatternEffect::None, _ => PatternEffect::None,
}; };
if matches!(slot.note, Note::KeyOff) {
effect1 = PatternEffect::Stop;
}
if sample == 0 { if sample == 0 {
pattern_data.push(agb_tracker_interop::PatternSlot { pattern_data.push(agb_tracker_interop::PatternSlot {
speed: 0.into(), speed: 0.into(),