use crate::core::error::Result;
use crate::core::state::{Sink, Source};
use super::frame::Region;
use super::units::{Envelope, LengthCounter};
const NTSC_PERIODS: [u16; 16] = [
4, 8, 16, 32, 64, 96, 128, 160, 202, 254, 380, 508, 762, 1016, 2034, 4068,
];
const PAL_PERIODS: [u16; 16] = [
4, 8, 14, 30, 60, 88, 118, 148, 188, 236, 354, 472, 708, 944, 1890, 3778,
];
pub const fn periods(region: Region) -> [u16; 16] {
match region {
Region::Ntsc | Region::Dendy => NTSC_PERIODS,
Region::Pal => PAL_PERIODS,
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Noise {
pub envelope: Envelope,
pub length: LengthCounter,
region: Region,
mode: bool,
period_index: u8,
timer: u16,
shift: u16,
}
impl Noise {
pub const fn new(region: Region) -> Noise {
Noise {
envelope: Envelope::new(),
length: LengthCounter::new(),
region,
mode: false,
period_index: 0,
timer: 0,
shift: 1,
}
}
#[inline]
pub const fn shift(&self) -> u16 {
self.shift
}
#[inline]
pub fn period_cycles(&self) -> u16 {
periods(self.region)[usize::from(self.period_index)]
}
#[inline]
fn reload(&self) -> u16 {
self.period_cycles() / 2 - 1
}
pub fn write_control(&mut self, value: u8) {
self.envelope.write_control(value);
self.length.set_halt(self.envelope.loop_flag());
}
pub fn write_period(&mut self, value: u8) {
self.mode = value & 0x80 != 0;
self.period_index = value & 0x0F;
}
pub fn write_length(&mut self, value: u8) {
self.length.load(value);
self.envelope.restart();
}
pub fn tick_timer(&mut self) {
if self.timer == 0 {
self.timer = self.reload();
self.clock_shift();
} else {
self.timer -= 1;
}
}
fn clock_shift(&mut self) {
let tap = if self.mode { 6 } else { 1 };
let feedback = (self.shift & 1) ^ ((self.shift >> tap) & 1);
self.shift >>= 1;
self.shift |= feedback << 14;
}
pub fn output(&self) -> u8 {
if !self.length.active() || self.shift & 1 != 0 {
return 0;
}
self.envelope.volume()
}
pub fn save(&self, w: &mut dyn Sink) -> Result<()> {
self.envelope.save(w)?;
self.length.save(w)?;
w.write_bool(self.mode)?;
w.write_u8(self.period_index)?;
w.write_u16(self.timer)?;
w.write_u16(self.shift)
}
pub fn load<'a>(&mut self, r: &mut dyn Source<'a>) -> Result<()> {
self.envelope.load(r)?;
self.length.load_state(r)?;
self.mode = r.read_bool()?;
self.period_index = r.read_u8()? & 0x0F;
self.timer = r.read_u16()?;
self.shift = r.read_u16()? & 0x7FFF;
Ok(())
}
}