lib.rsannotatedlib.rssource1031 lines · 35.6 KB · raw

Yamaha OPLL FM synthesis sound chip. Used in the YM2413 and the NES VRC7 expansion audio chip

This implementation is largely based on reverse engineering work by andete: https://github.com/andete/ym2413

6use bincode::{Decode, Encode};
7use jgenesis_common::num::{GetBit, U16Ext};
8use std::sync::LazyLock;
9use std::{array, cmp};
11type FixedPatches = [[u8; 8]; 15];

Tables from https://www.smspower.org/Development/YM2413ReverseEngineeringNotes2015-03-20

14#[rustfmt::skip]
15const ENVELOPE_INCREMENT_TABLES: [[u8; 8]; 4] =
16    [
17        [0, 1, 0, 1, 0, 1, 0, 1],
18        [0, 1, 0, 1, 1, 1, 0, 1],
19        [0, 1, 1, 1, 0, 1, 1, 1],
20        [0, 1, 1, 1, 1, 1, 1, 1],
21    ];

Numbers are multiplied by 2 here - need to divide by 2 after multiplying

24const MULTIPLIER_TABLE: [u32; 16] = [1, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 20, 24, 24, 30, 30];

Numbers for key_scale_level=3; need to be shifted down for key_scale_level=1 or 2

