From 19cb6c5f3d36afd7ec7a640c892e8ee4abcab054 Mon Sep 17 00:00:00 2001 From: Amjad Alsharafi <26300843+Amjad50@users.noreply.github.com> Date: Sat, 21 Dec 2024 19:11:55 +0800 Subject: [PATCH] Added `Dac` to smooth audio signals and remove unneeded code Signed-off-by: Amjad Alsharafi <26300843+Amjad50@users.noreply.github.com> --- plastic_core/src/apu2a03/channel.rs | 73 +++++++++++++++ plastic_core/src/apu2a03/channels/dmc.rs | 2 +- plastic_core/src/apu2a03/channels/noise.rs | 2 +- plastic_core/src/apu2a03/channels/square.rs | 2 +- plastic_core/src/apu2a03/channels/triangle.rs | 2 +- plastic_core/src/apu2a03/envelope.rs | 2 +- plastic_core/src/apu2a03/length_counter.rs | 3 +- plastic_core/src/apu2a03/mod.rs | 55 +++++------- plastic_core/src/apu2a03/tone_source.rs | 88 ------------------- 9 files changed, 100 insertions(+), 129 deletions(-) create mode 100644 plastic_core/src/apu2a03/channel.rs delete mode 100644 plastic_core/src/apu2a03/tone_source.rs diff --git a/plastic_core/src/apu2a03/channel.rs b/plastic_core/src/apu2a03/channel.rs new file mode 100644 index 0000000..a40a59e --- /dev/null +++ b/plastic_core/src/apu2a03/channel.rs @@ -0,0 +1,73 @@ +use serde::{Deserialize, Serialize}; +use std::{ + collections::VecDeque, + ops::{Deref, DerefMut}, +}; + +pub trait APUChannel: Serialize + for<'de> Deserialize<'de> { + fn get_output(&mut self) -> f32; +} + +pub trait TimedAPUChannel: APUChannel { + fn timer_clock(&mut self); +} + +#[derive(Serialize, Deserialize)] +pub struct BufferedChannel { + buffer: VecDeque, +} + +impl BufferedChannel { + pub fn new() -> Self { + Self { + buffer: VecDeque::new(), + } + } + + pub fn recored_sample(&mut self, sample: f32) { + self.buffer.push_back(sample); + } + + pub fn take_buffer(&mut self) -> Vec { + self.buffer.drain(..).collect() + } +} + +#[derive(Serialize, Deserialize)] +#[serde(bound = "C: APUChannel")] +pub struct Dac { + capacitor: f32, + channel: C, +} + +impl Dac { + pub fn new(channel: C) -> Self { + Self { + capacitor: 0., + channel, + } + } + + pub fn dac_output(&mut self) -> f32 { + let dac_in = self.channel.get_output() / 2.2; + let dac_out = dac_in - self.capacitor; + + self.capacitor = dac_in - dac_out * 0.996; + + dac_out + } +} + +impl Deref for Dac { + type Target = C; + + fn deref(&self) -> &Self::Target { + &self.channel + } +} + +impl DerefMut for Dac { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.channel + } +} diff --git a/plastic_core/src/apu2a03/channels/dmc.rs b/plastic_core/src/apu2a03/channels/dmc.rs index afe75c2..7fac646 100644 --- a/plastic_core/src/apu2a03/channels/dmc.rs +++ b/plastic_core/src/apu2a03/channels/dmc.rs @@ -1,4 +1,4 @@ -use super::super::tone_source::{APUChannel, TimedAPUChannel}; +use super::super::channel::{APUChannel, TimedAPUChannel}; use serde::{Deserialize, Serialize}; const DMC_PERIOD_RATES_NTSC: [u16; 0x10] = [ diff --git a/plastic_core/src/apu2a03/channels/noise.rs b/plastic_core/src/apu2a03/channels/noise.rs index 9356825..91c15ee 100644 --- a/plastic_core/src/apu2a03/channels/noise.rs +++ b/plastic_core/src/apu2a03/channels/noise.rs @@ -1,5 +1,5 @@ +use super::super::channel::{APUChannel, TimedAPUChannel}; use super::super::envelope::{EnvelopeGenerator, EnvelopedChannel}; -use super::super::tone_source::{APUChannel, TimedAPUChannel}; use serde::{Deserialize, Serialize}; /// Table for NTSC only diff --git a/plastic_core/src/apu2a03/channels/square.rs b/plastic_core/src/apu2a03/channels/square.rs index c9b8278..075fcaf 100644 --- a/plastic_core/src/apu2a03/channels/square.rs +++ b/plastic_core/src/apu2a03/channels/square.rs @@ -1,6 +1,6 @@ +use super::super::channel::{APUChannel, TimedAPUChannel}; use super::super::envelope::{EnvelopeGenerator, EnvelopedChannel}; use super::super::sequencer::Sequencer; -use super::super::tone_source::{APUChannel, TimedAPUChannel}; use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize)] diff --git a/plastic_core/src/apu2a03/channels/triangle.rs b/plastic_core/src/apu2a03/channels/triangle.rs index eda71f6..9a8670d 100644 --- a/plastic_core/src/apu2a03/channels/triangle.rs +++ b/plastic_core/src/apu2a03/channels/triangle.rs @@ -1,5 +1,5 @@ +use super::super::channel::{APUChannel, TimedAPUChannel}; use super::super::sequencer::Sequencer; -use super::super::tone_source::{APUChannel, TimedAPUChannel}; use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize)] diff --git a/plastic_core/src/apu2a03/envelope.rs b/plastic_core/src/apu2a03/envelope.rs index c91b38b..e96bb28 100644 --- a/plastic_core/src/apu2a03/envelope.rs +++ b/plastic_core/src/apu2a03/envelope.rs @@ -1,4 +1,4 @@ -use super::tone_source::APUChannel; +use super::channel::APUChannel; use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize)] diff --git a/plastic_core/src/apu2a03/length_counter.rs b/plastic_core/src/apu2a03/length_counter.rs index 1865612..34b047b 100644 --- a/plastic_core/src/apu2a03/length_counter.rs +++ b/plastic_core/src/apu2a03/length_counter.rs @@ -1,5 +1,5 @@ +use super::channel::{APUChannel, TimedAPUChannel}; use super::envelope::{EnvelopeGenerator, EnvelopedChannel}; -use super::tone_source::{APUChannel, TimedAPUChannel}; use serde::{Deserialize, Serialize}; const LEGNTH_COUNTER_TABLE: [u8; 0x20] = [ @@ -67,6 +67,7 @@ impl LengthCounter { } #[derive(Serialize, Deserialize)] +#[serde(bound = "C: APUChannel")] pub struct LengthCountedChannel where C: APUChannel, diff --git a/plastic_core/src/apu2a03/mod.rs b/plastic_core/src/apu2a03/mod.rs index 6ebcd05..699784a 100644 --- a/plastic_core/src/apu2a03/mod.rs +++ b/plastic_core/src/apu2a03/mod.rs @@ -1,9 +1,9 @@ mod apu2a03_registers; +mod channel; mod channels; mod envelope; mod length_counter; mod sequencer; -mod tone_source; use crate::common::{ interconnection::{APUCPUConnection, CPUIrqProvider}, @@ -11,12 +11,12 @@ use crate::common::{ CPU_FREQ, }; use apu2a03_registers::Register; +use channel::{APUChannel, BufferedChannel, Dac, TimedAPUChannel}; use channels::{Dmc, NoiseWave, SquarePulse, TriangleWave}; use envelope::EnvelopedChannel; use length_counter::LengthCountedChannel; use serde::{Deserialize, Serialize}; use std::cell::Cell; -use tone_source::{APUChannel, BufferedChannel, TimedAPUChannel}; // for performance /// The sample rate expected to get from [`NES::audio_buffer`](crate::NES::audio_buffer) @@ -29,11 +29,11 @@ const SAMPLES_EVERY_N_APU_CLOCK: f64 = CPU_FREQ / (SAMPLE_RATE as f64); #[derive(Serialize, Deserialize)] pub struct APU2A03 { - square_pulse_1: LengthCountedChannel, - square_pulse_2: LengthCountedChannel, - triangle: LengthCountedChannel, - noise: LengthCountedChannel, - dmc: Dmc, + square_pulse_1: Dac>, + square_pulse_2: Dac>, + triangle: Dac>, + noise: Dac>, + dmc: Dac, buffered_channel: BufferedChannel, @@ -58,14 +58,14 @@ impl APU2A03 { let buffered_channel = BufferedChannel::new(); Self { - square_pulse_1: LengthCountedChannel::new(SquarePulse::new(true)), - square_pulse_2: LengthCountedChannel::new(SquarePulse::new(false)), + square_pulse_1: Dac::new(LengthCountedChannel::new(SquarePulse::new(true))), + square_pulse_2: Dac::new(LengthCountedChannel::new(SquarePulse::new(false))), - triangle: LengthCountedChannel::new(TriangleWave::new()), + triangle: Dac::new(LengthCountedChannel::new(TriangleWave::new())), - noise: LengthCountedChannel::new(NoiseWave::new()), + noise: Dac::new(LengthCountedChannel::new(NoiseWave::new())), - dmc: Dmc::new(), + dmc: Dac::new(Dmc::new()), buffered_channel, @@ -385,11 +385,11 @@ impl APU2A03 { } fn get_mixer_output(&mut self) -> f32 { - let square_pulse_1 = self.square_pulse_1.get_output(); - let square_pulse_2 = self.square_pulse_2.get_output(); - let triangle = self.triangle.get_output(); - let noise = self.noise.get_output(); - let dmc = self.dmc.get_output(); + let square_pulse_1 = self.square_pulse_1.dac_output(); + let square_pulse_2 = self.square_pulse_2.dac_output(); + let triangle = self.triangle.dac_output(); + let noise = self.noise.dac_output(); + let dmc = self.dmc.dac_output(); let pulse_out = if square_pulse_1 == 0. && square_pulse_2 == 0. { 0. @@ -425,28 +425,13 @@ impl APU2A03 { std::cmp::Ordering::Greater => self.wait_reset -= 1, } - // after how many apu clocks a sample should be recorded - let samples_every_n_apu_clock = SAMPLES_EVERY_N_APU_CLOCK + self.offset; - self.sample_counter += 1.; - if self.sample_counter >= samples_every_n_apu_clock { + if self.sample_counter >= SAMPLES_EVERY_N_APU_CLOCK { let output = self.get_mixer_output(); self.buffered_channel.recored_sample(output); - // check for needed change in offset - let change = if self.buffered_channel.get_is_overusing() { - -0.001 - } else if self.buffered_channel.get_is_underusing() { - 0.001 - } else { - 0. - }; - - self.offset += change; - self.buffered_channel.clear_using_flags(); - - self.sample_counter -= samples_every_n_apu_clock; + self.sample_counter -= SAMPLES_EVERY_N_APU_CLOCK; } // clocked on every CPU cycle @@ -463,7 +448,7 @@ impl APU2A03 { // this is clocked in every CPU cycle, so the numbers are multiplied by 2 match self.cycle { - 7455 => { + 7457 => { self.generate_quarter_frame_clock(); } 14913 => { diff --git a/plastic_core/src/apu2a03/tone_source.rs b/plastic_core/src/apu2a03/tone_source.rs deleted file mode 100644 index e3c522d..0000000 --- a/plastic_core/src/apu2a03/tone_source.rs +++ /dev/null @@ -1,88 +0,0 @@ -use serde::{Deserialize, Serialize}; -use std::collections::VecDeque; - -pub trait APUChannel { - fn get_output(&mut self) -> f32; -} - -pub trait TimedAPUChannel: APUChannel { - fn timer_clock(&mut self); -} - -#[derive(Serialize, Deserialize)] -pub struct BufferedChannel { - buffer: VecDeque, - overusing: bool, - underusing: bool, - last: f32, - recent_record: bool, // did a record happen recently - recent_output: bool, // did an output request happen recently - // - // these are used to know if we are now in a bulk recording - // stage, which what happens in the APU -} - -impl BufferedChannel { - pub fn new() -> Self { - Self { - buffer: VecDeque::new(), - overusing: false, - underusing: false, - last: 0., - recent_record: false, - recent_output: false, - } - } - - pub fn get_is_overusing(&self) -> bool { - self.overusing - } - - pub fn get_is_underusing(&self) -> bool { - self.underusing - } - - pub fn clear_using_flags(&mut self) { - self.overusing = false; - self.underusing = false; - } - - pub fn recored_sample(&mut self, sample: f32) { - self.buffer.push_back(sample); - if self.recent_record { - // 60 FPS - if self.buffer.len() > (super::SAMPLE_RATE / 60) as usize && !self.overusing { - self.underusing = true; - } - self.recent_record = false; - } - if self.recent_output { - self.recent_output = false; - self.recent_record = true; - } - } - - pub fn take_buffer(&mut self) -> Vec { - self.buffer.drain(..).collect() - } -} - -impl APUChannel for BufferedChannel { - fn get_output(&mut self) -> f32 { - self.recent_output = true; - - if self.buffer.is_empty() { - self.overusing = true; - self.underusing = false; - - self.last - } else if self.buffer.len() == 1 { - self.last = self.buffer.pop_front().unwrap(); - // this should not reach here, or just one time - // buffer is empty [Problem] - self.last - } else { - self.buffer.pop_front().unwrap() - } - } -}