Skip to content

Commit

Permalink
feat: made sample blending work, f*cking finally
Browse files Browse the repository at this point in the history
  • Loading branch information
nenikitov committed Sep 27, 2024
1 parent 0e7aa34 commit a53917f
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 5 deletions.
38 changes: 34 additions & 4 deletions engine/src/asset/sound/dat/mixer_new.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,14 +56,14 @@ impl PatternEffectLogic for PatternEffect {
}
}

#[derive(Default)]
#[derive(Default, Clone)]
struct PlayerChannelNote {
finetune: Option<FineTune>,
finetune_initial: Option<FineTune>,
on: bool,
}

#[derive(Default)]
#[derive(Default, Clone)]
struct PlayerChannel {
instrument: Option<Rc<TInstrument>>,
sample: Option<Rc<TSample>>,
Expand All @@ -72,11 +72,21 @@ struct PlayerChannel {
effects: [Option<PatternEffect>; 2],
effects_memory: HashMap<PatternEffectMemoryKey, PatternEffect>,

previous: Option<(Box<Self>, f64)>,

pos_sample: f64,
pos_volume_envelope: usize,
}

impl PlayerChannel {
// Too large of a time and samples will audibly blend and play 2 notes at the same time, which sounds weird.
// Too little and transitions between notes will click.
// This is 800 microseconds, which amounts to
// - 13 samples at 16000
// - 35 samples at 44100
// - 38 samples at 48000
const SAMPLE_BLEND: f64 = 0.0008;

fn note_cut(&mut self) {
self.volume = Some(PatternEventVolume::Value(0.));
}
Expand All @@ -87,7 +97,7 @@ impl PlayerChannel {
}

fn generate_sample<T: AudioSamplePoint>(&mut self, step: f64) -> T {
if let Some(instrument) = &self.instrument
let current_sample = if let Some(instrument) = &self.instrument
&& let Some(sample) = &self.sample
&& let Some(volume) = &self.volume
&& let Some(note) = self.note.finetune
Expand All @@ -103,11 +113,31 @@ impl PlayerChannel {

T::from_normalized_f32(value * volume)
} else {
T::from_normalized_f32(0f32)
T::from_normalized_f32(0.)
};

if let Some((previous, position)) = &mut self.previous {
let factor = (*position / Self::SAMPLE_BLEND).min(1.) as f32;
let current_sample = current_sample.into_normalized_f32();
let previous_sample = previous.generate_sample::<T>(step).into_normalized_f32();

*position += step;
if *position >= Self::SAMPLE_BLEND {
self.previous = None

Check warning on line 126 in engine/src/asset/sound/dat/mixer_new.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/mixer_new.rs:126:17 | 126 | self.previous = None | ^^^^^^^^^^^^^^^^^^^^ help: add a `;` here: `self.previous = None;` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#semicolon_if_nothing_returned
}

T::from_normalized_f32(previous_sample + factor * (current_sample - previous_sample))
} else {
current_sample
}
}

fn change_instrument(&mut self, instrument: Option<Rc<TInstrument>>) {
// In tracker music, every instrument change is a state reset
// Previous state is kept to subtly blend in notes to remove clicks.
self.previous = None;
self.previous = Some((Box::new(self.clone()), 0.));

if let Some(instrument) = instrument {
self.instrument = Some(instrument);
self.pos_reset();
Expand Down
2 changes: 1 addition & 1 deletion engine/src/asset/sound/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ mod tests {

// TODO(nenikitov): Remove this debug code
{
let i = 0x3;
let i = 0x8;
let song = sounds
.iter()
.filter_map(|s| match s {
Expand Down

0 comments on commit a53917f

Please sign in to comment.