27const KEY_SCALE_TABLE: [u8; 16] =
28    [0, 48, 64, 74, 80, 86, 90, 94, 96, 100, 102, 104, 106, 108, 110, 112];
30#[derive(Debug, Clone, Copy, Default, Encode, Decode)]
31struct OperatorSettings {
32    tremolo: bool,
33    vibrato: bool,
34    sustained_tone: bool,
35    key_scale_rate: bool,
36    key_scale_level: u8,
37    multiple: u8,
38    wave_rectification: bool,
39    attack_rate: u8,
40    decay_rate: u8,
41    sustain_level: u8,
42    release_rate: u8,
43}
44
45#[derive(Debug, Clone, Copy, Default, Encode, Decode)]
46struct ChannelSettings {
47    block: u8,
48    f_number: u16,
49    sustain: bool,
50    instrument: u8,
51    volume: u8,
52    modulator_feedback_level: u8,
53    modulator_total_level: u8,
54}
55
56#[derive(Debug, Clone, Copy, Default, Encode, Decode)]
57struct PhaseGenerator {
58    counter: u32,
59}
60
61const PHASE_COUNTER_MASK: u32 = (1 << 19) - 1;
62const PHASE_MASK: u32 = (1 << 10) - 1;
63
64impl PhaseGenerator {
65    #[inline]
66    fn clock(&mut self, block: u8, f_number: u16, multiple: u8, fm_position: u8, vibrato: bool) {
67        let fm_shift = if vibrato { compute_fm_shift(fm_position, f_number) } else { 0 };
68
69        let phase_shift = (((2 * u32::from(f_number) + fm_shift as u32)
70            * MULTIPLIER_TABLE[multiple as usize])
71            << block)
72            >> 2;
73        self.counter = self.counter.wrapping_add(phase_shift) & PHASE_COUNTER_MASK;
74    }
75}
76#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Encode, Decode)]
77enum EnvelopePhase {
78    Damp,
79    Attack,
80    Decay,
81    Sustain,
82    #[default]
83    Release,
84}
85
86#[derive(Debug, Clone, Encode, Decode)]
87struct EnvelopeGenerator {
88    operator_type: OperatorType,
89    key_on: bool,
90    attenuation: u8,
91    phase: EnvelopePhase,
92    global_counter: u32,
93}
94
95const MAX_ATTENUATION: u8 = 127;
96
97impl EnvelopeGenerator {
98    fn new(operator_type: OperatorType) -> Self {
99        Self {
100            operator_type,
101            key_on: false,
102            attenuation: MAX_ATTENUATION,
103            phase: EnvelopePhase::Release,
104            global_counter: 0,
105        }
106    }
107
108    fn set_key_on(&mut self, key_on: bool, sustained_tone: bool) {
109        if !self.key_on && key_on {
110            self.phase = EnvelopePhase::Damp;
111        } else if self.key_on && !key_on {
112            self.phase = match (self.operator_type, sustained_tone) {
113                (OperatorType::Carrier, _) | (OperatorType::Modulator, false) => {
114                    EnvelopePhase::Release
115                }
116                (OperatorType::Modulator, true) => EnvelopePhase::Sustain,
117            };
118        }
119        self.key_on = key_on;
120    }
121
122    fn clock(
123        &mut self,
124        operator: OperatorSettings,
125        channel: ChannelSettings,
126        phase_generator: &mut PhaseGenerator,
127        modulator: Option<&mut Operator>,
128    ) {
129        self.global_counter = self.global_counter.wrapping_add(1);
130
131        let sustain_level = operator.sustain_level << 3;
132        let rks = compute_rks(channel.block, channel.f_number, operator.key_scale_rate);
133
134        if self.phase == EnvelopePhase::Damp
135            && self.attenuation >= ENVELOPE_END
136            && self.operator_type == OperatorType::Carrier
137        {
138            if 4 * operator.attack_rate + rks >= 60 {
139                // Skip attack phase if rate is 60-63
140                self.attenuation = 0;
141                self.phase = EnvelopePhase::Decay;
142            } else {
143                self.phase = EnvelopePhase::Attack;
144            }
145            phase_generator.counter = 0;
146
147            if let Some(modulator) = modulator {
148                let modulator_rks =
149                    compute_rks(channel.block, channel.f_number, modulator.settings.key_scale_rate);
150                if 4 * modulator.settings.attack_rate + modulator_rks >= 60 {
151                    modulator.envelope.attenuation = 0;
152                    modulator.envelope.phase = EnvelopePhase::Decay;
153                } else {
154                    modulator.envelope.phase = EnvelopePhase::Attack;
155                }
156                modulator.phase.counter = 0;
157            }
158        }
159
160        if self.phase == EnvelopePhase::Attack && self.attenuation == 0 {
161            self.phase = EnvelopePhase::Decay;
162        }
163
164        if self.phase == EnvelopePhase::Decay && self.attenuation >= sustain_level {
165            self.phase = EnvelopePhase::Sustain;
166        }
167
168        let r = match self.phase {
169            EnvelopePhase::Damp => 12,
170            EnvelopePhase::Attack => operator.attack_rate,
171            EnvelopePhase::Decay => operator.decay_rate,
172            EnvelopePhase::Sustain => {
173                if operator.sustained_tone {
174                    0
175                } else {
176                    operator.release_rate
177                }
178            }
179            EnvelopePhase::Release => {
180                if channel.sustain {
181                    5
182                } else if !operator.sustained_tone {
183                    7
184                } else {
185                    operator.release_rate
186                }
187            }
188        };
189
190        let rate = if r == 0 { 0 } else { cmp::min(63, 4 * r + rks) };
191
192        // Envelope behaviors from:
193        // https://www.smspower.org/Development/YM2413ReverseEngineeringNotes2015-03-20
194        // https://www.smspower.org/Development/YM2413ReverseEngineeringNotes2015-03-27
195        match self.phase {
196            EnvelopePhase::Attack => {
197                match rate {
198                    0..=3 | 60..=63 => {
199                        // Do nothing
200                    }
201                    4..=47 => {
202                        let shift = 13 - (rate >> 2);
203                        let mask = ((1 << shift) - 1) & !0x03;
204                        if self.global_counter & mask == 0 {
205                            let table_idx = (rate & 0x03) as usize;
206                            let increment_idx = ((self.global_counter >> shift) & 0x07) as usize;
207                            let increment = ENVELOPE_INCREMENT_TABLES[table_idx][increment_idx];
208                            if increment == 1 {
209                                self.attenuation -= (self.attenuation >> 4) + 1;
210                            }
211                        }
212                    }
213                    48..=59 => {
214                        let table_idx = (rate & 0x03) as usize;
215                        let increment_idx = ((self.global_counter >> 1) & 0x06) as usize;
216                        let increment = ENVELOPE_INCREMENT_TABLES[table_idx][increment_idx];
217                        let shift = 16 - (rate >> 2) - increment;
218                        self.attenuation -= (self.attenuation >> shift) + 1;
219                    }
220                    _ => panic!("rate must be <= 63"),
221                }
222            }
223            EnvelopePhase::Damp
224            | EnvelopePhase::Decay
225            | EnvelopePhase::Sustain
226            | EnvelopePhase::Release => {
227                match rate {
228                    0..=3 => {
229                        // Do nothing
230                    }
231                    4..=51 => {
232                        let shift = 13 - (rate >> 2);
233                        if self.global_counter & ((1 << shift) - 1) == 0 {
234                            let table_idx = (rate & 0x03) as usize;
235                            let increment_idx = ((self.global_counter >> shift) & 0x07) as usize;
236                            let increment = ENVELOPE_INCREMENT_TABLES[table_idx][increment_idx];
237                            self.attenuation =
238                                cmp::min(MAX_ATTENUATION, self.attenuation + increment);
239                        }
240                    }
241                    52..=55 => {
242                        // Rates 52-55 increment every clock, and each pair of increments gets
243                        // repeated once before moving on to the next pair
244                        let table_idx = (rate & 0x03) as usize;
245                        let increment_idx = (((self.global_counter >> 1) & 0x06)
246                            | (self.global_counter & 0x01))
247                            as usize;
248                        let increment = ENVELOPE_INCREMENT_TABLES[table_idx][increment_idx];
249                        self.attenuation = cmp::min(MAX_ATTENUATION, self.attenuation + increment);
250                    }
251                    56..=59 => {
252                        // Rates 56-59 increment every clock, only use even columns from the table,
253                        // and increment by 1 higher than what's in the table
254                        let table_idx = (rate & 0x03) as usize;
255                        let increment_idx = ((self.global_counter >> 1) & 0x06) as usize;
256                        let increment = ENVELOPE_INCREMENT_TABLES[table_idx][increment_idx] + 1;
257                        self.attenuation = cmp::min(MAX_ATTENUATION, self.attenuation + increment);
258                    }
259                    60..=63 => {
260                        // Always increment by 2
261                        self.attenuation = cmp::min(MAX_ATTENUATION, self.attenuation + 2);
262                    }
263                    _ => panic!("rate should always be <= 63"),
264                }
265            }
266        }
267    }
268}
269
270fn compute_rks(block: u8, f_number: u16, key_scale_rate: bool) -> u8 {
271    ((block << 1) | u8::from(f_number.bit(8))) >> (2 * u8::from(!key_scale_rate))
272}
273
274#[derive(Debug, Clone, Copy, PartialEq, Eq, Encode, Decode)]
275enum OperatorType {
276    Modulator,
277    Carrier,
278}
279
280#[derive(Debug, Clone, Encode, Decode)]
281struct Operator {
282    settings: OperatorSettings,
283    phase: PhaseGenerator,
284    envelope: EnvelopeGenerator,
285    current_output: i32,
286    prev_output: i32,
287}

