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 use agb_tracker_interop as __private;
|
|
|
|
|
|
|
|
pub use __private::Track;
|
|
|
|
|
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 {}
|
|
|
|
}
|