Skip to content

Commit

Permalink
Support volume sweep frequency change while the channel is active
Browse files Browse the repository at this point in the history
  • Loading branch information
YushiOMOTE committed Aug 3, 2024
1 parent a4bcb53 commit 081ca3f
Show file tree
Hide file tree
Showing 3 changed files with 124 additions and 13 deletions.
115 changes: 104 additions & 11 deletions core/src/apu/envelope.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,44 +5,137 @@ use super::frame_sequencer::Frame;
#[derive(Debug, Clone)]
pub struct Envelope {
amp: usize,
inc: bool,
increase: bool,
timer: Timer,
interval: usize,
force_tick: bool,
active: bool,
}

impl Envelope {
pub fn new() -> Self {
Self {
amp: 0,
inc: false,
increase: false,
timer: Timer::enabled(),
interval: 0,
force_tick: false,
active: false,
}
}

pub fn update(&mut self, amp: usize, count: usize, inc: bool) {
pub fn trigger(&mut self, amp: usize, interval: usize, increase: bool) {
self.amp = amp;
self.inc = inc;
self.timer.reset();
self.timer.set_interval(count);
self.increase = increase;
self.interval = interval;

self.reload_timer();

self.active = true;
}

pub fn reload(&mut self, _amp: usize, interval: usize, increase: bool) {
if !self.active {
return;
}

// Definition of "enabled" here is non-zero interval.
let old_enabled = self.interval != 0;
let new_enabled = interval != 0;

// The envelope speed can be changed
// while it's active, and the change takes effect after
// the next time it ticks.
self.interval = interval;

if !old_enabled && new_enabled {
// Enabling the envelope takes effect instantly.
self.reload_timer();

// For simulating the APU bug (see below)
self.force_tick = true;
} else if old_enabled && !new_enabled {
// Disabling the envelope takes effect instantly.
self.reload_timer();

self.force_tick = false;
}

// Zombie mode effect (1)
// If the old envelope period was zero and the envelope is still doing automatic updates, volume is incremented by 1,
// otherwise if the envelope was in subtract mode, volume is incremented by 2.
if !old_enabled && self.amp < 15 && self.increase {
self.increase_amp();
}

// Zombie mode effect (2)
// If the mode was changed (add to subtract or subtract to add), volume is set to 16-volume.
if self.increase != increase {
self.amp = 15;
self.increase = increase;
}
}

pub fn step(&mut self, frame: Frame) {
match frame.switched() {
Some(7) => {}
Some(1) | Some(3) | Some(5) if self.force_tick => {
// Enabling the envelope trigger an APU bug - in the next
// *even* DIV-APU tick, the APU will tick the volume
// envelope of that apropriate channel, even if it would
// not tick volume envelope at that tick otherwise
self.tick();

return;
}
_ => return,
}

if !self.timer.tick() {
return;
}

self.amp = if self.inc {
self.amp.saturating_add(1).min(15)
} else {
self.amp.saturating_sub(1)
};
self.tick();
}

pub fn amp(&self) -> usize {
self.amp
}

pub fn deactivate(&mut self) {
self.active = false;
}

fn tick(&mut self) {
self.force_tick = false;

self.reload_timer();

self.update_amp();
}

fn update_amp(&mut self) {
if self.increase {
self.increase_amp();
} else {
self.decrease_amp();
}
}

fn increase_amp(&mut self) {
self.amp = self.amp.saturating_add(1).min(15);
}

fn decrease_amp(&mut self) {
self.amp = self.amp.saturating_sub(1);
}

fn reload_timer(&mut self) {
self.timer.reset();
self.timer.set_interval(self.interval);
}

pub fn power_off(&mut self) {
self.active = false;
}
}
11 changes: 10 additions & 1 deletion core/src/apu/noise.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,11 +106,15 @@ impl Noise {

self.nr42 = Nr42::from_bits(value);

self.envelope
.reload(self.nr42.init(), self.nr42.count(), self.nr42.increase());

if self.nr42.init() > 0 || self.nr42.increase() {
self.dac.power_on();
} else {
self.dac.power_off();
self.length_counter.deactivate();
self.envelope.deactivate();
}
}

Expand Down Expand Up @@ -149,13 +153,17 @@ impl Noise {
self.reload_initial_timer(self.nr43.step());
self.lfsr.trigger(self.nr43.step());
self.envelope
.update(self.nr42.init(), self.nr42.count(), self.nr42.increase());
.trigger(self.nr42.init(), self.nr42.count(), self.nr42.increase());
}

self.nr44.trigger()
}

pub fn step(&mut self, cycles: usize, frame: Frame) {
if !self.is_active() {
self.envelope.deactivate();
}

self.length_counter.step(frame);
self.envelope.step(frame);

Expand Down Expand Up @@ -241,6 +249,7 @@ impl Noise {
self.nr44 = Nr44::default();

self.length_counter.power_off();
self.envelope.power_off();

self.dac.power_off();
}
Expand Down
11 changes: 10 additions & 1 deletion core/src/apu/tone.rs
Original file line number Diff line number Diff line change
Expand Up @@ -162,11 +162,15 @@ impl Tone {

self.nr12 = Nr12::from_bits(value);

self.envelope
.reload(self.nr12.init(), self.nr12.count(), self.nr12.increase());

if self.nr12.init() > 0 || self.nr12.increase() {
self.dac.power_on();
} else {
self.dac.power_off();
self.length_counter.deactivate();
self.envelope.deactivate();
}
}

Expand Down Expand Up @@ -218,7 +222,7 @@ impl Tone {

self.load_initial_timer();
self.envelope
.update(self.nr12.init(), self.nr12.count(), self.nr12.increase());
.trigger(self.nr12.init(), self.nr12.count(), self.nr12.increase());
self.current_duty = self.nr11.wave_duty();
}

Expand Down Expand Up @@ -253,11 +257,16 @@ impl Tone {
self.index = 0;

self.length_counter.power_off();
self.envelope.power_off();

self.dac.power_off();
}

pub fn step(&mut self, cycles: usize, frame: Frame) {
if !self.is_active() {
self.envelope.deactivate();
}

if let Some(sweep) = self.sweep.as_mut() {
if let Some(new_freq) = sweep.step(frame) {
self.freq = Freq::from_value(new_freq);
Expand Down

0 comments on commit 081ca3f

Please sign in to comment.