mirror of
https://github.com/italicsjenga/agb.git
synced 2024-12-23 08:11:33 +11:00
Refactor to effects
This commit is contained in:
parent
7861571a96
commit
aa635e9aa6
|
@ -29,14 +29,24 @@ pub struct Pattern {
|
|||
|
||||
#[derive(Debug)]
|
||||
pub struct PatternSlot {
|
||||
pub volume: Num<i16, 4>,
|
||||
pub speed: Num<u32, 8>,
|
||||
pub panning: Num<i16, 4>,
|
||||
pub sample: usize,
|
||||
pub effect1: PatternEffect,
|
||||
pub effect2: PatternEffect,
|
||||
}
|
||||
|
||||
pub const SKIP_SLOT: usize = 277;
|
||||
pub const STOP_CHANNEL: usize = 278;
|
||||
#[derive(Debug, Default)]
|
||||
pub enum PatternEffect {
|
||||
/// Don't play an effect
|
||||
#[default]
|
||||
None,
|
||||
/// Stops playing the current note
|
||||
Stop,
|
||||
/// Plays an arpeggiation of three notes in one row, cycling betwen the current note, current note + first speed, current note + second speed
|
||||
Arpeggio(Num<u16, 8>, Num<u16, 8>),
|
||||
Panning(Num<i16, 4>),
|
||||
Volume(Num<i16, 4>),
|
||||
}
|
||||
|
||||
#[cfg(feature = "quote")]
|
||||
impl<'a> quote::ToTokens for Track<'a> {
|
||||
|
@ -116,22 +126,20 @@ impl quote::ToTokens for PatternSlot {
|
|||
use quote::{quote, TokenStreamExt};
|
||||
|
||||
let PatternSlot {
|
||||
volume,
|
||||
speed,
|
||||
panning,
|
||||
sample,
|
||||
effect1,
|
||||
effect2,
|
||||
} = &self;
|
||||
|
||||
let volume = volume.to_raw();
|
||||
let speed = speed.to_raw();
|
||||
let panning = panning.to_raw();
|
||||
|
||||
tokens.append_all(quote! {
|
||||
agb_tracker::__private::agb_tracker_interop::PatternSlot {
|
||||
volume: agb_tracker::__private::Num::from_raw(#volume),
|
||||
speed: agb_tracker::__private::Num::from_raw(#speed),
|
||||
panning: agb_tracker::__private::Num::from_raw(#panning),
|
||||
sample: #sample,
|
||||
effect1: #effect1,
|
||||
effect2: #effect2,
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -155,3 +163,32 @@ impl quote::ToTokens for Pattern {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "quote")]
|
||||
impl quote::ToTokens for PatternEffect {
|
||||
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
|
||||
use quote::{quote, TokenStreamExt};
|
||||
|
||||
let type_bit = match self {
|
||||
PatternEffect::None => quote! { None },
|
||||
PatternEffect::Stop => quote! { Stop },
|
||||
PatternEffect::Arpeggio(first, second) => {
|
||||
let first = first.to_raw();
|
||||
let second = second.to_raw();
|
||||
quote! { Arpeggio(agb_tracker::__private::Num::from_raw(#first), agb_tracker::__private::Num::from_raw(#second)) }
|
||||
}
|
||||
PatternEffect::Panning(panning) => {
|
||||
let panning = panning.to_raw();
|
||||
quote! { Panning(agb_tracker::__private::Num::from_raw(#panning))}
|
||||
}
|
||||
PatternEffect::Volume(volume) => {
|
||||
let volume = volume.to_raw();
|
||||
quote! { Volume(agb_tracker::__private::Num::from_raw(#volume))}
|
||||
}
|
||||
};
|
||||
|
||||
tokens.append_all(quote! {
|
||||
agb_tracker::__private::agb_tracker_interop::PatternEffect::#type_bit
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
extern crate alloc;
|
||||
|
||||
use agb_tracker_interop::{PatternEffect, Sample};
|
||||
use alloc::vec::Vec;
|
||||
|
||||
use agb::{
|
||||
|
@ -26,7 +27,7 @@ pub use agb_tracker_interop::Track;
|
|||
|
||||
pub struct Tracker {
|
||||
track: &'static Track<'static>,
|
||||
channels: Vec<Option<ChannelId>>,
|
||||
channels: Vec<TrackerChannel>,
|
||||
|
||||
frame: Num<u16, 8>,
|
||||
tick: u16,
|
||||
|
@ -35,10 +36,14 @@ pub struct Tracker {
|
|||
current_pattern: usize,
|
||||
}
|
||||
|
||||
struct TrackerChannel {
|
||||
channel_id: Option<ChannelId>,
|
||||
}
|
||||
|
||||
impl Tracker {
|
||||
pub fn new(track: &'static Track<'static>) -> Self {
|
||||
let mut channels = Vec::new();
|
||||
channels.resize_with(track.num_channels, || None);
|
||||
channels.resize_with(track.num_channels, || TrackerChannel { channel_id: None });
|
||||
|
||||
Self {
|
||||
track,
|
||||
|
@ -66,55 +71,62 @@ impl Tracker {
|
|||
let pattern_slots =
|
||||
&self.track.pattern_data[pattern_data_pos..pattern_data_pos + self.track.num_channels];
|
||||
|
||||
for (channel_id, pattern_slot) in self.channels.iter_mut().zip(pattern_slots) {
|
||||
if pattern_slot.sample == agb_tracker_interop::SKIP_SLOT {
|
||||
// completely skip
|
||||
} else if pattern_slot.sample == agb_tracker_interop::STOP_CHANNEL {
|
||||
if let Some(channel) = channel_id
|
||||
.take()
|
||||
.and_then(|channel_id| mixer.channel(&channel_id))
|
||||
{
|
||||
channel.stop();
|
||||
}
|
||||
} else if pattern_slot.sample == 0 {
|
||||
if let Some(channel) = channel_id
|
||||
.as_ref()
|
||||
.and_then(|channel_id| mixer.channel(channel_id))
|
||||
{
|
||||
if pattern_slot.volume != 0.into() {
|
||||
channel.volume(pattern_slot.volume);
|
||||
}
|
||||
|
||||
if pattern_slot.panning != 0.into() {
|
||||
channel.panning(pattern_slot.panning);
|
||||
}
|
||||
|
||||
if pattern_slot.speed != 0.into() {
|
||||
channel.playback(pattern_slot.speed);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if let Some(channel) = channel_id
|
||||
.take()
|
||||
.and_then(|channel_id| mixer.channel(&channel_id))
|
||||
{
|
||||
channel.stop();
|
||||
}
|
||||
|
||||
for (channel, pattern_slot) in self.channels.iter_mut().zip(pattern_slots) {
|
||||
if pattern_slot.sample != 0 {
|
||||
let sample = &self.track.samples[pattern_slot.sample - 1];
|
||||
let mut new_channel = SoundChannel::new(sample.data);
|
||||
new_channel
|
||||
.panning(pattern_slot.panning)
|
||||
.volume(pattern_slot.volume)
|
||||
.playback(pattern_slot.speed)
|
||||
.restart_point(sample.restart_point);
|
||||
|
||||
if sample.should_loop {
|
||||
new_channel.should_loop();
|
||||
}
|
||||
|
||||
*channel_id = mixer.play_sound(new_channel);
|
||||
channel.play_sound(mixer, sample);
|
||||
}
|
||||
|
||||
channel.apply_effect(mixer, &pattern_slot.effect1, self.tick, pattern_slot.speed);
|
||||
channel.apply_effect(mixer, &pattern_slot.effect2, self.tick, pattern_slot.speed);
|
||||
// if pattern_slot.sample == agb_tracker_interop::SKIP_SLOT {
|
||||
// // completely skip
|
||||
// } else if pattern_slot.sample == agb_tracker_interop::STOP_CHANNEL {
|
||||
// if let Some(channel) = channel_id
|
||||
// .take()
|
||||
// .and_then(|channel_id| mixer.channel(&channel_id))
|
||||
// {
|
||||
// channel.stop();
|
||||
// }
|
||||
// } else if pattern_slot.sample == 0 {
|
||||
// if let Some(channel) = channel_id
|
||||
// .as_ref()
|
||||
// .and_then(|channel_id| mixer.channel(channel_id))
|
||||
// {
|
||||
// if pattern_slot.volume != 0.into() {
|
||||
// channel.volume(pattern_slot.volume);
|
||||
// }
|
||||
|
||||
// if pattern_slot.panning != 0.into() {
|
||||
// channel.panning(pattern_slot.panning);
|
||||
// }
|
||||
|
||||
// if pattern_slot.speed != 0.into() {
|
||||
// channel.playback(pattern_slot.speed);
|
||||
// }
|
||||
// }
|
||||
// } else {
|
||||
// if let Some(channel) = channel_id
|
||||
// .take()
|
||||
// .and_then(|channel_id| mixer.channel(&channel_id))
|
||||
// {
|
||||
// channel.stop();
|
||||
// }
|
||||
|
||||
// let sample = &self.track.samples[pattern_slot.sample - 1];
|
||||
// let mut new_channel = SoundChannel::new(sample.data);
|
||||
// new_channel
|
||||
// .panning(pattern_slot.panning)
|
||||
// .volume(pattern_slot.volume)
|
||||
// .playback(pattern_slot.speed)
|
||||
// .restart_point(sample.restart_point);
|
||||
|
||||
// if sample.should_loop {
|
||||
// new_channel.should_loop();
|
||||
// }
|
||||
|
||||
// *channel_id = mixer.play_sound(new_channel);
|
||||
// }
|
||||
}
|
||||
|
||||
self.increment_step();
|
||||
|
@ -147,6 +159,57 @@ impl Tracker {
|
|||
}
|
||||
}
|
||||
|
||||
impl TrackerChannel {
|
||||
fn play_sound(&mut self, mixer: &mut Mixer<'_>, sample: &Sample<'static>) {
|
||||
self.channel_id
|
||||
.take()
|
||||
.and_then(|channel_id| mixer.channel(&channel_id))
|
||||
.map(|channel| channel.stop());
|
||||
|
||||
let mut new_channel = SoundChannel::new(sample.data);
|
||||
|
||||
if sample.should_loop {
|
||||
new_channel
|
||||
.should_loop()
|
||||
.restart_point(sample.restart_point);
|
||||
}
|
||||
|
||||
self.channel_id = mixer.play_sound(new_channel)
|
||||
}
|
||||
|
||||
fn apply_effect(
|
||||
&mut self,
|
||||
mixer: &mut Mixer<'_>,
|
||||
effect: &PatternEffect,
|
||||
tick: u16,
|
||||
speed: Num<u32, 8>,
|
||||
) {
|
||||
if let Some(channel) = self
|
||||
.channel_id
|
||||
.as_ref()
|
||||
.and_then(|channel_id| mixer.channel(&channel_id))
|
||||
{
|
||||
if speed != 0.into() {
|
||||
channel.playback(speed);
|
||||
}
|
||||
|
||||
match effect {
|
||||
PatternEffect::None => {}
|
||||
PatternEffect::Stop => {
|
||||
channel.stop();
|
||||
}
|
||||
PatternEffect::Arpeggio(_, _) => todo!(),
|
||||
PatternEffect::Panning(panning) => {
|
||||
channel.panning(*panning);
|
||||
}
|
||||
PatternEffect::Volume(volume) => {
|
||||
channel.volume(*volume);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[agb::entry]
|
||||
fn main(gba: agb::Gba) -> ! {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use std::{collections::HashMap, error::Error, fs, path::Path};
|
||||
|
||||
use agb_tracker_interop::PatternEffect;
|
||||
use proc_macro2::TokenStream;
|
||||
use proc_macro_error::abort;
|
||||
|
||||
|
@ -53,7 +54,6 @@ pub fn parse_module(module: &Module) -> TokenStream {
|
|||
should_loop: bool,
|
||||
fine_tune: f64,
|
||||
relative_note: i8,
|
||||
volume: f64,
|
||||
restart_point: u32,
|
||||
}
|
||||
|
||||
|
@ -66,7 +66,6 @@ pub fn parse_module(module: &Module) -> TokenStream {
|
|||
let should_loop = !matches!(sample.flags, LoopType::No);
|
||||
let fine_tune = sample.finetune as f64;
|
||||
let relative_note = sample.relative_note;
|
||||
let volume = sample.volume as f64;
|
||||
let restart_point = sample.loop_start;
|
||||
let sample_len = if sample.loop_length > 0 {
|
||||
(sample.loop_length + sample.loop_start) as usize
|
||||
|
@ -93,7 +92,6 @@ pub fn parse_module(module: &Module) -> TokenStream {
|
|||
should_loop,
|
||||
fine_tune,
|
||||
relative_note,
|
||||
volume,
|
||||
restart_point,
|
||||
});
|
||||
}
|
||||
|
@ -125,50 +123,35 @@ pub fn parse_module(module: &Module) -> TokenStream {
|
|||
}
|
||||
};
|
||||
|
||||
let (mut volume, mut panning) = match slot.volume {
|
||||
0x10..=0x50 => (Some((slot.volume - 0x10) as f64 / 64.0), None),
|
||||
0xC0..=0xCF => (
|
||||
None,
|
||||
Some(Num::new(slot.volume as i16 - (0xC0 + (0xCF - 0xC0) / 2)) / 64),
|
||||
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,
|
||||
),
|
||||
_ => (None, Some(0.into())),
|
||||
_ => PatternEffect::None,
|
||||
};
|
||||
|
||||
if slot.effect_type == 0xC {
|
||||
volume = Some(slot.effect_parameter as f64 / 255.0);
|
||||
}
|
||||
let effect2 = match slot.effect_type {
|
||||
0x8 => {
|
||||
PatternEffect::Panning(Num::new(slot.effect_parameter as i16 - 128) / 128)
|
||||
}
|
||||
0xC => PatternEffect::Volume(Num::new(slot.effect_parameter as i16) / 255),
|
||||
_ => PatternEffect::None,
|
||||
};
|
||||
|
||||
if slot.effect_type == 0x8 {
|
||||
panning = Some(Num::new(slot.effect_parameter as i16 - 128) / 128);
|
||||
if matches!(slot.note, Note::KeyOff) {
|
||||
effect1 = PatternEffect::Stop;
|
||||
}
|
||||
|
||||
if sample == 0 {
|
||||
if slot.volume == 0 && slot.effect_type == 0 {
|
||||
pattern_data.push(agb_tracker_interop::PatternSlot {
|
||||
volume: 0.into(),
|
||||
speed: 0.into(),
|
||||
panning: 0.into(),
|
||||
sample: agb_tracker_interop::SKIP_SLOT,
|
||||
});
|
||||
} else if matches!(slot.note, Note::KeyOff) || volume == Some(0.0) {
|
||||
pattern_data.push(agb_tracker_interop::PatternSlot {
|
||||
volume: 0.into(),
|
||||
speed: 0.into(),
|
||||
panning: 0.into(),
|
||||
sample: agb_tracker_interop::STOP_CHANNEL,
|
||||
});
|
||||
} else {
|
||||
let volume: Num<i16, 4> =
|
||||
Num::from_raw((volume.unwrap_or(0.into()) * (1 << 4) as f64) as i16);
|
||||
let panning = panning.unwrap_or(0.into());
|
||||
|
||||
pattern_data.push(agb_tracker_interop::PatternSlot {
|
||||
volume,
|
||||
speed: 0.into(),
|
||||
panning,
|
||||
sample: 0,
|
||||
});
|
||||
}
|
||||
pattern_data.push(agb_tracker_interop::PatternSlot {
|
||||
speed: 0.into(),
|
||||
sample: 0,
|
||||
effect1,
|
||||
effect2,
|
||||
});
|
||||
} else {
|
||||
let sample_played = &samples[sample - 1];
|
||||
|
||||
|
@ -179,15 +162,11 @@ pub fn parse_module(module: &Module) -> TokenStream {
|
|||
module.frequency_type,
|
||||
);
|
||||
|
||||
let overall_volume = volume.unwrap_or(1.into()) * sample_played.volume;
|
||||
let volume: Num<i16, 4> =
|
||||
Num::from_raw((overall_volume * (1 << 4) as f64) as i16);
|
||||
|
||||
pattern_data.push(agb_tracker_interop::PatternSlot {
|
||||
volume,
|
||||
speed,
|
||||
panning: panning.unwrap_or(0.into()),
|
||||
sample,
|
||||
effect1,
|
||||
effect2,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue