tetanes_core/apu/
noise.rs

1//! APU Noise Channel implementation.
2//!
3//! See: <https://www.nesdev.org/wiki/APU_Noise>
4
5use crate::{
6    apu::{
7        Channel,
8        envelope::Envelope,
9        length_counter::LengthCounter,
10        timer::{Timer, TimerCycle},
11    },
12    common::{Clock, NesRegion, Regional, Reset, ResetKind, Sample},
13};
14use serde::{Deserialize, Serialize};
15
16/// Noise shift mode.
17#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize, Deserialize)]
18pub enum ShiftMode {
19    /// Zero (XOR bits 0 and 1)
20    Zero,
21    /// One (XOR bits 0 and 6)
22    One,
23}
24
25/// APU Noise Channel provides pseudo-random noise generation.
26///
27/// See: <https://www.nesdev.org/wiki/APU_Noise>
28#[derive(Debug, Clone, Serialize, Deserialize)]
29#[must_use]
30pub struct Noise {
31    pub region: NesRegion,
32    pub timer: Timer,
33    pub shift: u16,
34    pub shift_mode: ShiftMode,
35    pub length: LengthCounter,
36    pub envelope: Envelope,
37    pub force_silent: bool,
38}
39
40impl Default for Noise {
41    fn default() -> Self {
42        Self::new(NesRegion::Ntsc)
43    }
44}
45
46impl Noise {
47    const PERIOD_TABLE_NTSC: [u64; 16] = [
48        4, 8, 16, 32, 64, 96, 128, 160, 202, 254, 380, 508, 762, 1016, 2034, 4068,
49    ];
50    const PERIOD_TABLE_PAL: [u64; 16] = [
51        4, 8, 14, 30, 60, 88, 118, 148, 188, 236, 354, 472, 708, 944, 1890, 3778,
52    ];
53
54    pub const fn new(region: NesRegion) -> Self {
55        Self {
56            region,
57            timer: Timer::new(Self::period(region, 0)),
58            shift: 1, // defaults to 1 on power up
59            shift_mode: ShiftMode::Zero,
60            length: LengthCounter::new(Channel::Noise),
61            envelope: Envelope::new(),
62            force_silent: false,
63        }
64    }
65
66    #[must_use]
67    pub const fn is_muted(&self) -> bool {
68        (self.shift & 0x01) == 0x01 || self.silent()
69    }
70
71    #[must_use]
72    pub const fn silent(&self) -> bool {
73        self.force_silent
74    }
75
76    pub const fn set_silent(&mut self, silent: bool) {
77        self.force_silent = silent;
78    }
79
80    const fn period(region: NesRegion, val: u8) -> u64 {
81        let index = (val & 0x0F) as usize;
82        match region {
83            NesRegion::Auto | NesRegion::Ntsc | NesRegion::Dendy => {
84                Self::PERIOD_TABLE_NTSC[index] - 1
85            }
86            NesRegion::Pal => Self::PERIOD_TABLE_PAL[index] - 1,
87        }
88    }
89
90    pub fn clock_quarter_frame(&mut self) {
91        self.envelope.clock();
92    }
93
94    pub fn clock_half_frame(&mut self) {
95        self.clock_quarter_frame();
96        self.length.clock();
97    }
98
99    /// $400C Noise control
100    pub const fn write_ctrl(&mut self, val: u8) {
101        self.length.write_ctrl((val & 0x20) == 0x20); // !D5
102        self.envelope.write_ctrl(val);
103    }
104
105    /// $400E Noise timer
106    pub const fn write_timer(&mut self, val: u8) {
107        self.timer.period = Self::period(self.region, val);
108        self.shift_mode = if (val & 0x80) == 0x80 {
109            ShiftMode::One
110        } else {
111            ShiftMode::Zero
112        };
113    }
114
115    /// $400F Length counter
116    pub const fn write_length(&mut self, val: u8) {
117        self.length.write(val >> 3);
118        self.envelope.restart();
119    }
120
121    pub const fn set_enabled(&mut self, enabled: bool) {
122        self.length.set_enabled(enabled);
123    }
124
125    pub const fn volume(&self) -> u8 {
126        if self.length.counter > 0 {
127            self.envelope.volume()
128        } else {
129            0
130        }
131    }
132}
133
134impl Sample for Noise {
135    fn output(&self) -> f32 {
136        if self.is_muted() {
137            0f32
138        } else {
139            f32::from(self.volume())
140        }
141    }
142}
143
144impl TimerCycle for Noise {
145    fn cycle(&self) -> u64 {
146        self.timer.cycle
147    }
148}
149
150impl Clock for Noise {
151    //    Timer --> Shift Register   Length Counter
152    //                    |                |
153    //                    v                v
154    // Envelope -------> Gate ----------> Gate --> (to mixer)
155    fn clock(&mut self) -> u64 {
156        if self.timer.clock() > 0 {
157            let shift_by = if self.shift_mode == ShiftMode::One {
158                6
159            } else {
160                1
161            };
162            let feedback = (self.shift & 0x01) ^ ((self.shift >> shift_by) & 0x01);
163            self.shift >>= 1;
164            self.shift |= feedback << 14;
165            1
166        } else {
167            0
168        }
169    }
170}
171
172impl Regional for Noise {
173    fn region(&self) -> NesRegion {
174        self.region
175    }
176
177    fn set_region(&mut self, region: NesRegion) {
178        self.region = region;
179    }
180}
181
182impl Reset for Noise {
183    fn reset(&mut self, kind: ResetKind) {
184        self.timer.reset(kind);
185        self.timer.period = Self::period(self.region, 0);
186        self.length.reset(kind);
187        self.envelope.reset(kind);
188        self.shift = 1;
189        self.shift_mode = ShiftMode::Zero;
190    }
191}