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}