Operators start outputting 0 once attenuation is >= 124 (out of 127)

290const ENVELOPE_END: u8 = 124;
292impl Operator {
293    fn new(operator_type: OperatorType) -> Self {
294        Self {
295            settings: OperatorSettings::default(),
296            phase: PhaseGenerator::default(),
297            envelope: EnvelopeGenerator::new(operator_type),
298            current_output: 0,
299            prev_output: 0,
300        }
301    }
302
303    fn set_key_on(&mut self, key_on: bool) {
304        self.envelope.set_key_on(key_on, self.settings.sustained_tone);
305    }
306
307    fn clock(
308        &mut self,
309        channel: ChannelSettings,
310        modulation_input: u32,
311        base_attenuation: u8,
312        am_output: u8,
313        fm_position: u8,
314        modulator: Option<&mut Operator>,
315    ) -> i32 {
316        let block = channel.block;
317        let f_number = channel.f_number;
318
319        self.phase.clock(
320            block,
321            f_number,
322            self.settings.multiple,
323            fm_position,
324            self.settings.vibrato,
325        );
326        self.envelope.clock(self.settings, channel, &mut self.phase, modulator);
327
328        if self.envelope.attenuation >= ENVELOPE_END {
329            self.prev_output = self.current_output;
330            self.current_output = 0;
331            return 0;
332        }
333
334        // Phase counter is 19 bits, log-sin table is a 10-bit loookup
335        let adjusted_phase = (self.phase.counter >> 9).wrapping_add(modulation_input) & PHASE_MASK;
336        let (sine_attenuation, sign) = log_sine_lookup(adjusted_phase);
337
338        let key_scale_level = self.settings.key_scale_level;
339        let key_scale_attenuation = if key_scale_level != 0 {
340            KEY_SCALE_TABLE[(f_number >> 5) as usize].saturating_sub((7 - block) << 4)
341                >> (3 - key_scale_level)
342        } else {
343            0
344        };
345
346        let am_attenuation = if self.settings.tremolo { am_output } else { 0 };
347
348        let total_attenuation = cmp::min(
349            u16::from(MAX_ATTENUATION),
350            u16::from(base_attenuation)
351                + u16::from(key_scale_attenuation)
352                + u16::from(self.envelope.attenuation)
353                + u16::from(am_attenuation),
354        );
355        let amplitude_magnitude = exp2_lookup(sine_attenuation + 16 * total_attenuation);
356
357        let amplitude = match (sign, self.settings.wave_rectification) {
358            (Sign::Positive, _) => i32::from(amplitude_magnitude),
359            (Sign::Negative, false) => -i32::from(amplitude_magnitude),
360            (Sign::Negative, true) => 0,
361        };
362
363        self.prev_output = self.current_output;
364        self.current_output = amplitude;
365        amplitude
366    }
367}
368
369fn compute_fm_shift(fm_position: u8, f_number: u16) -> i16 {
370    // Based on https://www.smspower.org/Development/YM2413ReverseEngineeringNotes2015-12-01
371    let f_num_high_bits = f_number >> 6;
372    let magnitude = match fm_position & 0x03 {
373        0 => 0,
374        1 | 3 => (f_num_high_bits >> 1) as i16,
375        2 => f_num_high_bits as i16,
376        _ => unreachable!("value & 0x03 is always <= 3"),
377    };
378    let sign = if fm_position.bit(3) { -1 } else { 1 };
379    sign * magnitude
380}
381
382#[derive(Debug, Clone, Copy, PartialEq, Eq)]
383enum Sign {
384    Positive,
385    Negative,
386}

Returns the attenuation for the given phase, in log2 decibels units log-sin[i] = -log2(sin((i + 0.5) / 256 * PI/2)) * 256 Output range is 0..=2137 Source: https://www.smspower.org/Development/YM2413ReverseEngineeringNotes2015-04-09

392fn log_sine_lookup(phase: u32) -> (u16, Sign) {
393    static LOOKUP_TABLE: LazyLock<[(u16, Sign); 1024]> = LazyLock::new(|| {
394        let quarter_table: [u16; 256] = array::from_fn(|i| {
395            let sine = ((i as f64 + 0.5) / 256.0 * std::f64::consts::PI / 2.0).sin();
396            (-sine.log2() * 256.0).round() as u16
397        });
398
399        array::from_fn(|i| match i {
400            0..=255 => (quarter_table[i], Sign::Positive),
401            256..=511 => (quarter_table[255 - (i & 0xFF)], Sign::Positive),
402            512..=767 => (quarter_table[i & 0xFF], Sign::Negative),
403            768..=1023 => (quarter_table[255 - (i & 0xFF)], Sign::Negative),
404            _ => unreachable!("array::from_fn with array of size 1024"),
405        })
406    });
407
408    LOOKUP_TABLE[phase as usize]
409}

