feat(snd): implement sweep behaviour
This commit is contained in:
parent
29237f0a2c
commit
d2e37da2b0
149
src/sound.rs
149
src/sound.rs
|
@ -33,10 +33,50 @@ impl Sound {
|
||||||
|
|
||||||
match self.frame_seq_state {
|
match self.frame_seq_state {
|
||||||
Step0Length => todo!(),
|
Step0Length => todo!(),
|
||||||
Step2LengthAndSweep => todo!(),
|
Step2LengthAndSweep => {
|
||||||
|
// Handle Length
|
||||||
|
self.handle_sweep();
|
||||||
|
}
|
||||||
Step4Length => todo!(),
|
Step4Length => todo!(),
|
||||||
Step6LengthAndSweep => todo!(),
|
Step6LengthAndSweep => {
|
||||||
Step7VolumeEnvelope => {
|
// Handle Length
|
||||||
|
self.handle_sweep();
|
||||||
|
}
|
||||||
|
Step7VolumeEnvelope => self.handle_volume(),
|
||||||
|
Step1Nothing | Step3Nothing | Step5Nothing => {}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.div_prev = Some(bit_5);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_sweep(&mut self) {
|
||||||
|
if self.ch1.sweep_timer > 0 {
|
||||||
|
self.ch1.sweep_timer -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.ch1.sweep_timer == 0 {
|
||||||
|
self.ch1.sweep_timer = if self.ch1.sweep.time() != 0 {
|
||||||
|
self.ch1.sweep.time()
|
||||||
|
} else {
|
||||||
|
8
|
||||||
|
};
|
||||||
|
|
||||||
|
if self.ch1.sweep_enabled && self.ch1.sweep.time() != 0 {
|
||||||
|
let new_freq = self.ch1.calc_sweep_freq();
|
||||||
|
|
||||||
|
if new_freq <= 2047 && self.ch1.sweep.shift_count() != 0 {
|
||||||
|
self.ch1.set_frequency(new_freq);
|
||||||
|
self.ch1.shadow_freq = new_freq & 0x07FF;
|
||||||
|
|
||||||
|
let _ = self.ch1.calc_sweep_freq();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_volume(&mut self) {
|
||||||
use EnvelopeDirection::*;
|
use EnvelopeDirection::*;
|
||||||
// Channels 1, 2 and 4 have Volume Envelopes
|
// Channels 1, 2 and 4 have Volume Envelopes
|
||||||
|
|
||||||
|
@ -49,12 +89,8 @@ impl Sound {
|
||||||
self.ch1.period_timer = self.ch1.envelope.sweep_count();
|
self.ch1.period_timer = self.ch1.envelope.sweep_count();
|
||||||
|
|
||||||
match self.ch1.envelope.direction() {
|
match self.ch1.envelope.direction() {
|
||||||
Decrease if self.ch1.current_volume > 0x00 => {
|
Decrease if self.ch1.current_volume > 0x00 => self.ch1.current_volume -= 1,
|
||||||
self.ch1.current_volume -= 1
|
Increase if self.ch1.current_volume < 0x0F => self.ch1.current_volume += 1,
|
||||||
}
|
|
||||||
Increase if self.ch1.current_volume < 0x0F => {
|
|
||||||
self.ch1.current_volume += 1
|
|
||||||
}
|
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -69,12 +105,8 @@ impl Sound {
|
||||||
self.ch2.period_timer = self.ch2.envelope.sweep_count();
|
self.ch2.period_timer = self.ch2.envelope.sweep_count();
|
||||||
|
|
||||||
match self.ch2.envelope.direction() {
|
match self.ch2.envelope.direction() {
|
||||||
Decrease if self.ch2.current_volume > 0x00 => {
|
Decrease if self.ch2.current_volume > 0x00 => self.ch2.current_volume -= 1,
|
||||||
self.ch2.current_volume -= 1
|
Increase if self.ch2.current_volume < 0x0F => self.ch2.current_volume += 1,
|
||||||
}
|
|
||||||
Increase if self.ch2.current_volume < 0x0F => {
|
|
||||||
self.ch2.current_volume += 1
|
|
||||||
}
|
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -89,24 +121,13 @@ impl Sound {
|
||||||
self.ch4.period_timer = self.ch4.envelope.sweep_count();
|
self.ch4.period_timer = self.ch4.envelope.sweep_count();
|
||||||
|
|
||||||
match self.ch4.envelope.direction() {
|
match self.ch4.envelope.direction() {
|
||||||
Decrease if self.ch4.current_volume > 0x00 => {
|
Decrease if self.ch4.current_volume > 0x00 => self.ch4.current_volume -= 1,
|
||||||
self.ch4.current_volume -= 1
|
Increase if self.ch4.current_volume < 0x0F => self.ch4.current_volume += 1,
|
||||||
}
|
|
||||||
Increase if self.ch4.current_volume < 0x0F => {
|
|
||||||
self.ch4.current_volume += 1
|
|
||||||
}
|
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Step1Nothing | Step3Nothing | Step5Nothing => {}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.div_prev = Some(bit_5);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
@ -160,8 +181,8 @@ bitfield! {
|
||||||
pub struct FrequencyHigh(u8);
|
pub struct FrequencyHigh(u8);
|
||||||
impl Debug;
|
impl Debug;
|
||||||
initial, _set_initial: 7;
|
initial, _set_initial: 7;
|
||||||
pub from into FrequencyType, get_freq_type, set_freq_type: 6;
|
from into FrequencyType, freq_type, set_freq_type: 6, 6;
|
||||||
pub _, set_freq_bits: 2, 0;
|
freq_bits, set_freq_bits: 2, 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FrequencyHigh {
|
impl FrequencyHigh {
|
||||||
|
@ -274,6 +295,11 @@ pub(crate) struct Channel1 {
|
||||||
// Envelope Functionality
|
// Envelope Functionality
|
||||||
period_timer: u8,
|
period_timer: u8,
|
||||||
current_volume: u8,
|
current_volume: u8,
|
||||||
|
|
||||||
|
// Sweep Functionality
|
||||||
|
sweep_timer: u8,
|
||||||
|
shadow_freq: u16,
|
||||||
|
sweep_enabled: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Channel1 {
|
impl Channel1 {
|
||||||
|
@ -286,10 +312,55 @@ impl Channel1 {
|
||||||
pub(crate) fn set_freq_hi(&mut self, byte: u8) {
|
pub(crate) fn set_freq_hi(&mut self, byte: u8) {
|
||||||
self.freq_hi = byte.into();
|
self.freq_hi = byte.into();
|
||||||
|
|
||||||
|
// Envelope Behaviour during trigger event
|
||||||
if self.freq_hi.initial() {
|
if self.freq_hi.initial() {
|
||||||
self.period_timer = self.envelope.sweep_count();
|
self.period_timer = self.envelope.sweep_count();
|
||||||
self.current_volume = self.envelope.init_vol();
|
self.current_volume = self.envelope.init_vol();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sweep behaviour during trigger event
|
||||||
|
self.shadow_freq = self.frequency();
|
||||||
|
self.sweep_timer = if self.sweep.time() != 0 {
|
||||||
|
self.sweep.time()
|
||||||
|
} else {
|
||||||
|
8
|
||||||
|
};
|
||||||
|
|
||||||
|
if self.sweep.time() != 0 || self.sweep.shift_count() != 0 {
|
||||||
|
self.sweep_enabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.sweep.shift_count() != 0 {
|
||||||
|
let _ = self.calc_sweep_freq();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn calc_sweep_freq(&mut self) -> u16 {
|
||||||
|
use SweepDirection::*;
|
||||||
|
let shifted_shadow_freq = self.shadow_freq >> self.sweep.shift_count();
|
||||||
|
|
||||||
|
let new_freq = match self.sweep.direction() {
|
||||||
|
Increase => self.shadow_freq + shifted_shadow_freq,
|
||||||
|
Decrease => self.shadow_freq - shifted_shadow_freq,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Overflow check
|
||||||
|
if new_freq > 2047 {
|
||||||
|
todo!("Frequency failed the overflow check. Disable the channel");
|
||||||
|
}
|
||||||
|
|
||||||
|
new_freq
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_frequency(&mut self, word: u16) {
|
||||||
|
let freq_bits = word & 0x07FF;
|
||||||
|
self.freq_lo = (freq_bits & 0x00FF) as u8;
|
||||||
|
self.freq_hi
|
||||||
|
.set_freq_bits(((freq_bits & 0x0700) >> 8) as u8);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn frequency(&self) -> u16 {
|
||||||
|
(self.freq_hi.freq_bits() as u16) << 8 | self.freq_lo as u16
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -297,7 +368,7 @@ bitfield! {
|
||||||
pub struct Sweep(u8);
|
pub struct Sweep(u8);
|
||||||
impl Debug;
|
impl Debug;
|
||||||
time, set_time: 6, 4;
|
time, set_time: 6, 4;
|
||||||
from into SweepChange, change, set_change: 3, 3;
|
from into SweepDirection, direction, set_direction: 3, 3;
|
||||||
shift_count, set_shift_count: 2, 0;
|
shift_count, set_shift_count: 2, 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -327,23 +398,23 @@ impl From<Sweep> for u8 {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub(crate) enum SweepChange {
|
pub(crate) enum SweepDirection {
|
||||||
Additive = 0,
|
Increase = 0,
|
||||||
Subtractive = 1,
|
Decrease = 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<u8> for SweepChange {
|
impl From<u8> for SweepDirection {
|
||||||
fn from(byte: u8) -> Self {
|
fn from(byte: u8) -> Self {
|
||||||
match byte & 0x01 {
|
match byte & 0x01 {
|
||||||
0b00 => Self::Additive,
|
0b00 => Self::Increase,
|
||||||
0b01 => Self::Subtractive,
|
0b01 => Self::Decrease,
|
||||||
_ => unreachable!("{:04X} is not a valid value for SweepChange", byte),
|
_ => unreachable!("{:04X} is not a valid value for SweepChange", byte),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<SweepChange> for u8 {
|
impl From<SweepDirection> for u8 {
|
||||||
fn from(change: SweepChange) -> Self {
|
fn from(change: SweepDirection) -> Self {
|
||||||
change as u8
|
change as u8
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue