chore(snd): reimplement NR52 & implement sampling for ch2
Also add rodio as dependency for audio
This commit is contained in:
@@ -233,7 +233,7 @@ impl BusIo for Bus {
|
||||
0x23 => self.snd.ch4.freq_data(),
|
||||
0x24 => self.snd.ctrl.channel.into(),
|
||||
0x25 => self.snd.ctrl.output.into(),
|
||||
0x26 => self.snd.ctrl.status.into(),
|
||||
0x26 => self.snd.ctrl.status(&self.snd),
|
||||
0x30..=0x3F => self.snd.ch3.wave_ram[addr as usize - 0xFF30],
|
||||
0x40 => self.ppu.ctrl.into(),
|
||||
0x41 => self.ppu.stat.into(),
|
||||
@@ -351,7 +351,7 @@ impl BusIo for Bus {
|
||||
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?
|
||||
0x26 => self.snd.ctrl.set_status(byte), // FIXME: Should we control which bytes are written to here?
|
||||
0x30..=0x3F => self.snd.ch3.wave_ram[addr as usize - 0xFF30] = byte,
|
||||
0x40 => self.ppu.ctrl = byte.into(),
|
||||
0x41 => self.ppu.stat.update(byte),
|
||||
|
55
src/sound.rs
55
src/sound.rs
@@ -23,6 +23,9 @@ impl Sound {
|
||||
pub(crate) fn clock(&mut self, div: u16) {
|
||||
use FrameSequencerState::*;
|
||||
|
||||
// Decrement Channel 2 Frequency Timer
|
||||
let ch2_amplitude = self.ch2.clock();
|
||||
|
||||
// the 5th bit of the high byte
|
||||
let bit_5 = (div >> 13 & 0x01) as u8;
|
||||
|
||||
@@ -213,8 +216,25 @@ pub(crate) struct SoundControl {
|
||||
pub(crate) channel: ChannelControl,
|
||||
/// 0xFF25 | NR51 - Selection of Sound output terminal
|
||||
pub(crate) output: SoundOutput,
|
||||
|
||||
enabled: bool,
|
||||
}
|
||||
|
||||
impl SoundControl {
|
||||
/// 0xFF26 | NR52 - Sound On/Off
|
||||
pub(crate) status: SoundStatus,
|
||||
pub fn status(&self, snd: &Sound) -> u8 {
|
||||
(self.enabled as u8) << 7
|
||||
| (snd.ch4.enabled as u8) << 3
|
||||
| (snd.ch3.enabled as u8) << 2
|
||||
| (snd.ch2.enabled as u8) << 1
|
||||
| snd.ch1.enabled as u8
|
||||
}
|
||||
|
||||
/// 0xFF26 | NR52 - Sound On/Off
|
||||
pub fn set_status(&mut self, byte: u8) {
|
||||
// TODO: Should all channel enabled fields be disabled when this is reset?
|
||||
self.enabled = (byte >> 7) & 0x01 == 0x01;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: What to do about the separation of freq bits
|
||||
@@ -467,10 +487,25 @@ pub(crate) struct Channel2 {
|
||||
// Length Functionality
|
||||
length_timer: u16,
|
||||
|
||||
freq_timer: u16,
|
||||
duty_pos: u8,
|
||||
|
||||
enabled: bool,
|
||||
}
|
||||
|
||||
impl Channel2 {
|
||||
fn clock(&mut self) -> u8 {
|
||||
self.freq_timer -= 1;
|
||||
|
||||
if self.freq_timer == 0 {
|
||||
// TODO: Why is this 2048?
|
||||
self.freq_timer = (2048 - self.frequency()) * 4;
|
||||
self.duty_pos = (self.duty_pos + 1) % 8;
|
||||
}
|
||||
|
||||
self.duty.wave_pattern().amplitude(self.duty_pos)
|
||||
}
|
||||
|
||||
/// 0xFF16 | NR21 - Channel 2 Sound length / Wave Pattern Duty
|
||||
pub(crate) fn duty(&self) -> u8 {
|
||||
self.duty.into()
|
||||
@@ -502,6 +537,10 @@ impl Channel2 {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn frequency(&self) -> u16 {
|
||||
(self.freq_hi.freq_bits() as u16) << 8 | self.freq_lo as u16
|
||||
}
|
||||
}
|
||||
|
||||
bitfield! {
|
||||
@@ -605,6 +644,20 @@ pub enum WavePattern {
|
||||
ThreeQuarters = 3, // 75% ( ______--______--______-- )
|
||||
}
|
||||
|
||||
impl WavePattern {
|
||||
pub const fn amplitude(&self, index: u8) -> u8 {
|
||||
use WavePattern::*;
|
||||
let i = 7 - index; // an index of 0 should get the highest bit
|
||||
|
||||
match *self {
|
||||
OneEighth => (0b00000001 >> i) & 0x01,
|
||||
OneQuarter => (0b10000001 >> i) & 0x01,
|
||||
OneHalf => (0b10000111 >> i) & 0x01,
|
||||
ThreeQuarters => (0b01111110 >> i) & 0x01,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for WavePattern {
|
||||
fn default() -> Self {
|
||||
Self::OneEighth // Rationale: OneEighth is 0x00
|
||||
|
Reference in New Issue
Block a user