chore(apu): improve code organization
continuous-integration/drone/push Build is passing
Details
continuous-integration/drone/push Build is passing
Details
This commit is contained in:
parent
5d64e539a7
commit
360a9a7b65
178
src/apu.rs
178
src/apu.rs
|
@ -5,7 +5,7 @@ use types::ch1::{Sweep, SweepDirection};
|
|||
use types::ch3::Volume as Ch3Volume;
|
||||
use types::ch4::{CounterWidth, Frequency as Ch4Frequency, PolynomialCounter};
|
||||
use types::common::{EnvelopeDirection, FrequencyHigh, SoundDuty, VolumeEnvelope};
|
||||
use types::fs::{FrameSequencer, FrameSequencerState};
|
||||
use types::fs::{FrameSequencer, State as FrameSequencerState};
|
||||
use types::{ChannelControl, SoundOutput};
|
||||
|
||||
pub mod gen;
|
||||
|
@ -98,16 +98,16 @@ impl Apu {
|
|||
self.sample_counter += SAMPLE_INCREMENT;
|
||||
|
||||
// Frame Sequencer (512Hz)
|
||||
if self.falling_edge(12, div) {
|
||||
if self.is_falling_edge(12, div) {
|
||||
use FrameSequencerState::*;
|
||||
|
||||
match self.sequencer.state() {
|
||||
Length => self.handle_length(),
|
||||
Length => self.clock_length(),
|
||||
LengthAndSweep => {
|
||||
self.handle_length();
|
||||
self.handle_sweep();
|
||||
self.clock_length();
|
||||
self.clock_sweep();
|
||||
}
|
||||
Envelope => self.handle_envelope(),
|
||||
Envelope => self.clock_envelope(),
|
||||
Nothing => {}
|
||||
}
|
||||
|
||||
|
@ -116,16 +116,16 @@ impl Apu {
|
|||
|
||||
self.div_prev = Some(div);
|
||||
|
||||
self.ch1.clock();
|
||||
self.ch2.clock();
|
||||
self.ch3.clock();
|
||||
self.ch4.clock();
|
||||
self.ch1.tick();
|
||||
self.ch2.tick();
|
||||
self.ch3.tick();
|
||||
self.ch4.tick();
|
||||
|
||||
if self.sample_counter >= SM83_CLOCK_SPEED {
|
||||
self.sample_counter %= SM83_CLOCK_SPEED;
|
||||
|
||||
if let Some(ref mut prod) = self.prod {
|
||||
if prod.available_block() {
|
||||
if prod.available_blocking() {
|
||||
// Sample the APU
|
||||
let ch1_amplitude = self.ch1.amplitude();
|
||||
let ch1_left = self.ctrl.output.ch1_left() as u8 as f32 * ch1_amplitude;
|
||||
|
@ -157,10 +157,6 @@ impl Apu {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn set_producer(&mut self, prod: SampleProducer<f32>) {
|
||||
self.prod = Some(prod);
|
||||
}
|
||||
|
||||
/// 0xFF26 | NR52 - Sound On/Off
|
||||
pub(crate) fn set_status(&mut self, byte: u8) {
|
||||
self.ctrl.enabled = (byte >> 7) & 0x01 == 0x01;
|
||||
|
@ -183,6 +179,10 @@ impl Apu {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn attach_producer(&mut self, prod: SampleProducer<f32>) {
|
||||
self.prod = Some(prod);
|
||||
}
|
||||
|
||||
fn reset(&mut self) {
|
||||
self.ch1.sweep = Default::default();
|
||||
self.ch1.duty = Default::default();
|
||||
|
@ -216,7 +216,7 @@ impl Apu {
|
|||
self.ch4.enabled = Default::default();
|
||||
}
|
||||
|
||||
fn clock_length(freq_hi: &FrequencyHigh, length_timer: &mut u16, enabled: &mut bool) {
|
||||
fn process_length(freq_hi: &FrequencyHigh, length_timer: &mut u16, enabled: &mut bool) {
|
||||
if freq_hi.length_disable() && *length_timer > 0 {
|
||||
*length_timer -= 1;
|
||||
|
||||
|
@ -228,7 +228,7 @@ impl Apu {
|
|||
}
|
||||
}
|
||||
|
||||
fn clock_length_ch4(freq: &Ch4Frequency, length_timer: &mut u16, enabled: &mut bool) {
|
||||
fn ch4_process_length(freq: &Ch4Frequency, length_timer: &mut u16, enabled: &mut bool) {
|
||||
if freq.length_disable() && *length_timer > 0 {
|
||||
*length_timer -= 1;
|
||||
|
||||
|
@ -240,33 +240,33 @@ impl Apu {
|
|||
}
|
||||
}
|
||||
|
||||
fn handle_length(&mut self) {
|
||||
Self::clock_length(
|
||||
fn clock_length(&mut self) {
|
||||
Self::process_length(
|
||||
&self.ch1.freq_hi,
|
||||
&mut self.ch1.length_timer,
|
||||
&mut self.ch1.enabled,
|
||||
);
|
||||
|
||||
Self::clock_length(
|
||||
Self::process_length(
|
||||
&self.ch2.freq_hi,
|
||||
&mut self.ch2.length_timer,
|
||||
&mut self.ch2.enabled,
|
||||
);
|
||||
|
||||
Self::clock_length(
|
||||
Self::process_length(
|
||||
&self.ch3.freq_hi,
|
||||
&mut self.ch3.length_timer,
|
||||
&mut self.ch3.enabled,
|
||||
);
|
||||
|
||||
Self::clock_length_ch4(
|
||||
Self::ch4_process_length(
|
||||
&self.ch4.freq,
|
||||
&mut self.ch4.length_timer,
|
||||
&mut self.ch4.enabled,
|
||||
);
|
||||
}
|
||||
|
||||
fn handle_sweep(&mut self) {
|
||||
fn clock_sweep(&mut self) {
|
||||
if self.ch1.sweep_timer != 0 {
|
||||
self.ch1.sweep_timer -= 1;
|
||||
}
|
||||
|
@ -288,7 +288,7 @@ impl Apu {
|
|||
}
|
||||
}
|
||||
|
||||
fn clock_envelope(envelope: &VolumeEnvelope, period_timer: &mut u8, current_volume: &mut u8) {
|
||||
fn process_envelope(envelope: &VolumeEnvelope, period_timer: &mut u8, current_volume: &mut u8) {
|
||||
use EnvelopeDirection::*;
|
||||
|
||||
if envelope.period() != 0 {
|
||||
|
@ -308,29 +308,29 @@ impl Apu {
|
|||
}
|
||||
}
|
||||
|
||||
fn handle_envelope(&mut self) {
|
||||
fn clock_envelope(&mut self) {
|
||||
// Channels 1, 2 and 4 have Volume Envelopes
|
||||
|
||||
Self::clock_envelope(
|
||||
Self::process_envelope(
|
||||
&self.ch1.envelope,
|
||||
&mut self.ch1.period_timer,
|
||||
&mut self.ch1.current_volume,
|
||||
);
|
||||
|
||||
Self::clock_envelope(
|
||||
Self::process_envelope(
|
||||
&self.ch2.envelope,
|
||||
&mut self.ch2.period_timer,
|
||||
&mut self.ch2.current_volume,
|
||||
);
|
||||
|
||||
Self::clock_envelope(
|
||||
Self::process_envelope(
|
||||
&self.ch4.envelope,
|
||||
&mut self.ch4.period_timer,
|
||||
&mut self.ch4.current_volume,
|
||||
);
|
||||
}
|
||||
|
||||
fn falling_edge(&self, bit: u8, div: u16) -> bool {
|
||||
fn is_falling_edge(&self, bit: u8, div: u16) -> bool {
|
||||
match self.div_prev {
|
||||
Some(p) => (p >> bit & 0x01) == 0x01 && (div >> bit & 0x01) == 0x00,
|
||||
None => false,
|
||||
|
@ -416,26 +416,6 @@ pub(crate) struct Channel1 {
|
|||
}
|
||||
|
||||
impl Channel1 {
|
||||
fn amplitude(&self) -> f32 {
|
||||
if self.is_dac_enabled() && self.enabled {
|
||||
let input = self.duty.wave_pattern().amplitude(self.duty_pos) * self.current_volume;
|
||||
(input as f32 / 7.5) - 1.0
|
||||
} else {
|
||||
0.0
|
||||
}
|
||||
}
|
||||
|
||||
fn clock(&mut self) {
|
||||
if self.freq_timer != 0 {
|
||||
self.freq_timer -= 1;
|
||||
}
|
||||
|
||||
if self.freq_timer == 0 {
|
||||
self.freq_timer = (2048 - self.frequency()) * 4;
|
||||
self.duty_pos = (self.duty_pos + 1) % 8;
|
||||
}
|
||||
}
|
||||
|
||||
/// 0xFF10 | NR10 - Channel 1 Sweep Register
|
||||
pub(crate) fn sweep(&self) -> u8 {
|
||||
u8::from(self.sweep) | 0x80
|
||||
|
@ -516,6 +496,26 @@ impl Channel1 {
|
|||
}
|
||||
}
|
||||
|
||||
fn tick(&mut self) {
|
||||
if self.freq_timer != 0 {
|
||||
self.freq_timer -= 1;
|
||||
}
|
||||
|
||||
if self.freq_timer == 0 {
|
||||
self.freq_timer = (2048 - self.frequency()) * 4;
|
||||
self.duty_pos = (self.duty_pos + 1) % 8;
|
||||
}
|
||||
}
|
||||
|
||||
fn amplitude(&self) -> f32 {
|
||||
if self.is_dac_enabled() && self.enabled {
|
||||
let input = self.duty.wave_pattern().amplitude(self.duty_pos) * self.current_volume;
|
||||
(input as f32 / 7.5) - 1.0
|
||||
} else {
|
||||
0.0
|
||||
}
|
||||
}
|
||||
|
||||
fn calc_sweep_freq(&mut self) -> u16 {
|
||||
use SweepDirection::*;
|
||||
|
||||
|
@ -574,26 +574,6 @@ pub(crate) struct Channel2 {
|
|||
}
|
||||
|
||||
impl Channel2 {
|
||||
fn amplitude(&self) -> f32 {
|
||||
if self.is_dac_enabled() && self.enabled {
|
||||
let input = self.duty.wave_pattern().amplitude(self.duty_pos) * self.current_volume;
|
||||
(input as f32 / 7.5) - 1.0
|
||||
} else {
|
||||
0.0
|
||||
}
|
||||
}
|
||||
|
||||
fn clock(&mut self) {
|
||||
if self.freq_timer != 0 {
|
||||
self.freq_timer -= 1;
|
||||
}
|
||||
|
||||
if self.freq_timer == 0 {
|
||||
self.freq_timer = (2048 - self.frequency()) * 4;
|
||||
self.duty_pos = (self.duty_pos + 1) % 8;
|
||||
}
|
||||
}
|
||||
|
||||
/// 0xFF16 | NR21 - Channel 2 Sound length / Wave Pattern Duty
|
||||
pub(crate) fn duty(&self) -> u8 {
|
||||
u8::from(self.duty) | 0x3F
|
||||
|
@ -649,6 +629,26 @@ impl Channel2 {
|
|||
}
|
||||
}
|
||||
|
||||
fn amplitude(&self) -> f32 {
|
||||
if self.is_dac_enabled() && self.enabled {
|
||||
let input = self.duty.wave_pattern().amplitude(self.duty_pos) * self.current_volume;
|
||||
(input as f32 / 7.5) - 1.0
|
||||
} else {
|
||||
0.0
|
||||
}
|
||||
}
|
||||
|
||||
fn tick(&mut self) {
|
||||
if self.freq_timer != 0 {
|
||||
self.freq_timer -= 1;
|
||||
}
|
||||
|
||||
if self.freq_timer == 0 {
|
||||
self.freq_timer = (2048 - self.frequency()) * 4;
|
||||
self.duty_pos = (self.duty_pos + 1) % 8;
|
||||
}
|
||||
}
|
||||
|
||||
fn frequency(&self) -> u16 {
|
||||
(self.freq_hi.freq_bits() as u16) << 8 | self.freq_lo as u16
|
||||
}
|
||||
|
@ -767,16 +767,7 @@ impl Channel3 {
|
|||
}
|
||||
}
|
||||
|
||||
fn amplitude(&self) -> f32 {
|
||||
if self.dac_enabled && self.enabled {
|
||||
let input = self.read_sample(self.offset) >> self.volume.shift_count();
|
||||
(input as f32 / 7.5) - 1.0
|
||||
} else {
|
||||
0.0
|
||||
}
|
||||
}
|
||||
|
||||
fn clock(&mut self) {
|
||||
fn tick(&mut self) {
|
||||
if self.freq_timer != 0 {
|
||||
self.freq_timer -= 1;
|
||||
}
|
||||
|
@ -787,6 +778,15 @@ impl Channel3 {
|
|||
}
|
||||
}
|
||||
|
||||
fn amplitude(&self) -> f32 {
|
||||
if self.dac_enabled && self.enabled {
|
||||
let input = self.read_sample(self.offset) >> self.volume.shift_count();
|
||||
(input as f32 / 7.5) - 1.0
|
||||
} else {
|
||||
0.0
|
||||
}
|
||||
}
|
||||
|
||||
fn read_sample(&self, index: u8) -> u8 {
|
||||
let i = (index / 2) as usize;
|
||||
|
||||
|
@ -889,16 +889,7 @@ impl Channel4 {
|
|||
}
|
||||
}
|
||||
|
||||
fn amplitude(&self) -> f32 {
|
||||
if self.is_dac_enabled() && self.enabled {
|
||||
let input = (!self.lf_shift & 0x01) as u8 * self.current_volume;
|
||||
(input as f32 / 7.5) - 1.0
|
||||
} else {
|
||||
0.0
|
||||
}
|
||||
}
|
||||
|
||||
fn clock(&mut self) {
|
||||
fn tick(&mut self) {
|
||||
if self.freq_timer != 0 {
|
||||
self.freq_timer -= 1;
|
||||
}
|
||||
|
@ -916,6 +907,15 @@ impl Channel4 {
|
|||
}
|
||||
}
|
||||
|
||||
fn amplitude(&self) -> f32 {
|
||||
if self.is_dac_enabled() && self.enabled {
|
||||
let input = (!self.lf_shift & 0x01) as u8 * self.current_volume;
|
||||
(input as f32 / 7.5) - 1.0
|
||||
} else {
|
||||
0.0
|
||||
}
|
||||
}
|
||||
|
||||
fn is_dac_enabled(&self) -> bool {
|
||||
self.envelope.0 & 0xF8 != 0x00
|
||||
}
|
||||
|
|
|
@ -48,7 +48,7 @@ impl<T> SampleProducer<T> {
|
|||
self.inner.slots() > 2
|
||||
}
|
||||
|
||||
pub(crate) fn available_block(&self) -> bool {
|
||||
pub(crate) fn available_blocking(&self) -> bool {
|
||||
loop {
|
||||
if self.inner.slots() > 2 {
|
||||
break true;
|
||||
|
|
|
@ -513,21 +513,21 @@ pub(super) mod fs {
|
|||
#[derive(Debug)]
|
||||
pub(crate) struct FrameSequencer {
|
||||
step: u8,
|
||||
state: FrameSequencerState,
|
||||
state: State,
|
||||
}
|
||||
|
||||
impl Default for FrameSequencer {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
step: Default::default(),
|
||||
state: FrameSequencerState::Length,
|
||||
state: State::Length,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FrameSequencer {
|
||||
pub(crate) fn next(&mut self) {
|
||||
use FrameSequencerState::*;
|
||||
use State::*;
|
||||
|
||||
self.step = (self.step + 1) % 8;
|
||||
self.state = match self.step {
|
||||
|
@ -539,18 +539,18 @@ pub(super) mod fs {
|
|||
};
|
||||
}
|
||||
|
||||
pub(crate) fn state(&self) -> FrameSequencerState {
|
||||
pub(crate) fn state(&self) -> State {
|
||||
self.state
|
||||
}
|
||||
|
||||
pub(crate) fn reset(&mut self) {
|
||||
self.step = Default::default();
|
||||
self.state = FrameSequencerState::Length;
|
||||
self.state = State::Length;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub(crate) enum FrameSequencerState {
|
||||
pub(crate) enum State {
|
||||
Length,
|
||||
Nothing,
|
||||
LengthAndSweep,
|
||||
|
|
|
@ -68,7 +68,7 @@ fn main() -> Result<()> {
|
|||
let (_stream, stream_handle) = OutputStream::try_default().expect("Initialized Audio");
|
||||
let sink = Sink::try_new(&stream_handle)?;
|
||||
sink.append(cons);
|
||||
game_boy.apu_mut().set_producer(prod);
|
||||
game_boy.apu_mut().attach_producer(prod);
|
||||
|
||||
std::thread::spawn(move || {
|
||||
sink.sleep_until_end();
|
||||
|
|
Loading…
Reference in New Issue