noise.rsannotatednoise.rssource113 lines · 3.1 KB · raw
1//! The APU's pseudo-random noise generator channel. Generates white noise using a linear feedback
2//! shift register with a configurable period.
3//!
4//! Channel output values are between 0 and 15 (inclusive).
5
6use crate::apu::units::{Envelope, LengthCounter, LengthCounterChannel};
7use bincode::{Decode, Encode};
8use jgenesis_common::num::GetBit;
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Encode, Decode)]
11enum LfsrMode {
12    Bit1Feedback,
13    Bit6Feedback,
14}
15
16#[derive(Debug, Clone, Encode, Decode)]
17struct LinearFeedbackShiftRegister {
18    register: u16,
19    mode: LfsrMode,
20}
21
22impl LinearFeedbackShiftRegister {
23    fn new() -> Self {
24        Self { register: 1, mode: LfsrMode::Bit1Feedback }
25    }
26
27    fn clock(&mut self) {
28        let feedback = match self.mode {
29            LfsrMode::Bit1Feedback => (self.register & 0x01) ^ ((self.register & 0x02) >> 1),
30            LfsrMode::Bit6Feedback => (self.register & 0x01) ^ ((self.register & 0x40) >> 6),
31        };
32
33        self.register = (self.register >> 1) | (feedback << 14);
34    }
35
36    fn sample(&self) -> u8 {
37        (!self.register & 0x01) as u8
38    }
39}
40
41const NOISE_PERIOD_LOOKUP_TABLE: [u16; 16] =
42    [4, 8, 16, 32, 64, 96, 128, 160, 202, 254, 380, 508, 762, 1016, 2034, 4068];
43
44#[derive(Debug, Clone, Encode, Decode)]
45pub struct NoiseChannel {
46    lfsr: LinearFeedbackShiftRegister,
47    timer_counter: u16,
48    timer_period: u16,
49    length_counter: LengthCounter,
50    envelope: Envelope,
51}
52
53impl NoiseChannel {
54    pub fn new() -> Self {
55        Self {
56            lfsr: LinearFeedbackShiftRegister::new(),
57            timer_counter: 0,
58            timer_period: 1,
59            length_counter: LengthCounter::new(LengthCounterChannel::Noise),
60            envelope: Envelope::new(),
61        }
62    }
63
64    pub fn clock_quarter_frame(&mut self) {
65        self.envelope.clock();
66    }
67
68    pub fn clock_half_frame(&mut self) {
69        self.length_counter.clock();
70    }
71
72    pub fn tick_cpu(&mut self) {
73        if self.timer_counter == 0 {
74            self.timer_counter = self.timer_period - 1;
75            self.lfsr.clock();
76        } else {
77            self.timer_counter -= 1;
78        }
79    }
80
81    pub fn process_vol_update(&mut self, vol_value: u8) {
82        self.envelope.process_vol_update(vol_value);
83        self.length_counter.process_vol_update(vol_value);
84    }
85
86    pub fn process_lo_update(&mut self, lo_value: u8) {
87        self.lfsr.mode =
88            if lo_value.bit(7) { LfsrMode::Bit6Feedback } else { LfsrMode::Bit1Feedback };
89
90        self.timer_period = NOISE_PERIOD_LOOKUP_TABLE[(lo_value & 0x0F) as usize];
91    }
92
93    pub fn process_hi_update(&mut self, hi_value: u8) {
94        self.envelope.process_hi_update();
95        self.length_counter.process_hi_update(hi_value);
96    }
97
98    pub fn process_snd_chn_update(&mut self, snd_chn_value: u8) {
99        self.length_counter.process_snd_chn_update(snd_chn_value);
100    }
101
102    pub fn sample(&self) -> u8 {
103        if self.length_counter.counter == 0 {
104            0
105        } else {
106            self.lfsr.sample() * self.envelope.volume()
107        }
108    }
109
110    pub fn length_counter(&self) -> u8 {
111        self.length_counter.counter
112    }
113}