diff --git a/tracker/agb-midi-core/src/lib.rs b/tracker/agb-midi-core/src/lib.rs index 7eb6d61f..2b1cefac 100644 --- a/tracker/agb-midi-core/src/lib.rs +++ b/tracker/agb-midi-core/src/lib.rs @@ -358,6 +358,10 @@ pub fn parse_midi(midi_info: &MidiInfo) -> Track { sustain: Some(envelope.amounts.len() - 1), loop_start: None, loop_end: None, + + vib_waveform: Default::default(), + vib_amount: Default::default(), + vib_speed: Default::default(), }) .collect(); diff --git a/tracker/agb-tracker-interop/src/lib.rs b/tracker/agb-tracker-interop/src/lib.rs index 9ed051d9..8e958f5d 100644 --- a/tracker/agb-tracker-interop/src/lib.rs +++ b/tracker/agb-tracker-interop/src/lib.rs @@ -49,6 +49,10 @@ pub struct Envelope { pub sustain: Option, pub loop_start: Option, pub loop_end: Option, + + pub vib_waveform: Waveform, + pub vib_amount: Num, + pub vib_speed: u8, } #[derive(Debug, Default, Clone, PartialEq, Eq)] @@ -80,7 +84,7 @@ pub enum PatternEffect { PitchBend(Num), } -#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)] pub enum Waveform { #[default] Sine, @@ -146,6 +150,9 @@ impl quote::ToTokens for Envelope { sustain, loop_start, loop_end, + vib_amount, + vib_speed, + vib_waveform, } = self; let amount = amount.iter().map(|value| { @@ -153,6 +160,11 @@ impl quote::ToTokens for Envelope { quote! { agb_tracker::__private::Num::from_raw(#value) } }); + let vib_amount = { + let value = vib_amount.to_raw(); + quote! { agb_tracker::__private::Num::from_raw(#value) } + }; + let sustain = match sustain { Some(value) => quote!(Some(#value)), None => quote!(None), @@ -175,6 +187,10 @@ impl quote::ToTokens for Envelope { sustain: #sustain, loop_start: #loop_start, loop_end: #loop_end, + + vib_waveform: #vib_waveform, + vib_amount: #vib_amount, + vib_speed: #vib_speed, } } }); diff --git a/tracker/agb-tracker/src/lib.rs b/tracker/agb-tracker/src/lib.rs index 3a8639f2..c575bb98 100644 --- a/tracker/agb-tracker/src/lib.rs +++ b/tracker/agb-tracker/src/lib.rs @@ -141,23 +141,27 @@ impl Waves { fn value(&self) -> Num { assert!(self.amount.abs() <= 1.into()); - let lookup = match self.waveform { - Waveform::Sine => lookups::SINE_LOOKUP, - Waveform::Saw => lookups::SAW_LOOKUP, - Waveform::Square => lookups::SQUARE_LOOKUP, - }; - - (self.amount * lookup[self.frame] + 1) - .try_change_base() - .unwrap() + calculate_wave(self.waveform, self.amount, self.frame) } } +fn calculate_wave(waveform: Waveform, amount: Num, frame: usize) -> Num { + let lookup = match waveform { + Waveform::Sine => lookups::SINE_LOOKUP, + Waveform::Saw => lookups::SAW_LOOKUP, + Waveform::Square => lookups::SQUARE_LOOKUP, + }; + + (amount * lookup[frame] + 1).try_change_base().unwrap() +} + struct EnvelopeState { frame: usize, envelope_id: usize, finished: bool, fadeout: Num, + + vibrato_pos: usize, } #[derive(Clone)] @@ -249,6 +253,8 @@ impl<'track, TChannelId> TrackerInner<'track, TChannelId> { envelope_id, finished: false, fadeout: sample.fadeout, + + vibrato_pos: 0, }); } @@ -277,7 +283,12 @@ impl<'track, TChannelId> TrackerInner<'track, TChannelId> { } fn realise>(&mut self, mixer: &mut M) { - for (mixer_channel, tracker_channel) in self.mixer_channels.iter().zip(&mut self.channels) { + for (i, (mixer_channel, tracker_channel)) in self + .mixer_channels + .iter() + .zip(&mut self.channels) + .enumerate() + { tracker_channel.tick(); if let Some(channel) = mixer_channel @@ -288,6 +299,19 @@ impl<'track, TChannelId> TrackerInner<'track, TChannelId> { if tracker_channel.vibrato.speed != 0 && tracker_channel.vibrato.enable { current_speed *= tracker_channel.vibrato.value().change_base(); + } else if let Some(envelope) = &mut self.envelopes[i] { + let track_envelope = &self.track.envelopes[envelope.envelope_id]; + + if track_envelope.vib_speed != 0 { + current_speed *= calculate_wave( + track_envelope.vib_waveform, + track_envelope.vib_amount.change_base(), + envelope.vibrato_pos, + ) + .change_base(); + envelope.vibrato_pos = + (envelope.vibrato_pos + track_envelope.vib_speed as usize) % 64; + } } channel.playback(current_speed.change_base()); diff --git a/tracker/agb-xm-core/src/lib.rs b/tracker/agb-xm-core/src/lib.rs index a895c3a8..53fb75c7 100644 --- a/tracker/agb-xm-core/src/lib.rs +++ b/tracker/agb-xm-core/src/lib.rs @@ -31,7 +31,12 @@ pub fn parse_module(module: &Module) -> agb_tracker_interop::Track { let envelope = &instrument.volume_envelope; let envelope_id = if envelope.enabled { - let envelope = EnvelopeData::new(envelope, module.default_bpm as u32); + let envelope = EnvelopeData::new( + envelope, + instrument, + module.frequency_type, + module.default_bpm as u32, + ); let id = existing_envelopes .entry(envelope) .or_insert_with_key(|envelope| { @@ -461,6 +466,10 @@ pub fn parse_module(module: &Module) -> agb_tracker_interop::Track { sustain: envelope.sustain, loop_start: envelope.loop_start, loop_end: envelope.loop_end, + + vib_amount: envelope.vib_amount.try_change_base().unwrap(), + vib_waveform: envelope.vib_waveform, + vib_speed: envelope.vib_speed, }) .collect::>(); @@ -541,10 +550,19 @@ struct EnvelopeData { sustain: Option, loop_start: Option, loop_end: Option, + + vib_waveform: Waveform, + vib_speed: u8, + vib_amount: Num, } impl EnvelopeData { - fn new(e: &xmrs::envelope::Envelope, bpm: u32) -> Self { + fn new( + e: &xmrs::envelope::Envelope, + instrument: &xmrs::instr_default::InstrDefault, + frequency_type: FrequencyType, + bpm: u32, + ) -> Self { let mut amounts = vec![]; for frame in 0..=(Self::envelope_frame_to_gba_frame(e.point.last().unwrap().frame, bpm)) { @@ -587,11 +605,29 @@ impl EnvelopeData { (None, None) }; + let vib_waveform = match instrument.vibrato.waveform { + xmrs::instr_vibrato::Waveform::Sine => Waveform::Sine, + xmrs::instr_vibrato::Waveform::Square => Waveform::Square, + xmrs::instr_vibrato::Waveform::RampUp => Waveform::Saw, + xmrs::instr_vibrato::Waveform::RampDown => Waveform::Saw, + }; + + let vib_speed = (instrument.vibrato.speed * 64.0) as u8; + let vib_depth = instrument.vibrato.depth * 16.0; + + let c4_speed = note_to_speed(Note::C4, 0.0, 0, frequency_type); + let vib_amount = + note_to_speed(Note::C4, vib_depth.into(), 0, frequency_type) / c4_speed - 1; + EnvelopeData { amounts, sustain, loop_start, loop_end, + + vib_waveform, + vib_speed, + vib_amount, } } diff --git a/tracker/desktop-player/tests/vibrato.xm b/tracker/desktop-player/tests/vibrato.xm index ea20a923..f167543c 100644 Binary files a/tracker/desktop-player/tests/vibrato.xm and b/tracker/desktop-player/tests/vibrato.xm differ