use crate::{
apu::{envelope::Envelope, length_counter::LengthCounter},
common::{Clock, Kind, NesRegion, Regional, Reset},
};
use serde::{Deserialize, Serialize};
#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize, Deserialize)]
enum ShiftMode {
Zero,
One,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[must_use]
pub struct Noise {
region: NesRegion,
force_silent: bool,
enabled: bool,
freq_timer: u16, freq_counter: u16, shift: u16, shift_mode: ShiftMode, length: LengthCounter,
envelope: Envelope,
}
impl Default for Noise {
fn default() -> Self {
Self::new()
}
}
impl Noise {
const FREQ_TABLE_NTSC: [u16; 16] = [
4, 8, 16, 32, 64, 96, 128, 160, 202, 254, 380, 508, 762, 1016, 2034, 4068,
];
const FREQ_TABLE_PAL: [u16; 16] = [
4, 8, 14, 30, 60, 88, 118, 148, 188, 236, 354, 472, 708, 944, 1890, 3778,
];
const SHIFT_BIT_15_MASK: u16 = !0x8000;
pub fn new() -> Self {
Self {
region: NesRegion::default(),
enabled: false,
force_silent: false,
freq_timer: 0u16,
freq_counter: 0u16,
shift: 1u16, shift_mode: ShiftMode::Zero,
length: LengthCounter::new(),
envelope: Envelope::new(),
}
}
#[inline]
#[must_use]
pub const fn silent(&self) -> bool {
self.force_silent
}
#[inline]
pub fn toggle_silent(&mut self) {
self.force_silent = !self.force_silent;
}
#[inline]
#[must_use]
pub const fn length_counter(&self) -> u8 {
self.length.counter()
}
#[inline]
const fn freq_timer(region: NesRegion, val: u8) -> u16 {
match region {
NesRegion::Ntsc => Self::FREQ_TABLE_NTSC[(val & 0x0F) as usize] - 1,
NesRegion::Pal | NesRegion::Dendy => Self::FREQ_TABLE_PAL[(val & 0x0F) as usize] - 1,
}
}
#[inline]
pub fn clock_quarter_frame(&mut self) {
self.envelope.clock();
}
#[inline]
pub fn clock_half_frame(&mut self) {
self.length.clock();
}
#[must_use]
pub fn output(&self) -> f32 {
if self.shift & 1 == 0 && self.length.counter != 0 && !self.force_silent {
if self.envelope.enabled {
f32::from(self.envelope.volume)
} else {
f32::from(self.envelope.constant_volume)
}
} else {
0f32
}
}
pub fn write_ctrl(&mut self, val: u8) {
self.length.write_ctrl(val);
self.envelope.write_ctrl(val);
}
pub fn write_timer(&mut self, val: u8) {
self.freq_timer = Self::freq_timer(self.region, val);
self.shift_mode = if (val >> 7) & 1 == 1 {
ShiftMode::One
} else {
ShiftMode::Zero
};
}
pub fn write_length(&mut self, val: u8) {
if self.enabled {
self.length.load_value(val);
}
self.envelope.reset = true;
}
pub fn set_enabled(&mut self, enabled: bool) {
self.enabled = enabled;
if !enabled {
self.length.counter = 0;
}
}
}
impl Clock for Noise {
fn clock(&mut self) -> usize {
if self.freq_counter > 0 {
self.freq_counter -= 1;
} else {
self.freq_counter = self.freq_timer;
let shift_amount = if self.shift_mode == ShiftMode::One {
6
} else {
1
};
let bit1 = self.shift & 1; let bit2 = (self.shift >> shift_amount) & 1; self.shift = (self.shift & Self::SHIFT_BIT_15_MASK) | ((bit1 ^ bit2) << 14);
self.shift >>= 1;
}
1
}
}
impl Regional for Noise {
#[inline]
fn region(&self) -> NesRegion {
self.region
}
#[inline]
fn set_region(&mut self, region: NesRegion) {
self.region = region;
}
}
impl Reset for Noise {
fn reset(&mut self, _kind: Kind) {
*self = Self::new();
}
}