use crate::{
apu::{
Channel,
envelope::Envelope,
length_counter::LengthCounter,
timer::{Timer, TimerCycle},
},
common::{Clock, NesRegion, Regional, Reset, ResetKind, Sample},
};
use serde::{Deserialize, Serialize};
#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize, Deserialize)]
pub enum ShiftMode {
Zero,
One,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[must_use]
pub struct Noise {
pub region: NesRegion,
pub timer: Timer,
pub shift: u16,
pub shift_mode: ShiftMode,
pub length: LengthCounter,
pub envelope: Envelope,
pub force_silent: bool,
}
impl Default for Noise {
fn default() -> Self {
Self::new(NesRegion::default())
}
}
impl Noise {
const PERIOD_TABLE_NTSC: [u16; 16] = [
4, 8, 16, 32, 64, 96, 128, 160, 202, 254, 380, 508, 762, 1016, 2034, 4068,
];
const PERIOD_TABLE_PAL: [u16; 16] = [
4, 8, 14, 30, 60, 88, 118, 148, 188, 236, 354, 472, 708, 944, 1890, 3778,
];
pub const fn new(region: NesRegion) -> Self {
Self {
region,
timer: Timer::new(Self::period(region, 0)),
shift: 1, shift_mode: ShiftMode::Zero,
length: LengthCounter::new(Channel::Noise),
envelope: Envelope::new(),
force_silent: false,
}
}
#[must_use]
pub const fn is_muted(&self) -> bool {
(self.shift & 0x01) == 0x01 || self.silent()
}
#[must_use]
pub const fn silent(&self) -> bool {
self.force_silent
}
pub const fn set_silent(&mut self, silent: bool) {
self.force_silent = silent;
}
const fn period(region: NesRegion, val: u8) -> u16 {
let index = (val & 0x0F) as usize;
match region {
NesRegion::Auto | NesRegion::Ntsc | NesRegion::Dendy => {
Self::PERIOD_TABLE_NTSC[index] - 1
}
NesRegion::Pal => Self::PERIOD_TABLE_PAL[index] - 1,
}
}
pub fn clock_quarter_frame(&mut self) {
self.envelope.clock();
}
pub fn clock_half_frame(&mut self) {
self.clock_quarter_frame();
self.length.clock();
}
pub const fn write_ctrl(&mut self, val: u8) {
self.length.write_ctrl((val & 0x20) == 0x20); self.envelope.write_ctrl(val);
}
pub const fn write_timer(&mut self, val: u8) {
self.timer.period = Self::period(self.region, val);
self.shift_mode = if (val & 0x80) == 0x80 {
ShiftMode::One
} else {
ShiftMode::Zero
};
}
pub const fn write_length(&mut self, val: u8) {
self.length.write(val >> 3);
self.envelope.restart();
}
pub const fn set_enabled(&mut self, enabled: bool) {
self.length.set_enabled(enabled);
}
pub const fn volume(&self) -> u8 {
if self.length.counter > 0 {
self.envelope.volume()
} else {
0
}
}
}
impl Sample for Noise {
fn output(&self) -> f32 {
if self.is_muted() {
0f32
} else {
f32::from(self.volume())
}
}
}
impl TimerCycle for Noise {
fn cycle(&self) -> u32 {
self.timer.cycle
}
}
impl Clock for Noise {
fn clock(&mut self) {
if self.timer.tick() {
let shift_by = if self.shift_mode == ShiftMode::One {
6
} else {
1
};
let feedback = (self.shift & 0x01) ^ ((self.shift >> shift_by) & 0x01);
self.shift >>= 1;
self.shift |= feedback << 14;
}
}
}
impl Regional for Noise {
fn region(&self) -> NesRegion {
self.region
}
fn set_region(&mut self, region: NesRegion) {
self.region = region;
}
}
impl Reset for Noise {
fn reset(&mut self, kind: ResetKind) {
self.timer.reset(kind);
self.timer.period = Self::period(self.region, 0);
self.length.reset(kind);
self.envelope.reset(kind);
self.shift = 1;
self.shift_mode = ShiftMode::Zero;
}
}