1//! YM2612 phase generator 2 3use crate::ym2612::lfo; 4use bincode::{Decode, Encode}; 5use jgenesis_common::num::GetBit; 6 7// Shifted F-num values are 17 bits 8const SHIFTED_F_NUM_MASK: u32 = 0x1FFFF; 9 10// Phase counter is 20 bits 11const PHASE_COUNTER_MASK: u32 = 0xFFFFF; 12 13// Created by adapting the documented detune table into increments of ~0.053Hz 14#[rustfmt::skip] 15const DETUNE_TABLE: &[[u8; 4]; 32] = &[ 16 [0, 0, 1, 2], [0, 0, 1, 2], [0, 0, 1, 2], [0, 0, 1, 2], // Block 0 17 [0, 1, 2, 2], [0, 1, 2, 3], [0, 1, 2, 3], [0, 1, 2, 3], // Block 1 18 [0, 1, 2, 4], [0, 1, 3, 4], [0, 1, 3, 4], [0, 1, 3, 5], // Block 2 19 [0, 2, 4, 5], [0, 2, 4, 6], [0, 2, 4, 6], [0, 2, 5, 7], // Block 3 20 [0, 2, 5, 8], [0, 3, 6, 8], [0, 3, 6, 9], [0, 3, 7, 10], // Block 4 21 [0, 4, 8, 11], [0, 4, 8, 12], [0, 4, 9, 13], [0, 5, 10, 14], // Block 5 22 [0, 5, 11, 16], [0, 6, 12, 17], [0, 6, 13, 19], [0, 7, 14, 20], // Block 6 23 [0, 8, 16, 22], [0, 8, 16, 22], [0, 8, 16, 22], [0, 8, 16, 22], // Block 7 24]; 25 26#[derive(Debug, Clone, Encode, Decode)] 27pub(super) struct PhaseGenerator { 28 // Register values 29 pub(super) f_number: u16, 30 pub(super) block: u8, 31 pub(super) multiple: u8, 32 pub(super) detune: u8, 33 // Internal state 34 counter: u32, 35 current_output: u16, 36} 37 38impl PhaseGenerator { 39 pub(super) fn new() -> Self { 40 Self { f_number: 0, block: 0, multiple: 0, detune: 0, counter: 0, current_output: 0 } 41 } 42 43 pub(super) fn reset(&mut self) { 44 self.counter = 0; 45 46 log::trace!("State at key on: {self:?}"); 47 } 48 49 #[inline] 50 pub(super) fn clock(&mut self, lfo_counter: u8, fm_sensitivity: u8) { 51 let phase_increment = self.compute_phase_increment(lfo_counter, fm_sensitivity); 52 self.counter = (self.counter + phase_increment) & PHASE_COUNTER_MASK; 53 54 // Phase generator output is the highest 10 bits of the 20-bit phase counter 55 self.current_output = (self.counter >> 10) as u16; 56 } 57 58 fn compute_phase_increment(&self, lfo_counter: u8, fm_sensitivity: u8) -> u32 { 59 // Vibrato / LFO FM; 12-bit result 60 let modulated_f_num = lfo::frequency_modulation(lfo_counter, fm_sensitivity, self.f_number); 61 62 // Apply block/octave multiplier; 17-bit result 63 // Right shift by 2 because the LFO FM result is left shifted by 1 relative to input F-num 64 let shifted_f_num = (u32::from(modulated_f_num) << self.block) >> 2; 65 66 // Apply detune; 17-bit result 67 let key_code = super::compute_key_code(self.f_number, self.block); 68 let detune_magnitude = self.detune & 3; 69 let detune_increment_magnitude: u32 = 70 DETUNE_TABLE[key_code as usize][detune_magnitude as usize].into(); 71 let detuned_f_num = if self.detune.bit(2) { 72 shifted_f_num.wrapping_sub(detune_increment_magnitude) & SHIFTED_F_NUM_MASK 73 } else { 74 shifted_f_num.wrapping_add(detune_increment_magnitude) & SHIFTED_F_NUM_MASK 75 }; 76 77 // Apply frequency multiplier; 20-bit result 78 match self.multiple { 79 0 => detuned_f_num >> 1, 80 m => detuned_f_num * u32::from(m), 81 } 82 } 83 84 pub(super) fn current_phase(&self) -> u16 { 85 self.current_output 86 } 87} 88 89impl Default for PhaseGenerator { 90 fn default() -> Self { 91 Self::new() 92 } 93}