Returns a 12-bit unsigned amplitude, assuming the input is an attenuation in log2 decibels units Output range is 0..=4084 Source: https://www.smspower.org/Development/YM2413ReverseEngineeringNotes2015-04-09

414#[allow(clippy::items_after_statements)]
415fn exp2_lookup(attenuation: u16) -> u16 {
416    let [attenuation_lsb, attenuation_msb] = attenuation.to_le_bytes();
417
418    if attenuation_msb >= 16 {
419        return 0;
420    }
421
422    static LOOKUP_TABLE: LazyLock<[u16; 256]> = LazyLock::new(|| {
423        array::from_fn(|i| (2.0_f64.powf((255 - i) as f64 / 256.0) * 1024.0).round() as u16 - 1024)
424    });
425
426    ((LOOKUP_TABLE[attenuation_lsb as usize] + 1024) << 1) >> attenuation_msb
427}
429fn compute_amplitude(attenuation: u16, sign: Sign) -> i32 {
430    let magnitude = exp2_lookup(attenuation);
431    match sign {
432        Sign::Positive => magnitude.into(),
433        Sign::Negative => -i32::from(magnitude),
434    }
435}
436
437#[derive(Debug, Clone, Encode, Decode)]
438struct Channel {
439    fixed_patches: FixedPatches,
440    modulator: Operator,
441    carrier: Operator,
442    settings: ChannelSettings,
443    // Used for tom-tom
444    modulator_volume_override: Option<u8>,
445}
446
447impl Channel {
448    fn new(fixed_patches: FixedPatches) -> Self {
449        Self {
450            fixed_patches,
451            modulator: Operator::new(OperatorType::Modulator),
452            carrier: Operator::new(OperatorType::Carrier),
453            settings: ChannelSettings::default(),
454            modulator_volume_override: None,
455        }
456    }
457
458    fn write_register_1(&mut self, value: u8) {
459        self.settings.f_number.set_lsb(value);
460
461        log::trace!("F-number: {:03X}", self.settings.f_number);
462    }
463
464    fn write_register_2(&mut self, value: u8) {
465        self.settings.f_number.set_msb(value & 0x01);
466        self.settings.block = (value >> 1) & 0x07;
467        self.settings.sustain = value.bit(5);
468
469        log::trace!(
470            "F-number: {:03X}, Block: {}, Channel Sustain: {}",
471            self.settings.f_number,
472            self.settings.block,
473            self.settings.sustain
474        );
475
476        self.set_key_on(value.bit(4));
477    }
478
479    fn write_register_3(&mut self, value: u8) {
480        self.settings.volume = value & 0x0F;
481        self.settings.instrument = value >> 4;
482
483        log::trace!(
484            "Volume: {:02X}, Instrument: {}",
485            self.settings.volume,
486            self.settings.instrument
487        );
488    }
489
490    fn set_key_on(&mut self, key_on: bool) {
491        if self.modulator.envelope.key_on != key_on {
492            log::trace!("State at key on ({key_on}): {self:?}");
493        }
494
495        self.modulator.set_key_on(key_on);
496        self.carrier.set_key_on(key_on);
497    }
498
499    fn reload_instrument(&mut self, custom_instrument_patch: [u8; 8]) {
500        let instrument_idx = self.settings.instrument;
501        let instrument = match instrument_idx {
502            0 => Instrument::from_patch(custom_instrument_patch),
503            _ => Instrument::from_patch(self.fixed_patches[(instrument_idx - 1) as usize]),
504        };
505
506        self.load_instrument(instrument);
507    }
508
509    fn load_instrument(&mut self, instrument: Instrument) {
510        self.modulator.settings = instrument.modulator;
511        self.carrier.settings = instrument.carrier;
512        self.settings.modulator_feedback_level = instrument.modulator_feedback_level;
513        self.settings.modulator_total_level = instrument.modulator_total_level;
514    }
515
516    fn clock(&mut self, am_output: u8, fm_position: u8) {
517        let modulation_feedback = match self.settings.modulator_feedback_level {
518            0 => 0,
519            feedback_level => {
520                (self.modulator.prev_output + self.modulator.current_output) >> (9 - feedback_level)
521            }
522        };
523        let modulator_base_attenuation =
524            self.modulator_volume_override.unwrap_or(self.settings.modulator_total_level << 1);
525        let modulator_output = self.modulator.clock(
526            self.settings,
527            modulation_feedback as u32,
528            modulator_base_attenuation,
529            am_output,
530            fm_position,
531            None,
532        );
533
534        self.carrier.clock(
535            self.settings,
536            modulator_output as u32,
537            self.settings.volume << 3,
538            am_output,
539            fm_position,
540            Some(&mut self.modulator),
541        );
542    }
543
544    fn sample(&self) -> i32 {
545        self.carrier.current_output >> 4
546    }
547}
548
549struct Instrument {
550    modulator: OperatorSettings,
551    carrier: OperatorSettings,
552    modulator_feedback_level: u8,
553    modulator_total_level: u8,
554}
555
556impl Instrument {
557    fn from_patch(patch: [u8; 8]) -> Self {
558        Self {
559            modulator: OperatorSettings {
560                tremolo: patch[0].bit(7),
561                vibrato: patch[0].bit(6),
562                sustained_tone: patch[0].bit(5),
563                key_scale_rate: patch[0].bit(4),
564                key_scale_level: patch[2] >> 6,
565                multiple: patch[0] & 0x0F,
566                wave_rectification: patch[3].bit(3),
567                attack_rate: patch[4] >> 4,
568                decay_rate: patch[4] & 0x0F,
569                sustain_level: patch[6] >> 4,
570                release_rate: patch[6] & 0x0F,
571            },
572            carrier: OperatorSettings {
573                tremolo: patch[1].bit(7),
574                vibrato: patch[1].bit(6),
575                sustained_tone: patch[1].bit(5),
576                key_scale_rate: patch[1].bit(4),
577                key_scale_level: patch[3] >> 6,
578                multiple: patch[1] & 0x0F,
579                wave_rectification: patch[3].bit(4),
580                attack_rate: patch[5] >> 4,
581                decay_rate: patch[5] & 0x0F,
582                sustain_level: patch[7] >> 4,
583                release_rate: patch[7] & 0x0F,
584            },
585            modulator_feedback_level: patch[3] & 0x07,
586            modulator_total_level: patch[2] & 0x3F,
587        }
588    }
589}
590
591#[derive(Debug, Clone, Encode, Decode)]
592struct AmUnit {
593    position: u8,
594    divider: u8,
595}
596
597const AM_DIVIDER: u8 = 64;
598const AM_POSITIONS: u8 = 210;
599
600impl AmUnit {
601    fn new() -> Self {
602        Self { position: 0, divider: AM_DIVIDER }
603    }
604
605    fn clock(&mut self) {
606        self.divider -= 1;
607        if self.divider == 0 {
608            self.divider = AM_DIVIDER;
609            self.position = (self.position + 1) % AM_POSITIONS;
610        }
611    }
612
613    fn output(&self) -> u8 {
614        // Based on https://www.smspower.org/Development/YM2413ReverseEngineeringNotes2015-11-28
615        match self.position {
616            0..=2 => 0,
617            3..=109 => (self.position - 3) >> 3,
618            110..=209 => 12 - ((self.position - 110) >> 3),
619            _ => panic!("AM position must be <= 209"),
620        }
621    }
622}
623
624#[derive(Debug, Clone, Encode, Decode)]
625struct FmUnit {
626    position: u8,
627    divider: u16,
628}
629
630const FM_DIVIDER: u16 = 1024;
631const FM_POSITIONS: u8 = 8;
632
633impl FmUnit {
634    fn new() -> Self {
635        Self { position: 0, divider: FM_DIVIDER }
636    }
637
638    fn clock(&mut self) {
639        self.divider -= 1;
640        if self.divider == 0 {
641            self.divider = FM_DIVIDER;
642            self.position = (self.position + 1) % FM_POSITIONS;
643        }
644    }
645}
646
647#[derive(Debug, Clone, Default, Encode, Decode)]
648struct RhythmSettings {
649    snare_drum_volume: u8,
650    snare_drum_on: bool,
651    tom_tom_volume: u8,
652    tom_tom_on: bool,
653    top_cymbal_volume: u8,
654    top_cymbal_on: bool,
655    high_hat_volume: u8,
656    high_hat_on: bool,
657}
658
659#[derive(Debug, Clone, Encode, Decode)]
660pub struct Opll<const CHANNELS: usize, const RHYTHM: bool> {
661    channels: [Channel; CHANNELS],
662    rhythm_mode_enabled: bool,
663    rhythm_settings: RhythmSettings,
664    lfsr: u32,
665    am_unit: AmUnit,
666    fm_unit: FmUnit,
667    selected_register: u8,
668    custom_instrument_patch: [u8; 8],
669    divider: u8,
670    clock_interval: u8,
671}
672
673const MAX_CARRIER_OUTPUT: f64 = 255.0;
674
675impl<const CHANNELS: usize, const RHYTHM: bool> Opll<CHANNELS, RHYTHM> {
676    fn new(fixed_patches: FixedPatches, clock_interval: u8) -> Self {
677        assert_ne!(clock_interval, 0, "OPLL clock interval must be non-zero");
678
679        Self {
680            channels: array::from_fn(|_| Channel::new(fixed_patches)),
681            rhythm_mode_enabled: false,
682            rhythm_settings: RhythmSettings::default(),
683            lfsr: 1,
684            am_unit: AmUnit::new(),
685            fm_unit: FmUnit::new(),
686            selected_register: 0,
687            custom_instrument_patch: [0; 8],
688            divider: clock_interval,
689            clock_interval,
690        }
691    }
692
693    pub fn select_register(&mut self, register: u8) {
694        self.selected_register = register;
695    }
696
697    pub fn write_data(&mut self, value: u8) {
698        log::trace!("Write to register {:02X}: {value:02X}", self.selected_register);
699
700        match self.selected_register {
701            register @ 0x00..=0x07 => {
702                self.custom_instrument_patch[register as usize] = value;
703
704                // Immediately reload any channels using custom instrument
705                let end_idx = if RHYTHM && self.rhythm_mode_enabled { 6 } else { CHANNELS };
706                for channel in &mut self.channels[..end_idx] {
707                    if channel.settings.instrument == 0 {
708                        channel
709                            .load_instrument(Instrument::from_patch(self.custom_instrument_patch));
710                    }
711                }
712            }
713            0x0E if RHYTHM => {
714                self.handle_rhythm_register_write(value);
715            }
716            register @ 0x10..=0x18 => {
717                let channel = register & 0x0F;
718                if channel < CHANNELS as u8 {
719                    self.channels[channel as usize].write_register_1(value);
720                }
721            }
722            register @ 0x20..=0x28 => {
723                let channel = register & 0x0F;
724                if channel < CHANNELS as u8 {
725                    self.channels[channel as usize].write_register_2(value);
726                }
727            }
728            register @ 0x30..=0x38 => {
729                let channel = register & 0x0F;
730                if channel < CHANNELS as u8 {
731                    self.channels[channel as usize].write_register_3(value);
732                }
733
734                if channel < 6 || (RHYTHM && !self.rhythm_mode_enabled) {
735                    self.channels[channel as usize].reload_instrument(self.custom_instrument_patch);
736                }
737
738                if RHYTHM {
739                    // Rhythm volume writes
740                    match channel {
741                        // No need to special case bass drum volume; it uses channel 6 volume normally
742                        7 => {
743                            self.rhythm_settings.high_hat_volume = value >> 4;
744                            self.rhythm_settings.snare_drum_volume = value & 0x0F;
745                        }
746                        8 => {
747                            let tom_tom_volume = value >> 4;
748                            self.rhythm_settings.tom_tom_volume = tom_tom_volume;
749                            self.rhythm_settings.top_cymbal_volume = value & 0x0F;
750
751                            if self.rhythm_mode_enabled {
752                                self.channels[8].modulator_volume_override =
753                                    Some(tom_tom_volume << 3);
754                            }
755                        }
756                        _ => {}
757                    }
758                }
759            }
760            _ => {}
761        }
762    }
763
764    fn handle_rhythm_register_write(&mut self, value: u8) {
765        if !RHYTHM {
766            return;
767        }
768
769        let rhythm_mode_enabled = value.bit(5);
770        if rhythm_mode_enabled != self.rhythm_mode_enabled {
771            if rhythm_mode_enabled {
772                self.channels[6].load_instrument(Instrument::from_patch(BASS_DRUM_PATCH));
773                self.channels[7].load_instrument(Instrument::from_patch(SNARE_DRUM_HIGH_HAT_PATCH));
774                self.channels[8].load_instrument(Instrument::from_patch(TOM_TOM_TOP_CYMBAL_PATCH));
775
776                self.channels[8].modulator_volume_override =
777                    Some(self.rhythm_settings.tom_tom_volume);
778            } else {
779                self.channels[6].reload_instrument(self.custom_instrument_patch);
780                self.channels[7].reload_instrument(self.custom_instrument_patch);
781                self.channels[8].reload_instrument(self.custom_instrument_patch);
782
783                self.channels[8].modulator_volume_override = None;
784
785                // TODO not sure this is right, but it fixes sounds in OutRun
786                self.channels[6].set_key_on(false);
787                self.channels[7].set_key_on(false);
788                self.channels[8].set_key_on(false);
789            }
790        }
791        self.rhythm_mode_enabled = rhythm_mode_enabled;
792
793        log::trace!("  Rhythm mode enabled: {rhythm_mode_enabled}");
794
795        if rhythm_mode_enabled {
796            let bass_drum_on = value.bit(4);
797            let snare_drum_on = value.bit(3);
798            let tom_tom_on = value.bit(2);
799            let top_cymbal_on = value.bit(1);
800            let high_hat_on = value.bit(0);
801
802            self.channels[6].set_key_on(bass_drum_on);
803            self.channels[7].set_key_on(snare_drum_on || high_hat_on);
804            self.channels[8].set_key_on(tom_tom_on || top_cymbal_on);
805
806            self.rhythm_settings.snare_drum_on = snare_drum_on;
807            self.rhythm_settings.tom_tom_on = tom_tom_on;
808            self.rhythm_settings.top_cymbal_on = top_cymbal_on;
809            self.rhythm_settings.high_hat_on = high_hat_on;
810
811            log::trace!("  Bass drum on: {}", value.bit(4));
812            log::trace!("  Snare drum on: {}", value.bit(3));
813            log::trace!("  Tom-tom on: {}", value.bit(2));
814            log::trace!("  Top cymbal on: {}", value.bit(1));
815            log::trace!("  High hat on: {}", value.bit(0));
816        }
817    }
818
819    pub fn tick(&mut self) {
820        self.divider -= 1;
821        if self.divider == 0 {
822            self.divider = self.clock_interval;
823            self.clock();
824        }
825    }
826
827    fn clock(&mut self) {
828        self.am_unit.clock();
829        self.fm_unit.clock();
830        self.shift_lfsr();
831
832        let am_output = self.am_unit.output();
833        let fm_position = self.fm_unit.position;
834        for channel in &mut self.channels {
835            channel.clock(am_output, fm_position);
836        }
837    }
838
839    fn shift_lfsr(&mut self) {
840        let xor_operand = if self.lfsr.bit(0) {
841            // Flip bits 22, 8, 7, and 0
842            0x400181
843        } else {
844            0
845        };
846        self.lfsr = (self.lfsr >> 1) ^ xor_operand;
847    }
848
849    #[must_use]
850    pub fn sample(&self) -> f64 {
851        let sample = if RHYTHM && self.rhythm_mode_enabled {
852            let melodic = self.channels[..6]
853                .iter()
854                .map(|channel| f64::from(channel.sample()) / MAX_CARRIER_OUTPUT)
855                .sum::<f64>();
856            let bass_drum = f64::from(self.channels[6].sample()) / MAX_CARRIER_OUTPUT;
857            let snare_drum = self.snare_drum_sample();
858            let tom_tom = self.tom_tom_sample();
859            let top_cymbal = self.top_cymbal_sample();
860            let high_hat = self.high_hat_sample();
861            melodic + 2.0 * (bass_drum + snare_drum + tom_tom + top_cymbal + high_hat)
862        } else {
863            self.channels
864                .iter()
865                .map(|channel| f64::from(channel.sample()) / MAX_CARRIER_OUTPUT)
866                .sum::<f64>()
867        };
868
869        (sample / CHANNELS as f64).clamp(-1.0, 1.0)
870    }
871
872    // Rhythm instrument formulas based on https://github.com/andete/ym2413/blob/master/results/rhythm/rhythm.md
873
874    fn snare_drum_sample(&self) -> f64 {
875        let operator = &self.channels[7].carrier;
876
877        if !self.rhythm_settings.snare_drum_on || operator.envelope.attenuation >= ENVELOPE_END {
878            return 0.0;
879        }
880
881        let phase = operator.phase.counter.bit(18);
882        let (sine_attenuation, sign) = match (self.lfsr.bit(0), phase) {
883            (false, false) | (true, true) => log_sine_lookup(0),
884            (false, true) => (0, Sign::Negative),
885            (true, false) => (0, Sign::Positive),
886        };
887
888        let total_attenuation = rhythm_attenuation(
889            operator.envelope.attenuation,
890            self.rhythm_settings.snare_drum_volume,
891        );
892        let amplitude = compute_amplitude(sine_attenuation + 16 * total_attenuation, sign) >> 4;
893        f64::from(amplitude) / MAX_CARRIER_OUTPUT
894    }
895
896    fn tom_tom_sample(&self) -> f64 {
897        if self.rhythm_settings.tom_tom_on {
898            f64::from(self.channels[8].modulator.current_output >> 4) / MAX_CARRIER_OUTPUT
899        } else {
900            0.0
901        }
902    }
903
904    fn top_cymbal_sample(&self) -> f64 {
905        let operator = &self.channels[8].carrier;
906
907        if !self.rhythm_settings.top_cymbal_on || operator.envelope.attenuation >= ENVELOPE_END {
908            return 0.0;
909        }
910
911        let sign = if self.top_cymbal_high_hat_phase() { Sign::Positive } else { Sign::Negative };
912
913        let total_attenuation = rhythm_attenuation(
914            operator.envelope.attenuation,
915            self.rhythm_settings.top_cymbal_volume,
916        );
917        // Sine attenuation is always 0
918        let amplitude = compute_amplitude(16 * total_attenuation, sign) >> 4;
919        f64::from(amplitude) / MAX_CARRIER_OUTPUT
920    }
921
922    fn high_hat_sample(&self) -> f64 {
923        let operator = &self.channels[7].modulator;
924
925        if !self.rhythm_settings.high_hat_on || operator.envelope.attenuation >= ENVELOPE_END {
926            return 0.0;
927        }
928
929        let phase = match (self.lfsr.bit(0), self.top_cymbal_high_hat_phase()) {
930            (false, false) => 0x2D0,
931            (false, true) => 0x34,
932            (true, false) => 0x234,
933            (true, true) => 0xD0,
934        };
935        let (sine_attenuation, sign) = log_sine_lookup(phase);
936
937        let total_attenuation =
938            rhythm_attenuation(operator.envelope.attenuation, self.rhythm_settings.high_hat_volume);
939        let amplitude = compute_amplitude(sine_attenuation + 16 * total_attenuation, sign) >> 4;
940        f64::from(amplitude) / MAX_CARRIER_OUTPUT
941    }
942
943    fn top_cymbal_high_hat_phase(&self) -> bool {
944        let c8_phase = self.channels[8].carrier.phase.counter >> 9;
945        let m7_phase = self.channels[7].modulator.phase.counter >> 9;
946
947        let c8_3 = c8_phase.bit(3);
948        let c8_5 = c8_phase.bit(5);
949        let m7_2 = m7_phase.bit(2);
950        let m7_3 = m7_phase.bit(3);
951        let m7_7 = m7_phase.bit(7);
952
953        (c8_5 ^ c8_3) && (m7_7 ^ m7_2) && (c8_5 ^ m7_3)
954    }
955}
956
957fn rhythm_attenuation(envelope_attenuation: u8, volume: u8) -> u16 {
958    cmp::min(u16::from(MAX_ATTENUATION), u16::from(envelope_attenuation) + u16::from(volume << 3))
959}

