Skip to content

Commit

Permalink
feat(effect): porta slide and bump
Browse files Browse the repository at this point in the history
  • Loading branch information
nenikitov committed May 5, 2024
1 parent 13ce305 commit 89f67a6
Show file tree
Hide file tree
Showing 4 changed files with 148 additions and 19 deletions.
16 changes: 15 additions & 1 deletion engine/src/asset/sound/dat/finetune.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::ops::{Add, Sub};
use std::ops::{Add, AddAssign, Neg, Sub};

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct FineTune {
Expand Down Expand Up @@ -46,6 +46,12 @@ impl Add for FineTune {
}
}

impl AddAssign for FineTune {
fn add_assign(&mut self, rhs: Self) {
self.cents = self.cents.saturating_add(rhs.cents)

Check warning on line 51 in engine/src/asset/sound/dat/finetune.rs

View workflow job for this annotation

GitHub Actions / clippy

consider adding a `;` to the last statement for consistent formatting

warning: consider adding a `;` to the last statement for consistent formatting --> engine/src/asset/sound/dat/finetune.rs:51:9 | 51 | self.cents = self.cents.saturating_add(rhs.cents) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add a `;` here: `self.cents = self.cents.saturating_add(rhs.cents);` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#semicolon_if_nothing_returned = note: `#[warn(clippy::semicolon_if_nothing_returned)]` implied by `#[warn(clippy::pedantic)]`
}
}

impl Sub for FineTune {
type Output = FineTune;

Expand All @@ -54,6 +60,14 @@ impl Sub for FineTune {
}
}

impl Neg for FineTune {
type Output = FineTune;

fn neg(self) -> Self::Output {
FineTune::new(-self.cents)
}
}

