use crate::{
apu::{
Channel,
envelope::Envelope,
length_counter::LengthCounter,
timer::{Timer, TimerCycle},
},
common::{Clock, Reset, ResetKind, Sample},
};
use serde::{Deserialize, Serialize};
#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize, Deserialize)]
pub enum OutputFreq {
Default,
Ultrasonic,
}
#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize, Deserialize)]
pub enum PulseChannel {
One,
Two,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[must_use]
pub struct Pulse {
pub channel: PulseChannel,
pub real_period: u16,
pub timer: Timer,
pub duty: u8, pub duty_cycle: u8, pub length: LengthCounter,
pub envelope: Envelope,
pub sweep: Sweep,
pub force_silent: bool,
pub output_freq: OutputFreq,
}
impl Default for Pulse {
fn default() -> Self {
Self::new(PulseChannel::One, OutputFreq::Default)
}
}
impl Pulse {
const DUTY_TABLE: [[u8; 8]; 4] = [
[0, 0, 0, 0, 0, 0, 0, 1],
[0, 0, 0, 0, 0, 0, 1, 1],
[0, 0, 0, 0, 1, 1, 1, 1],
[1, 1, 1, 1, 1, 1, 0, 0],
];
pub const fn new(channel: PulseChannel, output_freq: OutputFreq) -> Self {
Self {
channel,
real_period: 0,
timer: Timer::new(0),
duty: 0u8,
duty_cycle: 0,
length: LengthCounter::new(match channel {
PulseChannel::One => Channel::Pulse1,
PulseChannel::Two => Channel::Pulse2,
}),
envelope: Envelope::new(),
sweep: Sweep::new(channel),
force_silent: false,
output_freq,
}
}
#[inline]
pub fn is_muted(&self) -> bool {
self.output_freq == OutputFreq::Default
&& (self.real_period < 8 || (!self.sweep.negate && self.sweep.target_period > 0x7FF))
|| 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 update_target_period(&mut self) {
let delta = self.real_period >> self.sweep.shift;
if self.sweep.negate {
self.sweep.target_period = self.real_period - delta;
if let PulseChannel::One = self.channel {
self.sweep.target_period = self.sweep.target_period.wrapping_sub(1);
}
} else {
self.sweep.target_period = self.real_period + delta;
}
}
const fn set_period(&mut self, period: u16) {
self.real_period = period;
self.timer.period = (period * 2) + 1;
self.update_target_period();
}
const fn clock_sweep(&mut self) {
self.sweep.divider = self.sweep.divider.wrapping_sub(1);
if self.sweep.divider == 0 {
if self.sweep.shift > 0
&& self.sweep.enabled
&& self.real_period >= 8
&& self.sweep.target_period <= 0x7FF
{
self.set_period(self.sweep.target_period);
}
self.sweep.divider = self.sweep.period;
}
if self.sweep.reload {
self.sweep.divider = self.sweep.period;
self.sweep.reload = false;
}
}
pub fn clock_quarter_frame(&mut self) {
self.envelope.clock();
}
pub fn clock_half_frame(&mut self) {
self.clock_quarter_frame();
self.length.clock();
self.clock_sweep();
}
pub const fn write_ctrl(&mut self, val: u8) {
self.length.write_ctrl((val & 0x20) == 0x20); self.envelope.write_ctrl(val);
self.duty = (val & 0xC0) >> 6;
}
pub const fn write_sweep(&mut self, val: u8) {
self.sweep.enabled = (val & 0x80) == 0x80;
self.sweep.negate = (val & 0x08) == 0x08;
self.sweep.period = ((val & 0x70) >> 4) + 1;
self.sweep.shift = val & 0x07;
self.update_target_period();
self.sweep.reload = true;
}
pub fn write_timer_lo(&mut self, val: u8) {
self.set_period(self.real_period & 0x0700 | u16::from(val));
}
pub fn write_timer_hi(&mut self, val: u8) {
self.length.write(val >> 3);
self.set_period(self.real_period & 0xFF | (u16::from(val & 0x07) << 8));
self.duty_cycle = 0;
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 Pulse {
fn output(&self) -> f32 {
if self.is_muted() {
0.0
} else {
f32::from(
Self::DUTY_TABLE[self.duty as usize][self.duty_cycle as usize] * self.volume(),
)
}
}
}
impl TimerCycle for Pulse {
fn cycle(&self) -> u32 {
self.timer.cycle
}
}
impl Clock for Pulse {
fn clock(&mut self) {
if self.timer.tick() {
self.duty_cycle = self.duty_cycle.wrapping_sub(1) & 0x07;
}
}
}
impl Reset for Pulse {
fn reset(&mut self, kind: ResetKind) {
self.timer.reset(kind);
self.length.reset(kind);
self.envelope.reset(kind);
self.sweep.reset(kind);
self.update_target_period();
self.duty = 0;
self.duty_cycle = 0;
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Sweep {
pub enabled: bool,
pub channel: PulseChannel,
pub negate: bool, pub reload: bool,
pub shift: u8,
pub timer: u16,
pub divider: u8,
pub period: u8,
pub target_period: u16,
}
impl Sweep {
pub const fn new(channel: PulseChannel) -> Self {
Self {
enabled: false,
channel,
negate: false,
reload: false,
shift: 0,
timer: 0,
divider: 0,
period: 0,
target_period: 0,
}
}
}
impl Reset for Sweep {
fn reset(&mut self, _kind: ResetKind) {
self.enabled = false;
self.period = 0;
self.negate = false;
self.reload = false;
self.shift = 0;
self.divider = 0;
self.target_period = 0;
}
}