diff --git a/tracker/agb-tracker-interop/src/lib.rs b/tracker/agb-tracker-interop/src/lib.rs index 82568329..91dc6125 100644 --- a/tracker/agb-tracker-interop/src/lib.rs +++ b/tracker/agb-tracker-interop/src/lib.rs @@ -80,7 +80,6 @@ pub enum PatternEffect { #[derive(Debug, Default, Clone, PartialEq, Eq)] pub enum Waveform { #[default] - None, Sine, Saw, Square, @@ -349,7 +348,6 @@ impl quote::ToTokens for Waveform { use quote::{quote, TokenStreamExt}; let name = match self { - Waveform::None => quote!(None), Waveform::Sine => quote!(Sine), Waveform::Saw => quote!(Saw), Waveform::Square => quote!(Square), diff --git a/tracker/agb-tracker/src/lib.rs b/tracker/agb-tracker/src/lib.rs index 53295c69..fb524b4d 100644 --- a/tracker/agb-tracker/src/lib.rs +++ b/tracker/agb-tracker/src/lib.rs @@ -98,9 +98,11 @@ pub use agb_tracker_interop::Track; /// Stores the required state in order to play tracker music. pub struct TrackerInner<'track, TChannelId> { track: &'track Track, - channels: Vec>, + channels: Vec, envelopes: Vec>, + mixer_channels: Vec>, + frame: Num, tick: u32, first: bool, @@ -111,11 +113,16 @@ pub struct TrackerInner<'track, TChannelId> { current_pattern: usize, } -struct TrackerChannel { - channel_id: Option, +#[derive(Default)] +struct TrackerChannel { original_speed: Num, base_speed: Num, volume: Num, + + current_volume: Num, + current_speed: Num, + current_panning: Num, + is_playing: bool, } struct EnvelopeState { @@ -142,6 +149,9 @@ impl<'track, TChannelId> TrackerInner<'track, TChannelId> { let mut envelopes = Vec::new(); envelopes.resize_with(track.num_channels, || None); + let mut mixer_channels = Vec::new(); + mixer_channels.resize_with(track.num_channels, || None); + let global_settings = GlobalSettings { ticks_per_step: track.ticks_per_step, frames_per_tick: track.frames_per_tick, @@ -150,6 +160,7 @@ impl<'track, TChannelId> TrackerInner<'track, TChannelId> { Self { track, + mixer_channels, channels, envelopes, @@ -168,7 +179,9 @@ impl<'track, TChannelId> TrackerInner<'track, TChannelId> { /// See the [example](crate#example) for how to use the tracker. pub fn step>(&mut self, mixer: &mut M) { if !self.increment_frame() { - self.update_envelopes(mixer); + self.update_envelopes(); + + self.realise(mixer); return; } @@ -184,7 +197,25 @@ impl<'track, TChannelId> TrackerInner<'track, TChannelId> { { if pattern_slot.sample != 0 && self.tick == 0 { let sample = &self.track.samples[pattern_slot.sample as usize - 1]; - channel.play_sound(mixer, sample, &self.global_settings); + + if let Some(channel) = self.mixer_channels[i] + .take() + .and_then(|channel_id| mixer.channel(&channel_id)) + { + channel.stop(); + } + + let mut new_channel = M::SoundChannel::new(&sample.data); + if sample.should_loop { + new_channel + .should_loop() + .restart_point(sample.restart_point); + } + + self.mixer_channels[i] = mixer.play_sound(new_channel); + + channel.reset(sample); + self.envelopes[i] = sample.volume_envelope.map(|envelope_id| EnvelopeState { frame: 0, envelope_id, @@ -194,18 +225,16 @@ impl<'track, TChannelId> TrackerInner<'track, TChannelId> { } if self.tick == 0 { - channel.set_speed(mixer, pattern_slot.speed.change_base()); + channel.set_speed(pattern_slot.speed.change_base()); } channel.apply_effect( - mixer, &pattern_slot.effect1, self.tick, &mut self.global_settings, &mut self.envelopes[i], ); channel.apply_effect( - mixer, &pattern_slot.effect2, self.tick, &mut self.global_settings, @@ -213,20 +242,36 @@ impl<'track, TChannelId> TrackerInner<'track, TChannelId> { ); } - self.update_envelopes(mixer); + self.update_envelopes(); + self.realise(mixer); } - fn update_envelopes>(&mut self, mixer: &mut M) { + fn realise>(&mut self, mixer: &mut M) { + for (mixer_channel, tracker_channel) in self.mixer_channels.iter().zip(&self.channels) { + if let Some(channel) = mixer_channel + .as_ref() + .and_then(|channel_id| mixer.channel(channel_id)) + { + channel.playback(tracker_channel.current_speed.change_base()); + channel.volume(tracker_channel.current_volume.try_change_base().unwrap()); + channel.panning(tracker_channel.current_panning.try_change_base().unwrap()); + + if tracker_channel.is_playing { + channel.resume(); + } else { + channel.pause(); + } + } + } + } + + fn update_envelopes(&mut self) { 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]; - if !channel.update_volume_envelope( - mixer, - envelope_state, - envelope, - &self.global_settings, - ) { + if !channel.update_volume_envelope(envelope_state, envelope, &self.global_settings) + { envelope_state_option.take(); } else { envelope_state.frame += 1; @@ -289,178 +334,124 @@ impl<'track, TChannelId> TrackerInner<'track, TChannelId> { } } -impl TrackerChannel { - fn play_sound>( - &mut self, - mixer: &mut M, - sample: &Sample, - global_settings: &GlobalSettings, - ) { - if let Some(channel) = self - .channel_id - .take() - .and_then(|channel_id| mixer.channel(&channel_id)) - { - channel.stop(); - } - - let mut new_channel = M::SoundChannel::new(&sample.data); - - new_channel.volume( - (sample.volume.change_base() * global_settings.volume) - .try_change_base() - .unwrap(), - ); - - if sample.should_loop { - new_channel - .should_loop() - .restart_point(sample.restart_point); - } - - self.channel_id = mixer.play_sound(new_channel); +impl TrackerChannel { + fn reset(&mut self, sample: &Sample) { self.volume = sample.volume.change_base(); + self.current_volume = self.volume; + self.current_panning = 0.into(); + self.is_playing = true; } - fn set_speed>(&mut self, mixer: &mut M, speed: Num) { - if let Some(channel) = self - .channel_id - .as_ref() - .and_then(|channel_id| mixer.channel(channel_id)) - { - if speed != 0.into() { - self.base_speed = speed.change_base(); - self.original_speed = self.base_speed; - } - - channel.playback(self.base_speed.change_base()); + fn set_speed(&mut self, speed: Num) { + if speed != 0.into() { + self.base_speed = speed.change_base(); + self.original_speed = self.base_speed; } + + self.current_speed = self.base_speed; } - fn apply_effect>( + fn apply_effect( &mut self, - mixer: &mut M, effect: &PatternEffect, tick: u32, global_settings: &mut GlobalSettings, envelope_state: &mut Option, ) { - if let Some(channel) = self - .channel_id - .as_ref() - .and_then(|channel_id| mixer.channel(channel_id)) - { - match effect { - PatternEffect::None => {} - PatternEffect::Stop => { - channel.volume(0); + match effect { + PatternEffect::None => {} + PatternEffect::Stop => { + self.current_volume = 0.into(); + + if let Some(envelope_state) = envelope_state { + envelope_state.finished = true; + } + } + PatternEffect::Arpeggio(first, second) => { + match tick % 3 { + 0 => self.current_speed = self.base_speed.change_base(), + 1 => self.current_speed = first.change_base(), + 2 => self.current_speed = second.change_base(), + _ => unreachable!(), + }; + } + PatternEffect::Panning(panning) => { + self.current_panning = panning.change_base(); + } + PatternEffect::Volume(volume) => { + self.current_volume = (volume.change_base() * global_settings.volume) + .try_change_base() + .unwrap(); + + self.volume = volume.change_base(); + } + PatternEffect::VolumeSlide(amount) => { + if tick != 0 { + self.volume = (self.volume + amount.change_base()).max(0.into()); + self.current_volume = (self.volume * global_settings.volume) + .try_change_base() + .unwrap(); + } + } + PatternEffect::FineVolumeSlide(amount) => { + if tick == 0 { + self.volume = (self.volume + amount.change_base()).max(0.into()); + self.current_volume = (self.volume * global_settings.volume) + .try_change_base() + .unwrap(); + } + } + PatternEffect::NoteCut(wait) => { + if tick == *wait { + self.current_volume = 0.into(); + if let Some(envelope_state) = envelope_state { envelope_state.finished = true; } } - PatternEffect::Arpeggio(first, second) => { - match tick % 3 { - 0 => channel.playback(self.base_speed.change_base()), - 1 => channel.playback(first.change_base()), - 2 => channel.playback(second.change_base()), - _ => unreachable!(), - }; - } - PatternEffect::Panning(panning) => { - channel.panning(panning.change_base()); - } - PatternEffect::Volume(volume) => { - channel.volume( - (volume.change_base() * global_settings.volume) - .try_change_base() - .unwrap(), - ); - self.volume = volume.change_base(); - } - PatternEffect::VolumeSlide(amount) => { - if tick != 0 { - self.volume = (self.volume + amount.change_base()).max(0.into()); - channel.volume( - (self.volume * global_settings.volume) - .try_change_base() - .unwrap(), - ); - } - } - PatternEffect::FineVolumeSlide(amount) => { - if tick == 0 { - self.volume = (self.volume + amount.change_base()).max(0.into()); - channel.volume( - (self.volume * global_settings.volume) - .try_change_base() - .unwrap(), - ); - } - } - PatternEffect::NoteCut(wait) => { - if tick == *wait { - channel.volume(0); - - if let Some(envelope_state) = envelope_state { - envelope_state.finished = true; - } - } - } - PatternEffect::NoteDelay(wait) => { - if tick < *wait { - channel.pause(); - } - - if tick == *wait { - channel.resume(); - channel.volume( - (self.volume * global_settings.volume) - .try_change_base() - .unwrap(), - ); - } - } - PatternEffect::Portamento(amount) => { - if tick != 0 { - self.base_speed *= amount.change_base(); - channel.playback(self.base_speed.change_base()); - } - } - PatternEffect::TonePortamento(amount, target) => { - channel.volume( - (self.volume * global_settings.volume) - .try_change_base() - .unwrap(), - ); - - if tick != 0 { - if *amount < 1.into() { - self.base_speed = - (self.base_speed * amount.change_base()).max(target.change_base()); - } else { - self.base_speed = - (self.base_speed * amount.change_base()).min(target.change_base()); - } - } - - channel.playback(self.base_speed.change_base()); - } - PatternEffect::PitchBend(amount) => { - if tick == 0 { - self.base_speed = self.original_speed * amount.change_base(); - channel.playback(self.base_speed.change_base()); - } - } - // These are global effects handled below - PatternEffect::SetTicksPerStep(_) - | PatternEffect::SetFramesPerTick(_) - | PatternEffect::SetGlobalVolume(_) - | PatternEffect::GlobalVolumeSlide(_) => {} } - } + PatternEffect::NoteDelay(wait) => { + if tick < *wait { + self.is_playing = false; + } + + if tick == *wait { + self.is_playing = true; + self.current_volume = (self.volume * global_settings.volume) + .try_change_base() + .unwrap(); + } + } + PatternEffect::Portamento(amount) => { + if tick != 0 { + self.base_speed *= amount.change_base(); + self.current_speed = self.base_speed.change_base(); + } + } + PatternEffect::TonePortamento(amount, target) => { + self.current_volume = (self.volume * global_settings.volume) + .try_change_base() + .unwrap(); + + if tick != 0 { + if *amount < 1.into() { + self.base_speed = + (self.base_speed * amount.change_base()).max(target.change_base()); + } else { + self.base_speed = + (self.base_speed * amount.change_base()).min(target.change_base()); + } + } + + self.current_speed = self.base_speed.change_base(); + } + PatternEffect::PitchBend(amount) => { + if tick == 0 { + self.base_speed = self.original_speed * amount.change_base(); + self.current_speed = self.base_speed.change_base(); + } + } - // Some effects have to happen regardless of if we're actually playing anything - match effect { PatternEffect::SetTicksPerStep(amount) => { global_settings.ticks_per_step = *amount; } @@ -474,50 +465,27 @@ impl TrackerChannel { global_settings.volume = (global_settings.volume + *volume_delta).clamp(0.into(), 1.into()); } - _ => {} } } #[must_use] - fn update_volume_envelope>( + fn update_volume_envelope( &mut self, - mixer: &mut M, envelope_state: &EnvelopeState, envelope: &agb_tracker_interop::Envelope, global_settings: &GlobalSettings, ) -> bool { - if let Some(channel) = self - .channel_id - .as_ref() - .and_then(|channel_id| mixer.channel(channel_id)) - { - let amount = envelope.amount[envelope_state.frame]; + let amount = envelope.amount[envelope_state.frame]; - if envelope_state.finished { - self.volume = (self.volume - envelope_state.fadeout).max(0.into()); - } - - channel.volume( - (self.volume * amount.change_base() * global_settings.volume) - .try_change_base() - .unwrap(), - ); - - self.volume != 0.into() - } else { - false + if envelope_state.finished { + self.volume = (self.volume - envelope_state.fadeout).max(0.into()); } - } -} -impl Default for TrackerChannel { - fn default() -> Self { - Self { - channel_id: None, - original_speed: Num::default(), - base_speed: Num::default(), - volume: Num::default(), - } + self.current_volume = (self.volume * amount.change_base() * global_settings.volume) + .try_change_base() + .unwrap(); + + self.volume != 0.into() } }