YM2413 built-in instrument and rhythm patches from: https://siliconpr0n.org/archive/doku.php?id=vendor:yamaha:opl2#ym2413_instrument_rom

963const YM2413_INSTRUMENT_PATCHES: FixedPatches = [
964    [0x71, 0x61, 0x1E, 0x17, 0xD0, 0x78, 0x00, 0x17],
965    [0x13, 0x41, 0x1A, 0x0D, 0xD8, 0xF7, 0x23, 0x13],
966    [0x13, 0x01, 0x99, 0x00, 0xF2, 0xC4, 0x11, 0x23],
967    [0x31, 0x61, 0x0E, 0x07, 0xA8, 0x64, 0x70, 0x27],
968    [0x32, 0x21, 0x1E, 0x06, 0xE0, 0x76, 0x00, 0x28],
969    [0x31, 0x22, 0x16, 0x05, 0xE0, 0x71, 0x00, 0x18],
970    [0x21, 0x61, 0x1D, 0x07, 0x82, 0x81, 0x10, 0x07],
971    [0x23, 0x21, 0x2D, 0x14, 0xA2, 0x72, 0x00, 0x07],
972    [0x61, 0x61, 0x1B, 0x06, 0x64, 0x65, 0x10, 0x17],
973    [0x41, 0x61, 0x0B, 0x18, 0x85, 0xF7, 0x71, 0x07],
974    [0x13, 0x01, 0x83, 0x11, 0xFA, 0xE4, 0x10, 0x04],
975    [0x17, 0xC1, 0x24, 0x07, 0xF8, 0xF8, 0x22, 0x12],
976    [0x61, 0x50, 0x0C, 0x05, 0xC2, 0xF5, 0x20, 0x42],
977    [0x01, 0x01, 0x55, 0x03, 0xC9, 0x95, 0x03, 0x02],
978    [0x61, 0x41, 0x89, 0x03, 0xF1, 0xE4, 0x40, 0x13],
979];
981const BASS_DRUM_PATCH: [u8; 8] = [0x01, 0x01, 0x18, 0x0F, 0xDF, 0xF8, 0x6A, 0x6D];
982const SNARE_DRUM_HIGH_HAT_PATCH: [u8; 8] = [0x01, 0x01, 0x00, 0x00, 0xC8, 0xD8, 0xA7, 0x68];
983const TOM_TOM_TOP_CYMBAL_PATCH: [u8; 8] = [0x05, 0x01, 0x00, 0x00, 0xF8, 0xAA, 0x59, 0x55];

