agb/tracker/agb-tracker/src/lib.rs

215 lines
5.7 KiB
Rust
Raw Normal View History

2023-07-12 21:10:05 +10:00
#![no_std]
#![no_main]
// This is required to allow writing tests
#![cfg_attr(test, feature(custom_test_frameworks))]
#![cfg_attr(test, reexport_test_harness_main = "test_main")]
#![cfg_attr(test, test_runner(agb::test_runner::test_runner))]
2023-07-13 02:36:41 +10:00
extern crate alloc;
2023-07-17 08:57:11 +10:00
use agb_tracker_interop::{PatternEffect, Sample};
2023-07-13 03:52:29 +10:00
use alloc::vec::Vec;
2023-07-13 02:36:41 +10:00
use agb::{
fixnum::Num,
sound::mixer::{ChannelId, Mixer, SoundChannel},
};
2023-07-13 02:36:41 +10:00
2023-07-13 00:38:09 +10:00
#[cfg(feature = "xm")]
pub use agb_xm::import_xm;
pub mod __private {
pub use agb::fixnum::Num;
pub use agb_tracker_interop;
}
2023-07-13 00:38:09 +10:00
pub use agb_tracker_interop::Track;
2023-07-13 00:38:09 +10:00
2023-07-13 02:36:41 +10:00
pub struct Tracker {
track: &'static Track<'static>,
2023-07-17 08:57:11 +10:00
channels: Vec<TrackerChannel>,
2023-07-13 02:36:41 +10:00
frame: Num<u16, 8>,
tick: u16,
2023-07-17 09:27:20 +10:00
first: bool,
2023-07-13 02:36:41 +10:00
current_row: usize,
current_pattern: usize,
}
2023-07-17 08:57:11 +10:00
struct TrackerChannel {
channel_id: Option<ChannelId>,
2023-07-17 09:27:20 +10:00
base_speed: Num<u32, 8>,
2023-07-17 09:45:58 +10:00
volume: Num<i16, 4>,
2023-07-17 08:57:11 +10:00
}
2023-07-13 02:36:41 +10:00
impl Tracker {
pub fn new(track: &'static Track<'static>) -> Self {
2023-07-13 03:52:29 +10:00
let mut channels = Vec::new();
2023-07-17 09:27:20 +10:00
channels.resize_with(track.num_channels, || TrackerChannel {
channel_id: None,
base_speed: 0.into(),
2023-07-17 09:45:58 +10:00
volume: 0.into(),
2023-07-17 09:27:20 +10:00
});
2023-07-13 02:36:41 +10:00
Self {
track,
2023-07-13 03:52:29 +10:00
channels,
2023-07-13 02:36:41 +10:00
frame: 0.into(),
2023-07-17 09:27:20 +10:00
first: true,
tick: 0,
2023-07-13 02:36:41 +10:00
current_row: 0,
current_pattern: 0,
}
}
pub fn step(&mut self, mixer: &mut Mixer) {
2023-07-17 09:27:20 +10:00
if !self.increment_frame() {
return;
2023-07-13 02:36:41 +10:00
}
2023-07-13 04:06:55 +10:00
let pattern_to_play = self.track.patterns_to_play[self.current_pattern];
let current_pattern = &self.track.patterns[pattern_to_play];
2023-07-13 02:36:41 +10:00
2023-07-13 03:52:29 +10:00
let pattern_data_pos =
current_pattern.start_position + self.current_row * self.track.num_channels;
2023-07-13 02:36:41 +10:00
let pattern_slots =
2023-07-13 03:52:29 +10:00
&self.track.pattern_data[pattern_data_pos..pattern_data_pos + self.track.num_channels];
2023-07-13 02:36:41 +10:00
2023-07-17 08:57:11 +10:00
for (channel, pattern_slot) in self.channels.iter_mut().zip(pattern_slots) {
2023-07-17 09:27:20 +10:00
if pattern_slot.sample != 0 && self.tick == 0 {
let sample = &self.track.samples[pattern_slot.sample as usize - 1];
2023-07-17 08:57:11 +10:00
channel.play_sound(mixer, sample);
2023-07-13 02:36:41 +10:00
}
2023-07-17 08:57:11 +10:00
channel.apply_effect(
mixer,
&pattern_slot.effect1,
self.tick,
pattern_slot.speed.change_base(),
);
channel.apply_effect(
mixer,
&pattern_slot.effect2,
self.tick,
pattern_slot.speed.change_base(),
);
2023-07-13 02:36:41 +10:00
}
self.increment_step();
}
2023-07-17 09:27:20 +10:00
fn increment_frame(&mut self) -> bool {
if self.first {
self.first = false;
return true;
}
self.frame += 1;
if self.frame >= self.track.frames_per_tick {
self.tick += 1;
self.frame -= self.track.frames_per_tick;
2023-07-13 02:36:41 +10:00
2023-07-17 09:27:20 +10:00
if self.tick == self.track.ticks_per_step {
self.current_row += 1;
2023-07-13 02:36:41 +10:00
2023-07-17 09:27:20 +10:00
if self.current_row
>= self.track.patterns[self.track.patterns_to_play[self.current_pattern]].length
{
self.current_pattern += 1;
self.current_row = 0;
2023-07-13 04:06:55 +10:00
2023-07-17 09:27:20 +10:00
if self.current_pattern >= self.track.patterns_to_play.len() {
self.current_pattern = 0;
}
2023-07-13 04:06:55 +10:00
}
2023-07-17 09:27:20 +10:00
self.tick = 0;
2023-07-13 02:36:41 +10:00
}
2023-07-17 09:27:20 +10:00
true
} else {
false
2023-07-13 02:36:41 +10:00
}
2023-07-12 21:10:05 +10:00
}
2023-07-17 09:27:20 +10:00
fn increment_step(&mut self) {}
2023-07-12 21:10:05 +10:00
}
2023-07-17 08:57:11 +10:00
impl TrackerChannel {
fn play_sound(&mut self, mixer: &mut Mixer<'_>, sample: &Sample<'static>) {
self.channel_id
.take()
.and_then(|channel_id| mixer.channel(&channel_id))
.map(|channel| channel.stop());
let mut new_channel = SoundChannel::new(sample.data);
if sample.should_loop {
new_channel
.should_loop()
.restart_point(sample.restart_point);
}
self.channel_id = mixer.play_sound(new_channel);
self.volume = 1.into();
2023-07-17 08:57:11 +10:00
}
fn apply_effect(
&mut self,
mixer: &mut Mixer<'_>,
effect: &PatternEffect,
tick: u16,
speed: Num<u32, 8>,
) {
if let Some(channel) = self
.channel_id
.as_ref()
.and_then(|channel_id| mixer.channel(&channel_id))
{
if speed != 0.into() {
channel.playback(speed);
2023-07-17 09:27:20 +10:00
self.base_speed = speed;
2023-07-17 08:57:11 +10:00
}
match effect {
PatternEffect::None => {}
PatternEffect::Stop => {
channel.stop();
}
2023-07-17 09:27:20 +10:00
PatternEffect::Arpeggio(first, second) => {
match tick % 3 {
0 => channel.playback(self.base_speed),
1 => channel.playback(self.base_speed + first.change_base()),
2 => channel.playback(self.base_speed + second.change_base()),
2023-07-17 09:27:20 +10:00
_ => unreachable!(),
};
}
2023-07-17 08:57:11 +10:00
PatternEffect::Panning(panning) => {
channel.panning(*panning);
}
PatternEffect::Volume(volume) => {
channel.volume(*volume);
2023-07-17 09:45:58 +10:00
self.volume = *volume;
}
PatternEffect::VolumeSlide(amount) => {
self.volume += *amount;
if self.volume < 0.into() {
self.volume = 0.into();
}
channel.volume(self.volume);
2023-07-17 08:57:11 +10:00
}
}
}
}
}
2023-07-12 21:10:05 +10:00
#[cfg(test)]
#[agb::entry]
fn main(gba: agb::Gba) -> ! {
loop {}
}