YM2612 phase generator

3use crate::ym2612::lfo;
4use bincode::{Decode, Encode};
5use jgenesis_common::num::GetBit;

Shifted F-num values are 17 bits

8const SHIFTED_F_NUM_MASK: u32 = 0x1FFFF;

Phase counter is 20 bits

11const PHASE_COUNTER_MASK: u32 = 0xFFFFF;

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];
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}