From https://www.nesdev.org/wiki/VRC7_audio#Internal_patch_set Indexed into using (instrument # - 1) since 0 is custom instrument

987const VRC7_INSTRUMENT_PATCHES: FixedPatches = [
988    // $01: Buzzy bell
989    [0x03, 0x21, 0x05, 0x06, 0xE8, 0x81, 0x42, 0x27],
990    // $02: Guitar
991    [0x13, 0x41, 0x14, 0x0D, 0xD8, 0xF6, 0x23, 0x12],
992    // $02: Wurly
993    [0x11, 0x11, 0x08, 0x08, 0xFA, 0xB2, 0x20, 0x12],
994    // $04: Flute
995    [0x31, 0x61, 0x0C, 0x07, 0xA8, 0x64, 0x61, 0x27],
996    // $05: Clarinet
997    [0x32, 0x21, 0x1E, 0x06, 0xE1, 0x76, 0x01, 0x28],
998    // $06: Synth
999    [0x02, 0x01, 0x06, 0x00, 0xA3, 0xE2, 0xF4, 0xF4],
1000    // $07: Trumpet
1001    [0x21, 0x61, 0x1D, 0x07, 0x82, 0x81, 0x11, 0x07],
1002    // $08: Organ
1003    [0x23, 0x21, 0x22, 0x17, 0xA2, 0x72, 0x01, 0x17],
1004    // $09: Bells
1005    [0x35, 0x11, 0x25, 0x00, 0x40, 0x73, 0x72, 0x01],
1006    // $0A: Vibes
1007    [0xB5, 0x01, 0x0F, 0x0F, 0xA8, 0xA5, 0x51, 0x02],
1008    // $0B: Vibraphone
1009    [0x17, 0xC1, 0x24, 0x07, 0xF8, 0xF8, 0x22, 0x12],
1010    // $0C: Tutti
1011    [0x71, 0x23, 0x11, 0x06, 0x65, 0x74, 0x18, 0x16],
1012    // $0D: Fretless
1013    [0x01, 0x02, 0xD3, 0x05, 0xC9, 0x95, 0x03, 0x02],
1014    // $0E: Synth bass
1015    [0x61, 0x63, 0x0C, 0x00, 0x94, 0xC0, 0x33, 0xF6],
1016    // $0F: Sweep
1017    [0x21, 0x72, 0x0D, 0x00, 0xC1, 0xD5, 0x56, 0x06],
1018];
1020pub type Ym2413 = Opll<9, true>;
1021pub type Vrc7AudioUnit = Opll<6, false>;
1022
1023#[must_use]
1024pub fn new_ym2413(clock_interval: u8) -> Ym2413 {
1025    Ym2413::new(YM2413_INSTRUMENT_PATCHES, clock_interval)
1026}
1027
1028#[must_use]
1029pub fn new_vrc7(clock_interval: u8) -> Vrc7AudioUnit {
1030    Vrc7AudioUnit::new(VRC7_INSTRUMENT_PATCHES, clock_interval)
1031}