1//! Yamaha OPLL FM synthesis sound chip. Used in the YM2413 and the NES VRC7 expansion audio chip 2//! 3//! This implementation is largely based on reverse engineering work by andete: 4//! <https://github.com/andete/ym2413> 5 6use bincode::{Decode, Encode}; 7use jgenesis_common::num::{GetBit, U16Ext}; 8use std::sync::LazyLock; 9use std::{array, cmp}; 10 11type FixedPatches = [[u8; 8]; 15]; 12 13// 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 ]; 22 23// 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]; 25 26// 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]; 29 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} 288 289// Operators start outputting 0 once attenuation is >= 124 (out of 127) 290const ENVELOPE_END: u8 = 124; 291 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} 387 388// Returns the *attenuation* for the given phase, in log2 decibels units 389// log-sin[i] = -log2(sin((i + 0.5) / 256 * PI/2)) * 256 390// Output range is 0..=2137 391// 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} 410 411// Returns a 12-bit unsigned amplitude, assuming the input is an attenuation in log2 decibels units 412// Output range is 0..=4084 413// 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} 428 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} 960 961// YM2413 built-in instrument and rhythm patches from: 962// 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]; 980 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]; 984 985// From https://www.nesdev.org/wiki/VRC7_audio#Internal_patch_set 986// 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]; 1019 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}