#[cfg(test)]
mod tests {
use assert_approx_eq::assert_approx_eq;
Expand Down
21 changes: 19 additions & 2 deletions engine/src/asset/sound/dat/mixer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,18 @@ impl TSongMixerUtils for TSong {
PatternEffect::Speed(Speed::TicksPerRow(s)) => {
speed = s;
}
PatternEffect::Volume(Volume::Value(volume)) => {
PatternEffect::Volume(Volume::Set(volume)) => {
channel.volume = volume;
}
PatternEffect::Porta(Porta::Bump {
up: _,
small: _,
finetune: Some(finetune),
}) => {
if let Some(note) = &mut channel.note {
note.finetune += finetune;
}
}
PatternEffect::SampleOffset(Some(offset)) => {
channel.sample_position = offset;
}
Expand Down Expand Up @@ -125,6 +134,14 @@ impl TSongMixerUtils for TSong {
PatternEffect::Volume(Volume::Slide(Some(volume))) => {
channel.volume += volume;
}
PatternEffect::Porta(Porta::Slide {
up: _,
finetune: Some(finetune),
}) => {
if let Some(note) = &mut channel.note {
note.finetune += *finetune;
}
}
_ => {}
}
}
Expand Down Expand Up @@ -238,7 +255,7 @@ impl<'a> Channel<'a> {
if let Some(note) = &self.note
&& let Some(PatternEventInstrumentKind::Predefined(instrument)) = self.instrument
&& let TInstrumentSampleKind::Predefined(sample) =
&instrument.samples[note.finetune.note() as usize]
&instrument.samples[note.finetune.note().clamp(0, 95) as usize]

Check warning on line 258 in engine/src/asset/sound/dat/mixer.rs

View workflow job for this annotation

GitHub Actions / clippy

casting `i32` to `usize` may lose the sign of the value

warning: casting `i32` to `usize` may lose the sign of the value --> engine/src/asset/sound/dat/mixer.rs:258:37 | 258 | &instrument.samples[note.finetune.note().clamp(0, 95) as usize] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#cast_sign_loss
{
Some((note, instrument, sample))
} else {
Expand Down
122 changes: 107 additions & 15 deletions engine/src/asset/sound/dat/pattern_effect.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::hash::Hash;

use super::convert_volume;
use super::{convert_volume, finetune::FineTune};
use crate::{
asset::{extension::*, AssetParser},
utils::nom::*,
Expand All @@ -14,8 +14,23 @@ pub enum Speed {

#[derive(Debug, Clone, Copy)]
pub enum Volume {
Value(f32),
Set(f32),
Slide(Option<f32>),
Bump { up: bool, volume: Option<f32> },
}

#[derive(Debug, Clone, Copy)]
pub enum Porta {
Tone(Option<FineTune>),
Slide {
up: bool,
finetune: Option<FineTune>,
},
Bump {
up: bool,
small: bool,
finetune: Option<FineTune>,
},
}

#[derive(Debug, Default, Clone, Copy)]
Expand All @@ -28,22 +43,69 @@ pub enum PlaybackDirection {
#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)]
pub enum PatternEffectMemoryKey {
VolumeSlide,
VolumeBumpUp,
VolumeBumpDown,
SampleOffset,
PortaTone,
PortaSlideUp,
PortaSlideDown,
PortaBumpUp,
PortaBumpDown,
PortaBumpSmallUp,
PortaBumpSmallDown,
}

#[derive(Debug, Clone, Copy)]
pub enum PatternEffect {
Dummy(u8),
Speed(Speed),
Volume(Volume),
Porta(Porta),
SampleOffset(Option<usize>),
PlaybackDirection(PlaybackDirection),
}

impl PatternEffect {
pub fn memory_key(&self) -> Option<PatternEffectMemoryKey> {
match self {
PatternEffect::Porta(Porta::Tone(_)) => Some(PatternEffectMemoryKey::PortaTone),
PatternEffect::Porta(Porta::Slide {
up: true,
finetune: _,
}) => Some(PatternEffectMemoryKey::PortaSlideUp),
PatternEffect::Porta(Porta::Slide {
up: false,
finetune: _,
}) => Some(PatternEffectMemoryKey::PortaSlideDown),
PatternEffect::Porta(Porta::Bump {
up: true,
small: false,
finetune: _,
}) => Some(PatternEffectMemoryKey::PortaBumpUp),
PatternEffect::Porta(Porta::Bump {
up: false,
small: false,
finetune: _,
}) => Some(PatternEffectMemoryKey::PortaBumpDown),
PatternEffect::Porta(Porta::Bump {
up: true,
small: true,
finetune: _,
}) => Some(PatternEffectMemoryKey::PortaBumpSmallUp),
PatternEffect::Porta(Porta::Bump {
up: false,
small: true,
finetune: _,
}) => Some(PatternEffectMemoryKey::PortaBumpSmallDown),
PatternEffect::Volume(Volume::Slide(_)) => Some(PatternEffectMemoryKey::VolumeSlide),
PatternEffect::Volume(Volume::Bump {
up: true,
volume: _,
}) => Some(PatternEffectMemoryKey::VolumeBumpUp),
PatternEffect::Volume(Volume::Bump {
up: down,
volume: _,
}) => Some(PatternEffectMemoryKey::VolumeBumpDown),
PatternEffect::SampleOffset(_) => Some(PatternEffectMemoryKey::SampleOffset),
_ => None,
}
Expand Down Expand Up @@ -73,30 +135,60 @@ impl AssetParser<Wildcard> for Option<PatternEffect> {

Ok((
input,
should_parse.then(|| match kind {
0x09 => PatternEffect::SampleOffset(if value != 0 {
Some(value as usize * 256)
} else {
None
should_parse.then_some(match kind {
0x01 => PatternEffect::Porta(Porta::Slide {
up: true,
finetune: (value != 0).then_some(FineTune::new(8 * value as i32)),
}),
0x02 => PatternEffect::Porta(Porta::Slide {
up: false,
finetune: (value != 0).then_some(-FineTune::new(8 * value as i32)),
}),
0x03 => PatternEffect::Porta(Porta::Tone(
(value != 0).then_some(FineTune::new(8 * value as i32)),
)),
0x15 => PatternEffect::Porta(Porta::Bump {
up: true,
small: false,
finetune: (value != 0).then_some(FineTune::new(8 * value as i32)),
}),
0x16 => PatternEffect::Porta(Porta::Bump {
up: false,
small: false,
finetune: (value != 0).then_some(FineTune::new(8 * value as i32)),
}),
0x24 => PatternEffect::Porta(Porta::Bump {
up: true,
small: true,
finetune: (value != 0).then_some(FineTune::new(2 * value as i32)),
}),
0x25 => PatternEffect::Porta(Porta::Bump {
up: false,
small: true,
finetune: (value != 0).then_some(FineTune::new(2 * value as i32)),
}),
0x09 => {
PatternEffect::SampleOffset((value != 0).then_some(value as usize * 256))
}
0x0E => PatternEffect::Speed(if value >= 0x20 {
Speed::Bpm(value as usize)
} else {
Speed::TicksPerRow(value as usize)
}),
0x0C => PatternEffect::Volume(Volume::Value(convert_volume(value))),
0x0A => PatternEffect::Volume(Volume::Slide((value != 0).then(|| {
0x0C => PatternEffect::Volume(Volume::Set(convert_volume(value))),
0x0A => PatternEffect::Volume(Volume::Slide((value != 0).then_some(
if value >= 16 {
convert_volume(value / 16)
} else {
-convert_volume(value)
}
}))),
},
))),
// TODO(nenikitov): Remove dummy effect
0x00 | 0x01 | 0x02 | 0x03 | 0x04 | 0x05 | 0x06 | 0x07 | 0x08 | 0x0A | 0x0B
| 0x0C | 0x0D | 0x0F | 0x14 | 0x15 | 0x16 | 0x1D | 0x1E | 0x1F | 0x20
| 0x21 | 0x22 | 0x24 | 0x25 | 0x2E | 0x2F | 0x30 | 0x31 | 0x32 | 0x33
| 0x34 | 0x35 => PatternEffect::Dummy(kind),
0x00 | 0x03 | 0x04 | 0x05 | 0x06 | 0x07 | 0x08 | 0x0A | 0x0B | 0x0C | 0x0D
| 0x0F | 0x14 | 0x15 | 0x16 | 0x1D | 0x1E | 0x1F | 0x20 | 0x21 | 0x22
| 0x2E | 0x2F | 0x30 | 0x31 | 0x32 | 0x33 | 0x34 | 0x35 => {
PatternEffect::Dummy(kind)
}
// TODO(nenikitov): Add support for other effects
// 0x00 => Self::Arpegio,
// 0x01 => Self::PortaUp,
Expand Down
8 changes: 7 additions & 1 deletion engine/src/asset/sound/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,13 @@ mod tests {
Sound::Effect(_) => None,
})
.collect::<Vec<_>>()[0x9];
//dbg!(&test_music.patterns[0][0x29][5].effects);
let effects = test_music
.orders
.iter()
.flat_map(|p| p.iter().flat_map(|p| p.iter().flat_map(|p| p.effects)))
.flatten()
.collect::<Vec<_>>();
//dbg!(effects);

sounds
.iter()
Expand Down

0 comments on commit 89f67a6

Please sign in to comment.