From a91069eac24db7ddf70539c16fb1a3f1e96e9b77 Mon Sep 17 00:00:00 2001 From: Gwilym Inzani Date: Tue, 18 Jul 2023 21:36:37 +0100 Subject: [PATCH] Consider the global volume --- tracker/agb-tracker-interop/src/lib.rs | 20 ++++++++-- tracker/agb-tracker/src/lib.rs | 2 + tracker/agb-xm-core/src/lib.rs | 55 +++++++++++++++++++------- 3 files changed, 58 insertions(+), 19 deletions(-) diff --git a/tracker/agb-tracker-interop/src/lib.rs b/tracker/agb-tracker-interop/src/lib.rs index cd015246..c0bd85e4 100644 --- a/tracker/agb-tracker-interop/src/lib.rs +++ b/tracker/agb-tracker-interop/src/lib.rs @@ -19,6 +19,7 @@ pub struct Sample<'a> { pub data: &'a [u8], pub should_loop: bool, pub restart_point: u32, + pub volume: Num, } #[derive(Debug)] @@ -104,10 +105,16 @@ impl<'a> quote::ToTokens for Sample<'a> { fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { use quote::{quote, TokenStreamExt}; - let self_as_u8s: Vec<_> = self.data.iter().map(|i| *i as u8).collect(); + let Sample { + data, + should_loop, + restart_point, + volume, + } = self; + + let self_as_u8s: Vec<_> = data.iter().map(|i| *i as u8).collect(); let samples = ByteString(&self_as_u8s); - let should_loop = self.should_loop; - let restart_point = self.restart_point; + let volume = volume.to_raw(); tokens.append_all(quote! { { @@ -115,7 +122,12 @@ impl<'a> quote::ToTokens for Sample<'a> { struct AlignmentWrapper([u8; N]); const SAMPLE_DATA: &[u8] = &AlignmentWrapper(*#samples).0; - agb_tracker::__private::agb_tracker_interop::Sample { data: SAMPLE_DATA, should_loop: #should_loop, restart_point: #restart_point } + agb_tracker::__private::agb_tracker_interop::Sample { + data: SAMPLE_DATA, + should_loop: #should_loop, + restart_point: #restart_point, + volume: agb_tracker::__private::Num::from_raw(#volume), + } } }); } diff --git a/tracker/agb-tracker/src/lib.rs b/tracker/agb-tracker/src/lib.rs index 382db5d8..cdf5f10c 100644 --- a/tracker/agb-tracker/src/lib.rs +++ b/tracker/agb-tracker/src/lib.rs @@ -148,6 +148,8 @@ impl TrackerChannel { let mut new_channel = SoundChannel::new(sample.data); + new_channel.volume(sample.volume); + if sample.should_loop { new_channel .should_loop() diff --git a/tracker/agb-xm-core/src/lib.rs b/tracker/agb-xm-core/src/lib.rs index f02e9370..f6476114 100644 --- a/tracker/agb-xm-core/src/lib.rs +++ b/tracker/agb-xm-core/src/lib.rs @@ -55,6 +55,7 @@ pub fn parse_module(module: &Module) -> TokenStream { fine_tune: f64, relative_note: i8, restart_point: u32, + volume: Num, } let mut samples = vec![]; @@ -73,6 +74,8 @@ pub fn parse_module(module: &Module) -> TokenStream { usize::MAX }; + let volume = Num::from_raw((sample.volume * (1 << 4) as f32) as i16); + let sample = match &sample.data { SampleDataType::Depth8(depth8) => depth8 .iter() @@ -93,6 +96,7 @@ pub fn parse_module(module: &Module) -> TokenStream { fine_tune, relative_note, restart_point, + volume, }); } } @@ -127,50 +131,62 @@ pub fn parse_module(module: &Module) -> TokenStream { } }; - let mut effect1 = match slot.volume { - 0x10..=0x50 => { - PatternEffect::Volume(Num::new((slot.volume - 0x10) as i16) / 64) - } - 0xC0..=0xCF => PatternEffect::Panning( - Num::new(slot.volume as i16 - (0xC0 + (0xCF - 0xC0) / 2)) / 64, - ), - _ => PatternEffect::None, - }; + let mut effect1 = PatternEffect::None; - if matches!(slot.note, Note::KeyOff) { + let maybe_note_and_sample = if matches!(slot.note, Note::KeyOff) { effect1 = PatternEffect::Stop; note_and_sample[channel_number] = None; + &None } else if !matches!(slot.note, Note::None) { if sample != 0 { note_and_sample[channel_number] = Some((slot.note, &samples[sample - 1])); } else if let Some((note, _)) = &mut note_and_sample[channel_number] { *note = slot.note; } + + ¬e_and_sample[channel_number] + } else { + ¬e_and_sample[channel_number] + }; + + if matches!(effect1, PatternEffect::None) { + effect1 = match slot.volume { + 0x10..=0x50 => PatternEffect::Volume( + (Num::new((slot.volume - 0x10) as i16) / 64) + * maybe_note_and_sample + .map(|note_and_sample| note_and_sample.1.volume) + .unwrap_or(1.into()), + ), + 0xC0..=0xCF => PatternEffect::Panning( + Num::new(slot.volume as i16 - (0xC0 + (0xCF - 0xC0) / 2)) / 64, + ), + _ => PatternEffect::None, + }; } let effect2 = match slot.effect_type { 0x0 => { if slot.effect_parameter == 0 { PatternEffect::None - } else if let Some((note, sample)) = note_and_sample[channel_number] { + } else if let Some((note, sample)) = maybe_note_and_sample { let first_arpeggio = slot.effect_parameter >> 4; let second_arpeggio = slot.effect_parameter & 0xF; let note_speed = note_to_speed( - note, + *note, sample.fine_tune, sample.relative_note, module.frequency_type, ); let first_arpeggio_speed = note_to_speed( - note, + *note, sample.fine_tune, sample.relative_note + first_arpeggio as i8, module.frequency_type, ); let second_arpeggio_speed = note_to_speed( - note, + *note, sample.fine_tune, sample.relative_note + second_arpeggio as i8, module.frequency_type, @@ -207,7 +223,15 @@ pub fn parse_module(module: &Module) -> TokenStream { PatternEffect::VolumeSlide(Num::new(first as i16) / 16) } } - 0xC => PatternEffect::Volume(Num::new(slot.effect_parameter as i16) / 255), + 0xC => { + if let Some((_, sample)) = maybe_note_and_sample { + PatternEffect::Volume( + (Num::new(slot.effect_parameter as i16) / 255) * sample.volume, + ) + } else { + PatternEffect::None + } + } _ => PatternEffect::None, }; @@ -250,6 +274,7 @@ pub fn parse_module(module: &Module) -> TokenStream { data: &sample.data, should_loop: sample.should_loop, restart_point: sample.restart_point, + volume: sample.volume, }) .collect();