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

142 lines
4.1 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-13 03:52:29 +10:00
use alloc::vec::Vec;
2023-07-13 02:36:41 +10:00
use agb::sound::mixer::{ChannelId, Mixer, SoundChannel};
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>,
channels: Vec<Option<ChannelId>>,
step: u16,
current_row: usize,
current_pattern: usize,
}
impl Tracker {
pub fn new(track: &'static Track<'static>) -> Self {
2023-07-13 03:52:29 +10:00
let mut channels = Vec::new();
channels.resize_with(track.num_channels, || None);
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
step: 0,
current_row: 0,
current_pattern: 0,
}
}
pub fn step(&mut self, mixer: &mut Mixer) {
if self.step != 0 {
self.increment_step();
return; // TODO: volume / pitch slides
}
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
for (channel_id, pattern_slot) in self.channels.iter_mut().zip(pattern_slots) {
2023-07-13 08:41:30 +10:00
if pattern_slot.sample == agb_tracker_interop::SKIP_SLOT {
// completely skip
} else if pattern_slot.sample == agb_tracker_interop::STOP_CHANNEL {
if let Some(channel) = channel_id
.take()
.and_then(|channel_id| mixer.channel(&channel_id))
{
channel.stop();
}
} else if pattern_slot.sample == 0 {
if let Some(channel) = channel_id
.as_ref()
.and_then(|channel_id| mixer.channel(channel_id))
{
if pattern_slot.volume != 0.into() {
channel.volume(pattern_slot.volume);
}
if pattern_slot.panning != 0.into() {
channel.panning(pattern_slot.panning);
}
if pattern_slot.speed != 0.into() {
channel.playback(pattern_slot.speed);
2023-07-13 03:52:29 +10:00
}
}
2023-07-13 02:36:41 +10:00
} else {
if let Some(channel) = channel_id
.take()
.and_then(|channel_id| mixer.channel(&channel_id))
{
channel.stop();
}
let sample = &self.track.samples[pattern_slot.sample - 1];
let mut new_channel = SoundChannel::new(sample.data);
new_channel
.panning(pattern_slot.panning)
.volume(pattern_slot.volume)
.playback(pattern_slot.speed);
if sample.should_loop {
new_channel.should_loop();
}
*channel_id = mixer.play_sound(new_channel);
}
}
self.increment_step();
}
fn increment_step(&mut self) {
self.step += 1;
2023-07-13 04:06:55 +10:00
if self.step == self.track.frames_per_step {
2023-07-13 02:36:41 +10:00
self.current_row += 1;
2023-07-13 04:06:55 +10:00
if self.current_row
>= self.track.patterns[self.track.patterns_to_play[self.current_pattern]].length
{
2023-07-13 02:36:41 +10:00
self.current_pattern += 1;
self.current_row = 0;
2023-07-13 04:06:55 +10:00
if self.current_pattern >= self.track.patterns_to_play.len() {
self.current_pattern = 0;
}
2023-07-13 02:36:41 +10:00
}
self.step = 0;
}
2023-07-12 21:10:05 +10:00
}
}
#[cfg(test)]
#[agb::entry]
fn main(gba: agb::Gba) -> ! {
loop {}
}