From 3305ca0ff2dbbc83e341af4f5875dc2429271dcd Mon Sep 17 00:00:00 2001 From: Gwilym Inzani Date: Wed, 5 Jun 2024 10:15:31 +0100 Subject: [PATCH] Make the tracker generic on the mixer --- tracker/agb-tracker/src/lib.rs | 103 ++++++++++++++++++++++++------- tracker/agb-tracker/src/mixer.rs | 25 ++++++++ 2 files changed, 105 insertions(+), 23 deletions(-) create mode 100644 tracker/agb-tracker/src/mixer.rs diff --git a/tracker/agb-tracker/src/lib.rs b/tracker/agb-tracker/src/lib.rs index 0ef3a4cf..f389109b 100644 --- a/tracker/agb-tracker/src/lib.rs +++ b/tracker/agb-tracker/src/lib.rs @@ -65,13 +65,14 @@ extern crate alloc; +mod mixer; + use agb_tracker_interop::{PatternEffect, Sample}; use alloc::vec::Vec; -use agb::{ - fixnum::Num, - sound::mixer::{ChannelId, Mixer, SoundChannel}, -}; +pub use mixer::{Mixer, SoundChannel}; + +use agb::fixnum::Num; /// Import an XM file. Only available if you have the `xm` feature enabled (enabled by default). #[cfg(feature = "xm")] @@ -94,9 +95,9 @@ pub mod __private { pub use agb_tracker_interop::Track; /// Stores the required state in order to play tracker music. -pub struct Tracker { +pub struct Tracker { track: &'static Track, - channels: Vec, + channels: Vec>, envelopes: Vec>, frame: Num, @@ -109,9 +110,8 @@ pub struct Tracker { current_pattern: usize, } -#[derive(Default)] -struct TrackerChannel { - channel_id: Option, +struct TrackerChannel { + channel_id: Option, original_speed: Num, base_speed: Num, volume: Num, @@ -132,7 +132,7 @@ struct GlobalSettings { volume: Num, } -impl Tracker { +impl Tracker { /// Create a new tracker playing a specified track. See the [example](crate#example) for how to use the tracker. pub fn new(track: &'static Track) -> Self { let mut channels = Vec::new(); @@ -165,7 +165,7 @@ impl Tracker { /// Call this once per frame before calling [`mixer.frame`](agb::sound::mixer::Mixer::frame()). /// See the [example](crate#example) for how to use the tracker. - pub fn step(&mut self, mixer: &mut Mixer) { + pub fn step(&mut self, mixer: &mut M) { if !self.increment_frame() { self.update_envelopes(mixer); return; @@ -215,7 +215,7 @@ impl Tracker { self.update_envelopes(mixer); } - fn update_envelopes(&mut self, mixer: &mut Mixer) { + fn update_envelopes(&mut self, mixer: &mut M) { for (channel, envelope_state_option) in self.channels.iter_mut().zip(&mut self.envelopes) { if let Some(envelope_state) = envelope_state_option { let envelope = &self.track.envelopes[envelope_state.envelope_id]; @@ -288,13 +288,8 @@ impl Tracker { } } -impl TrackerChannel { - fn play_sound( - &mut self, - mixer: &mut Mixer<'_>, - sample: &Sample, - global_settings: &GlobalSettings, - ) { +impl TrackerChannel { + fn play_sound(&mut self, mixer: &mut M, sample: &Sample, global_settings: &GlobalSettings) { if let Some(channel) = self .channel_id .take() @@ -303,7 +298,7 @@ impl TrackerChannel { channel.stop(); } - let mut new_channel = SoundChannel::new(match sample.data { + let mut new_channel = M::SoundChannel::new(match sample.data { alloc::borrow::Cow::Borrowed(data) => data, alloc::borrow::Cow::Owned(_) => { unimplemented!("Must use borrowed COW data for tracker") @@ -326,7 +321,7 @@ impl TrackerChannel { self.volume = sample.volume.change_base(); } - fn set_speed(&mut self, mixer: &mut Mixer<'_>, speed: Num) { + fn set_speed(&mut self, mixer: &mut M, speed: Num) { if let Some(channel) = self .channel_id .as_ref() @@ -343,7 +338,7 @@ impl TrackerChannel { fn apply_effect( &mut self, - mixer: &mut Mixer<'_>, + mixer: &mut M, effect: &PatternEffect, tick: u32, global_settings: &mut GlobalSettings, @@ -485,7 +480,7 @@ impl TrackerChannel { #[must_use] fn update_volume_envelope( &mut self, - mixer: &mut Mixer<'_>, + mixer: &mut M, envelope_state: &EnvelopeState, envelope: &agb_tracker_interop::Envelope, global_settings: &GlobalSettings, @@ -514,8 +509,70 @@ impl TrackerChannel { } } +impl Default for TrackerChannel { + fn default() -> Self { + Self { + channel_id: None, + original_speed: Num::default(), + base_speed: Num::default(), + volume: Num::default(), + } + } +} + #[cfg(test)] #[agb::entry] fn main(gba: agb::Gba) -> ! { loop {} } + +impl SoundChannel for agb::sound::mixer::SoundChannel { + fn new(data: &'static [u8]) -> Self { + Self::new(data) + } + + fn stop(&mut self) { + self.stop(); + } + + fn pause(&mut self) -> &mut Self { + self.pause() + } + + fn resume(&mut self) -> &mut Self { + self.resume() + } + + fn should_loop(&mut self) -> &mut Self { + self.should_loop() + } + + fn volume(&mut self, value: impl Into>) -> &mut Self { + self.volume(value) + } + + fn restart_point(&mut self, value: impl Into>) -> &mut Self { + self.restart_point(value) + } + + fn playback(&mut self, playback_speed: impl Into>) -> &mut Self { + self.playback(playback_speed) + } + + fn panning(&mut self, panning: impl Into>) -> &mut Self { + self.panning(panning) + } +} + +impl<'gba> Mixer for agb::sound::mixer::Mixer<'gba> { + type ChannelId = agb::sound::mixer::ChannelId; + type SoundChannel = agb::sound::mixer::SoundChannel; + + fn channel(&mut self, channel_id: &Self::ChannelId) -> Option<&mut Self::SoundChannel> { + self.channel(channel_id) + } + + fn play_sound(&mut self, channel: Self::SoundChannel) -> Option { + self.play_sound(channel) + } +} diff --git a/tracker/agb-tracker/src/mixer.rs b/tracker/agb-tracker/src/mixer.rs new file mode 100644 index 00000000..18860f0a --- /dev/null +++ b/tracker/agb-tracker/src/mixer.rs @@ -0,0 +1,25 @@ +#![allow(missing_docs)] + +use agb::fixnum::Num; + +pub trait SoundChannel { + fn new(data: &'static [u8]) -> Self; + + fn stop(&mut self); + fn pause(&mut self) -> &mut Self; + fn resume(&mut self) -> &mut Self; + + fn should_loop(&mut self) -> &mut Self; + fn volume(&mut self, value: impl Into>) -> &mut Self; + fn restart_point(&mut self, value: impl Into>) -> &mut Self; + fn playback(&mut self, playback_speed: impl Into>) -> &mut Self; + fn panning(&mut self, panning: impl Into>) -> &mut Self; +} + +pub trait Mixer { + type ChannelId; + type SoundChannel: SoundChannel; + + fn channel(&mut self, channel_id: &Self::ChannelId) -> Option<&mut Self::SoundChannel>; + fn play_sound(&mut self, channel: Self::SoundChannel) -> Option; +}