feat(snd): implement volume envelope in ch1, 2 and 4
This commit is contained in:
parent
1bc5de7cff
commit
0c843b9ca9
14
src/bus.rs
14
src/bus.rs
|
@ -69,7 +69,7 @@ impl Bus {
|
|||
pub(crate) fn clock(&mut self) {
|
||||
self.ppu.clock();
|
||||
self.timer.clock();
|
||||
self.snd.clock();
|
||||
self.snd.clock(self.timer.divider);
|
||||
self.clock_dma();
|
||||
}
|
||||
|
||||
|
@ -223,10 +223,10 @@ impl BusIo for Bus {
|
|||
0x10 => self.snd.ch1.sweep.into(),
|
||||
0x11 => self.snd.ch1.duty.into(),
|
||||
0x12 => self.snd.ch1.envelope.into(),
|
||||
0x14 => self.snd.ch1.freq_hi.into(),
|
||||
0x14 => self.snd.ch1.freq_hi(),
|
||||
0x16 => self.snd.ch2.duty.into(),
|
||||
0x17 => self.snd.ch2.envelope.into(),
|
||||
0x19 => self.snd.ch2.freq_hi.into(),
|
||||
0x19 => self.snd.ch2.freq_hi(),
|
||||
0x1A => self.snd.ch3.enabled(),
|
||||
0x1B => self.snd.ch3.len,
|
||||
0x1C => self.snd.ch3.volume(),
|
||||
|
@ -234,7 +234,7 @@ impl BusIo for Bus {
|
|||
0x20 => self.snd.ch4.len(),
|
||||
0x21 => self.snd.ch4.envelope.into(),
|
||||
0x22 => self.snd.ch4.poly.into(),
|
||||
0x23 => self.snd.ch4.freq_data.into(),
|
||||
0x23 => self.snd.ch4.freq_data(),
|
||||
0x24 => self.snd.ctrl.channel.into(),
|
||||
0x25 => self.snd.ctrl.output.into(),
|
||||
0x26 => self.snd.ctrl.status.into(),
|
||||
|
@ -338,11 +338,11 @@ impl BusIo for Bus {
|
|||
0x11 => self.snd.ch1.duty = byte.into(),
|
||||
0x12 => self.snd.ch1.envelope = byte.into(),
|
||||
0x13 => self.snd.ch1.freq_lo = byte,
|
||||
0x14 => self.snd.ch1.freq_hi = byte.into(),
|
||||
0x14 => self.snd.ch1.set_freq_hi(byte),
|
||||
0x16 => self.snd.ch2.duty = byte.into(),
|
||||
0x17 => self.snd.ch2.envelope = byte.into(),
|
||||
0x18 => self.snd.ch2.freq_lo = byte,
|
||||
0x19 => self.snd.ch2.freq_hi = byte.into(),
|
||||
0x19 => self.snd.ch2.set_freq_hi(byte),
|
||||
0x1A => self.snd.ch3.set_enabled(byte),
|
||||
0x1B => self.snd.ch3.len = byte,
|
||||
0x1C => self.snd.ch3.set_volume(byte),
|
||||
|
@ -351,7 +351,7 @@ impl BusIo for Bus {
|
|||
0x20 => self.snd.ch4.set_len(byte),
|
||||
0x21 => self.snd.ch4.envelope = byte.into(),
|
||||
0x22 => self.snd.ch4.poly = byte.into(),
|
||||
0x23 => self.snd.ch4.freq_data = byte.into(),
|
||||
0x23 => self.snd.ch4.set_freq_data(byte),
|
||||
0x24 => self.snd.ctrl.channel = byte.into(),
|
||||
0x25 => self.snd.ctrl.output = byte.into(),
|
||||
0x26 => self.snd.ctrl.status = byte.into(), // FIXME: Should we control which bytes are written to here?
|
||||
|
|
216
src/sound.rs
216
src/sound.rs
|
@ -13,11 +13,134 @@ pub(crate) struct Sound {
|
|||
pub(crate) ch3: Channel3,
|
||||
/// Noise
|
||||
pub(crate) ch4: Channel4,
|
||||
|
||||
// Frame Sequencer
|
||||
frame_seq_state: FrameSequencerState,
|
||||
div_prev: Option<u8>,
|
||||
}
|
||||
|
||||
impl Sound {
|
||||
pub(crate) fn clock(&mut self) {
|
||||
//
|
||||
pub(crate) fn clock(&mut self, div: u16) {
|
||||
use FrameSequencerState::*;
|
||||
|
||||
// the 5th bit of the high byte
|
||||
let bit_5 = (div >> 13 & 0x01) as u8;
|
||||
|
||||
if let Some(0x01) = self.div_prev {
|
||||
if bit_5 == 0x00 {
|
||||
// Falling Edge, step the Frame Sequencer
|
||||
self.frame_seq_state.step();
|
||||
|
||||
match self.frame_seq_state {
|
||||
Step0Length => todo!(),
|
||||
Step2LengthAndSweep => todo!(),
|
||||
Step4Length => todo!(),
|
||||
Step6LengthAndSweep => todo!(),
|
||||
Step7VolumeEnvelope => {
|
||||
use EnvelopeDirection::*;
|
||||
// Channels 1, 2 and 4 have Volume Envelopes
|
||||
|
||||
if self.ch1.envelope.sweep_count() != 0 {
|
||||
if self.ch1.period_timer > 0 {
|
||||
self.ch1.period_timer -= 1;
|
||||
}
|
||||
|
||||
if self.ch1.period_timer == 0 {
|
||||
self.ch1.period_timer = self.ch1.envelope.sweep_count();
|
||||
|
||||
match self.ch1.envelope.direction() {
|
||||
Decrease if self.ch1.current_volume > 0x00 => {
|
||||
self.ch1.current_volume -= 1
|
||||
}
|
||||
Increase if self.ch1.current_volume < 0x0F => {
|
||||
self.ch1.current_volume += 1
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if self.ch2.envelope.sweep_count() != 0 {
|
||||
if self.ch2.period_timer > 0 {
|
||||
self.ch2.period_timer -= 1;
|
||||
}
|
||||
|
||||
if self.ch2.period_timer == 0 {
|
||||
self.ch2.period_timer = self.ch2.envelope.sweep_count();
|
||||
|
||||
match self.ch2.envelope.direction() {
|
||||
Decrease if self.ch2.current_volume > 0x00 => {
|
||||
self.ch2.current_volume -= 1
|
||||
}
|
||||
Increase if self.ch2.current_volume < 0x0F => {
|
||||
self.ch2.current_volume += 1
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if self.ch4.envelope.sweep_count() != 0 {
|
||||
if self.ch4.period_timer > 0 {
|
||||
self.ch4.period_timer -= 1;
|
||||
}
|
||||
|
||||
if self.ch4.period_timer == 0 {
|
||||
self.ch4.period_timer = self.ch4.envelope.sweep_count();
|
||||
|
||||
match self.ch4.envelope.direction() {
|
||||
Decrease if self.ch4.current_volume > 0x00 => {
|
||||
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)]
|
||||
enum FrameSequencerState {
|
||||
Step0Length,
|
||||
Step1Nothing,
|
||||
Step2LengthAndSweep,
|
||||
Step3Nothing,
|
||||
Step4Length,
|
||||
Step5Nothing,
|
||||
Step6LengthAndSweep,
|
||||
Step7VolumeEnvelope,
|
||||
}
|
||||
|
||||
impl FrameSequencerState {
|
||||
pub fn step(&mut self) {
|
||||
use FrameSequencerState::*;
|
||||
|
||||
*self = match *self {
|
||||
Step0Length => Step1Nothing,
|
||||
Step1Nothing => Step2LengthAndSweep,
|
||||
Step2LengthAndSweep => Step3Nothing,
|
||||
Step3Nothing => Step4Length,
|
||||
Step4Length => Step5Nothing,
|
||||
Step5Nothing => Step6LengthAndSweep,
|
||||
Step6LengthAndSweep => Step7VolumeEnvelope,
|
||||
Step7VolumeEnvelope => Step0Length,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for FrameSequencerState {
|
||||
fn default() -> Self {
|
||||
Self::Step0Length
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -36,11 +159,17 @@ pub(crate) struct SoundControl {
|
|||
bitfield! {
|
||||
pub struct FrequencyHigh(u8);
|
||||
impl Debug;
|
||||
pub _, set_initial: 7;
|
||||
initial, _set_initial: 7;
|
||||
pub from into FrequencyType, get_freq_type, set_freq_type: 6;
|
||||
pub _, set_freq_bits: 2, 0;
|
||||
}
|
||||
|
||||
impl FrequencyHigh {
|
||||
pub(crate) fn set_initial(&mut self, value: bool, ch1: &mut Channel1) {
|
||||
self._set_initial(value);
|
||||
}
|
||||
}
|
||||
|
||||
impl Copy for FrequencyHigh {}
|
||||
impl Clone for FrequencyHigh {
|
||||
fn clone(&self) -> Self {
|
||||
|
@ -140,7 +269,28 @@ pub(crate) struct Channel1 {
|
|||
/// 0xFF13 | NR13 - Channel 1 Frequency low (lower 8 bits only)
|
||||
pub(crate) freq_lo: u8,
|
||||
/// 0xFF14 | NR14 - Channel 1 Frequency high
|
||||
pub(crate) freq_hi: FrequencyHigh,
|
||||
freq_hi: FrequencyHigh,
|
||||
|
||||
// Envelope Functionality
|
||||
period_timer: u8,
|
||||
current_volume: u8,
|
||||
}
|
||||
|
||||
impl Channel1 {
|
||||
/// 0xFF14 | NR14 - Channel 1 Frequency high
|
||||
pub(crate) fn freq_hi(&self) -> u8 {
|
||||
self.freq_hi.into()
|
||||
}
|
||||
|
||||
/// 0xFF14 | NR14 - Channel 1 Frequency high
|
||||
pub(crate) fn set_freq_hi(&mut self, byte: u8) {
|
||||
self.freq_hi = byte.into();
|
||||
|
||||
if self.freq_hi.initial() {
|
||||
self.period_timer = self.envelope.sweep_count();
|
||||
self.current_volume = self.envelope.init_vol();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bitfield! {
|
||||
|
@ -207,14 +357,35 @@ pub(crate) struct Channel2 {
|
|||
/// 0xFF18 | NR23 - Channel 2 Frequency low (lower 8 bits only)
|
||||
pub(crate) freq_lo: u8,
|
||||
/// 0xFF19 | NR24 - Channel 2 Frequency high
|
||||
pub(crate) freq_hi: FrequencyHigh,
|
||||
freq_hi: FrequencyHigh,
|
||||
|
||||
// Envelope Functionality
|
||||
period_timer: u8,
|
||||
current_volume: u8,
|
||||
}
|
||||
|
||||
impl Channel2 {
|
||||
/// 0xFF19 | NR24 - Channel 2 Frequency high
|
||||
pub(crate) fn freq_hi(&self) -> u8 {
|
||||
self.freq_hi.into()
|
||||
}
|
||||
|
||||
/// 0xFF19 | NR24 - Channel 2 Frequency high
|
||||
pub(crate) fn set_freq_hi(&mut self, byte: u8) {
|
||||
self.freq_hi = byte.into();
|
||||
|
||||
if self.freq_hi.initial() {
|
||||
self.period_timer = self.envelope.sweep_count();
|
||||
self.current_volume = self.envelope.init_vol();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bitfield! {
|
||||
pub struct VolumeEnvelope(u8);
|
||||
impl Debug;
|
||||
pub init_vol, set_init_vol: 7, 4;
|
||||
pub from into EnvelopeDirection, direction, set_direction: 3;
|
||||
pub from into EnvelopeDirection, direction, set_direction: 3, 3;
|
||||
pub sweep_count, set_sweep_count: 2, 0;
|
||||
}
|
||||
|
||||
|
@ -244,7 +415,7 @@ impl From<VolumeEnvelope> for u8 {
|
|||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
enum EnvelopeDirection {
|
||||
pub enum EnvelopeDirection {
|
||||
Decrease = 0,
|
||||
Increase = 1,
|
||||
}
|
||||
|
@ -259,6 +430,12 @@ impl From<u8> for EnvelopeDirection {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<EnvelopeDirection> for u8 {
|
||||
fn from(direction: EnvelopeDirection) -> Self {
|
||||
direction as u8
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for EnvelopeDirection {
|
||||
fn default() -> Self {
|
||||
Self::Decrease
|
||||
|
@ -393,7 +570,28 @@ pub(crate) struct Channel4 {
|
|||
/// 0xFF22 | NR43 - Chanel 4 Polynomial Counter
|
||||
pub(crate) poly: PolynomialCounter,
|
||||
/// 0xFF23 | NR44 - Channel 4 Counter / Consecutive Selector and Restart
|
||||
pub(crate) freq_data: Channel4Frequency,
|
||||
freq_data: Channel4Frequency,
|
||||
|
||||
// Envelope Functionality
|
||||
period_timer: u8,
|
||||
current_volume: u8,
|
||||
}
|
||||
|
||||
impl Channel4 {
|
||||
/// 0xFF23 | NR44 - Channel 4 Counter / Consecutive Selector and Restart
|
||||
pub(crate) fn freq_data(&self) -> u8 {
|
||||
self.freq_data.into()
|
||||
}
|
||||
|
||||
/// 0xFF23 | NR44 - Channel 4 Counter / Consecutive Selector and Restart
|
||||
pub(crate) fn set_freq_data(&mut self, byte: u8) {
|
||||
self.freq_data = byte.into();
|
||||
|
||||
if self.freq_data.initial() {
|
||||
self.period_timer = self.envelope.sweep_count();
|
||||
self.current_volume = self.envelope.init_vol();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Channel4 {
|
||||
|
@ -464,7 +662,7 @@ impl From<CounterWidth> for u8 {
|
|||
bitfield! {
|
||||
pub struct Channel4Frequency(u8);
|
||||
impl Debug;
|
||||
_, set_initial: 7;
|
||||
initial, set_initial: 7;
|
||||
from into FrequencyType, freq_type, set_freq_type: 6, 6;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue