use crate::core::error::Result;
use crate::core::state::{Sink, Source};
use super::units::{Envelope, LengthCounter};
const DUTY: [[u8; 8]; 4] = [
[0, 1, 0, 0, 0, 0, 0, 0], [0, 1, 1, 0, 0, 0, 0, 0], [0, 1, 1, 1, 1, 0, 0, 0], [1, 0, 0, 1, 1, 1, 1, 1], ];
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Sweep {
enabled: bool,
negate: bool,
shift: u8,
period: u8,
divider: u8,
reload: bool,
ones_complement: bool,
}
impl Sweep {
pub const fn new(ones_complement: bool) -> Sweep {
Sweep {
enabled: false,
negate: false,
shift: 0,
period: 0,
divider: 0,
reload: false,
ones_complement,
}
}
pub fn write(&mut self, value: u8) {
self.enabled = value & 0x80 != 0;
self.period = (value >> 4) & 0x07;
self.negate = value & 0x08 != 0;
self.shift = value & 0x07;
self.reload = true;
}
pub fn target(&self, period: u16) -> u16 {
let change = i32::from(period >> self.shift);
let sum = if self.negate {
i32::from(period) - change - i32::from(self.ones_complement)
} else {
i32::from(period) + change
};
sum.max(0) as u16
}
pub fn muting(&self, period: u16) -> bool {
period < 8 || self.target(period) > 0x7FF
}
pub fn clock(&mut self, period: &mut u16) {
if self.divider == 0 && self.enabled && self.shift != 0 && !self.muting(*period) {
*period = self.target(*period);
}
if self.divider == 0 || self.reload {
self.divider = self.period;
self.reload = false;
} else {
self.divider -= 1;
}
}
pub fn save(&self, w: &mut dyn Sink) -> Result<()> {
w.write_bool(self.enabled)?;
w.write_bool(self.negate)?;
w.write_u8(self.shift)?;
w.write_u8(self.period)?;
w.write_u8(self.divider)?;
w.write_bool(self.reload)
}
pub fn load<'a>(&mut self, r: &mut dyn Source<'a>) -> Result<()> {
self.enabled = r.read_bool()?;
self.negate = r.read_bool()?;
self.shift = r.read_u8()?;
self.period = r.read_u8()?;
self.divider = r.read_u8()?;
self.reload = r.read_bool()?;
Ok(())
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Pulse {
pub envelope: Envelope,
pub sweep: Sweep,
pub length: LengthCounter,
duty: u8,
period: u16,
timer: u16,
step: u8,
}
impl Pulse {
pub const fn new(first: bool) -> Pulse {
Pulse {
envelope: Envelope::new(),
sweep: Sweep::new(first),
length: LengthCounter::new(),
duty: 0,
period: 0,
timer: 0,
step: 0,
}
}
#[inline]
pub const fn period(&self) -> u16 {
self.period
}
pub fn write_control(&mut self, value: u8) {
self.duty = value >> 6;
self.envelope.write_control(value);
self.length.set_halt(self.envelope.loop_flag());
}
pub fn write_sweep(&mut self, value: u8) {
self.sweep.write(value);
}
pub fn write_period_low(&mut self, value: u8) {
self.period = (self.period & 0x0700) | u16::from(value);
}
pub fn write_period_high(&mut self, value: u8) {
self.period = (self.period & 0x00FF) | (u16::from(value & 0x07) << 8);
self.length.load(value);
self.step = 0;
self.envelope.restart();
}
pub fn tick_timer(&mut self) {
if self.timer == 0 {
self.timer = self.period;
self.step = (self.step + 1) & 7;
} else {
self.timer -= 1;
}
}
pub fn output(&self) -> u8 {
if !self.length.active() || self.sweep.muting(self.period) {
return 0;
}
if DUTY[usize::from(self.duty)][usize::from(self.step)] == 0 {
return 0;
}
self.envelope.volume()
}
pub fn clock_sweep(&mut self) {
self.sweep.clock(&mut self.period);
}
pub fn save(&self, w: &mut dyn Sink) -> Result<()> {
self.envelope.save(w)?;
self.sweep.save(w)?;
self.length.save(w)?;
w.write_u8(self.duty)?;
w.write_u16(self.period)?;
w.write_u16(self.timer)?;
w.write_u8(self.step)
}
pub fn load<'a>(&mut self, r: &mut dyn Source<'a>) -> Result<()> {
self.envelope.load(r)?;
self.sweep.load(r)?;
self.length.load_state(r)?;
self.duty = r.read_u8()? & 3;
self.period = r.read_u16()? & 0x07FF;
self.timer = r.read_u16()?;
self.step = r.read_u8()? & 7;
Ok(())
}
}