Implement global volume

This commit is contained in:
Gwilym Inzani 2023-08-05 22:41:55 +01:00
parent 3976fd93ea
commit f6ed1c484b
3 changed files with 80 additions and 16 deletions

View file

@ -66,6 +66,8 @@ pub enum PatternEffect {
TonePortamento(Num<u16, 12>, Num<u16, 12>), TonePortamento(Num<u16, 12>, Num<u16, 12>),
SetTicksPerStep(u32), SetTicksPerStep(u32),
SetFramesPerTick(Num<u32, 8>), SetFramesPerTick(Num<u32, 8>),
SetGlobalVolume(Num<i32, 8>),
GlobalVolumeSlide(Num<i32, 8>),
} }
#[cfg(feature = "quote")] #[cfg(feature = "quote")]
@ -297,6 +299,14 @@ impl quote::ToTokens for PatternEffect {
let amount = new_frames_per_tick.to_raw(); let amount = new_frames_per_tick.to_raw();
quote! { SetFramesPerTick(agb_tracker::__private::Num::from_raw(#amount)) } quote! { SetFramesPerTick(agb_tracker::__private::Num::from_raw(#amount)) }
} }
PatternEffect::SetGlobalVolume(amount) => {
let amount = amount.to_raw();
quote! { SetGlobalVolume(agb_tracker::__private::Num::from_raw(#amount)) }
}
PatternEffect::GlobalVolumeSlide(amount) => {
let amount = amount.to_raw();
quote! { GlobalVolumeSlide(agb_tracker::__private::Num::from_raw(#amount)) }
}
}; };
tokens.append_all(quote! { tokens.append_all(quote! {

View file

@ -120,6 +120,7 @@ struct GlobalSettings {
ticks_per_step: u32, ticks_per_step: u32,
frames_per_tick: Num<u32, 8>, frames_per_tick: Num<u32, 8>,
volume: Num<i32, 8>,
} }
impl Tracker { impl Tracker {
@ -134,6 +135,7 @@ impl Tracker {
let global_settings = GlobalSettings { let global_settings = GlobalSettings {
ticks_per_step: track.ticks_per_step, ticks_per_step: track.ticks_per_step,
frames_per_tick: track.frames_per_tick, frames_per_tick: track.frames_per_tick,
volume: 1.into(),
}; };
Self { Self {
@ -172,7 +174,7 @@ impl Tracker {
{ {
if pattern_slot.sample != 0 && self.tick == 0 { if pattern_slot.sample != 0 && self.tick == 0 {
let sample = &self.track.samples[pattern_slot.sample as usize - 1]; let sample = &self.track.samples[pattern_slot.sample as usize - 1];
channel.play_sound(mixer, sample); channel.play_sound(mixer, sample, &self.global_settings);
self.envelopes[i] = sample.volume_envelope.map(|envelope_id| EnvelopeState { self.envelopes[i] = sample.volume_envelope.map(|envelope_id| EnvelopeState {
frame: 0, frame: 0,
envelope_id, envelope_id,
@ -208,7 +210,12 @@ impl Tracker {
if let Some(envelope_state) = envelope_state_option { if let Some(envelope_state) = envelope_state_option {
let envelope = &self.track.envelopes[envelope_state.envelope_id]; let envelope = &self.track.envelopes[envelope_state.envelope_id];
if !channel.update_volume_envelope(mixer, envelope_state.frame, envelope) { if !channel.update_volume_envelope(
mixer,
envelope_state.frame,
envelope,
&self.global_settings,
) {
envelope_state_option.take(); envelope_state_option.take();
} else { } else {
envelope_state.frame += 1; envelope_state.frame += 1;
@ -272,7 +279,12 @@ impl Tracker {
} }
impl TrackerChannel { impl TrackerChannel {
fn play_sound(&mut self, mixer: &mut Mixer<'_>, sample: &Sample<'static>) { fn play_sound(
&mut self,
mixer: &mut Mixer<'_>,
sample: &Sample<'static>,
global_settings: &GlobalSettings,
) {
if let Some(channel) = self if let Some(channel) = self
.channel_id .channel_id
.take() .take()
@ -283,7 +295,11 @@ impl TrackerChannel {
let mut new_channel = SoundChannel::new(sample.data); let mut new_channel = SoundChannel::new(sample.data);
new_channel.volume(sample.volume.change_base()); new_channel.volume(
(sample.volume.change_base() * global_settings.volume)
.try_change_base()
.unwrap(),
);
if sample.should_loop { if sample.should_loop {
new_channel new_channel
@ -342,19 +358,31 @@ impl TrackerChannel {
channel.panning(panning.change_base()); channel.panning(panning.change_base());
} }
PatternEffect::Volume(volume) => { PatternEffect::Volume(volume) => {
channel.volume(volume.change_base()); channel.volume(
(volume.change_base() * global_settings.volume)
.try_change_base()
.unwrap(),
);
self.volume = volume.change_base(); self.volume = volume.change_base();
} }
PatternEffect::VolumeSlide(amount) => { PatternEffect::VolumeSlide(amount) => {
if tick != 0 { if tick != 0 {
self.volume = (self.volume + amount.change_base()).max(0.into()); self.volume = (self.volume + amount.change_base()).max(0.into());
channel.volume(self.volume.try_change_base().unwrap()); channel.volume(
(self.volume * global_settings.volume)
.try_change_base()
.unwrap(),
);
} }
} }
PatternEffect::FineVolumeSlide(amount) => { PatternEffect::FineVolumeSlide(amount) => {
if tick == 0 { if tick == 0 {
self.volume = (self.volume + amount.change_base()).max(0.into()); self.volume = (self.volume + amount.change_base()).max(0.into());
channel.volume(self.volume.try_change_base().unwrap()); channel.volume(
(self.volume * global_settings.volume)
.try_change_base()
.unwrap(),
);
} }
} }
PatternEffect::NoteCut(wait) => { PatternEffect::NoteCut(wait) => {
@ -373,7 +401,11 @@ impl TrackerChannel {
} }
} }
PatternEffect::TonePortamento(amount, target) => { PatternEffect::TonePortamento(amount, target) => {
channel.volume(self.volume.try_change_base().unwrap()); channel.volume(
(self.volume * global_settings.volume)
.try_change_base()
.unwrap(),
);
if tick != 0 { if tick != 0 {
if *amount < 1.into() { if *amount < 1.into() {
@ -388,21 +420,27 @@ impl TrackerChannel {
channel.playback(self.base_speed.change_base()); channel.playback(self.base_speed.change_base());
} }
// These are global effects handled below // These are global effects handled below
PatternEffect::SetTicksPerStep(_) | PatternEffect::SetFramesPerTick(_) => {} PatternEffect::SetTicksPerStep(_)
| PatternEffect::SetFramesPerTick(_)
| PatternEffect::SetGlobalVolume(_)
| PatternEffect::GlobalVolumeSlide(_) => {}
} }
} }
// Some effects have to happen regardless of if we're actually playing anything // Some effects have to happen regardless of if we're actually playing anything
match effect { match effect {
PatternEffect::SetTicksPerStep(amount) => { PatternEffect::SetTicksPerStep(amount) => {
if tick == 0 { global_settings.ticks_per_step = *amount;
global_settings.ticks_per_step = *amount;
}
} }
PatternEffect::SetFramesPerTick(new_frames_per_tick) => { PatternEffect::SetFramesPerTick(new_frames_per_tick) => {
if tick == 0 { global_settings.frames_per_tick = *new_frames_per_tick;
global_settings.frames_per_tick = *new_frames_per_tick; }
} PatternEffect::SetGlobalVolume(volume) => {
global_settings.volume = *volume;
}
PatternEffect::GlobalVolumeSlide(volume_delta) => {
global_settings.volume =
(global_settings.volume + *volume_delta).clamp(0.into(), 1.into());
} }
_ => {} _ => {}
} }
@ -414,6 +452,7 @@ impl TrackerChannel {
mixer: &mut Mixer<'_>, mixer: &mut Mixer<'_>,
frame: usize, frame: usize,
envelope: &agb_tracker_interop::Envelope<'_>, envelope: &agb_tracker_interop::Envelope<'_>,
global_settings: &GlobalSettings,
) -> bool { ) -> bool {
if let Some(channel) = self if let Some(channel) = self
.channel_id .channel_id
@ -423,7 +462,7 @@ impl TrackerChannel {
let amount = envelope.amount[frame]; let amount = envelope.amount[frame];
channel.volume( channel.volume(
(self.volume * amount.change_base()) (self.volume * amount.change_base() * global_settings.volume)
.try_change_base() .try_change_base()
.unwrap(), .unwrap(),
); );

View file

@ -350,6 +350,21 @@ pub fn parse_module(module: &Module) -> TokenStream {
slot.effect_parameter as u32, slot.effect_parameter as u32,
)), )),
}, },
// G
0x10 => PatternEffect::SetGlobalVolume(
Num::new(slot.effect_parameter as i32) / 0x40,
),
// H
0x11 => {
let first = effect_parameter >> 4;
let second = effect_parameter & 0xF;
if first == 0 {
PatternEffect::GlobalVolumeSlide(-Num::new(second as i32) / 0x40)
} else {
PatternEffect::GlobalVolumeSlide(Num::new(first as i32) / 0x40)
}
}
_ => PatternEffect::None, _ => PatternEffect::None,
}; };