From 7a5f23c0a5eb6d9a72663f7cb018ded26f550ff9 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 10 Apr 2020 22:05:22 -0400 Subject: [PATCH] Adds accommodations for the OPLL. --- Components/OPL2/OPL2.cpp | 78 ++++++++++--------------- Components/OPL2/OPL2.hpp | 122 ++++++++++++++++++++++++++++----------- 2 files changed, 120 insertions(+), 80 deletions(-) diff --git a/Components/OPL2/OPL2.cpp b/Components/OPL2/OPL2.cpp index dc14a8335..e500a8ba0 100644 --- a/Components/OPL2/OPL2.cpp +++ b/Components/OPL2/OPL2.cpp @@ -124,8 +124,10 @@ OPLL::OPLL(Concurrency::DeferringAsyncTaskQueue &task_queue, bool is_vrc7): OPLB patch_set += 8; } - // TODO: install percussion. - (void)percussion_patch_set; + // Install rhythm patches. + for(int c = 0; c < 3; ++c) { + setup_fixed_instrument(c+16, &percussion_patch_set[c * 8]); + } } bool OPLL::is_zero_level() { @@ -169,22 +171,17 @@ void OPLL::write_register(uint8_t address, uint8_t value) { switch(address & 0xf0) { case 0x30: // Select an instrument in the top nibble, set a channel volume in the lower. - channels_[index].output_level = value & 0xf; + channels_[index].overrides.output_level = value & 0xf; channels_[index].modulator = &operators_[(value >> 4) * 2]; break; - case 0x10: - // Set the bottom part of the channel frequency. - channels_[index].frequency = (channels_[index].frequency & ~0xff) | value; - break; + case 0x10: channels_[index].set_frequency_low(value); break; case 0x20: // Set sustain on/off, key on/off, octave and a single extra bit of frequency. // So they're a lot like OPLL registers 0xb0 to 0xb8, but not identical. - channels_[index].frequency = (channels_[index].frequency & 0xff) | (value & 1); - channels_[index].octave = (value >> 1) & 0x7; - channels_[index].key_on = value & 0x10; - channels_[index].hold_sustain_level = value & 0x20; + channels_[index].set_9bit_frequency_octave_key_on(value); + channels_[index].overrides.hold_sustain_level = value & 0x20; break; default: break; @@ -262,6 +259,24 @@ void OPL2::write_register(uint8_t address, uint8_t value) { // Enqueue any changes that affect audio output. task_queue_.enqueue([this, address, value] { + // + // Modal modifications. + // + + switch(address) { + case 0x01: waveform_enable_ = value & 0x20; break; + case 0x08: + // b7: "composite sine wave mode on/off"? + csm_keyboard_split_ = value; + // b6: "Controls the split point of the keyboard. When 0, the keyboard split is the + // second bit from the bit 8 of the F-Number. When 1, the MSB of the F-Number is used." + break; + case 0xbd: depth_rhythm_control_ = value; break; + + default: break; + } + + // // Operator modifications. // @@ -295,42 +310,13 @@ void OPL2::write_register(uint8_t address, uint8_t value) { // Channel modifications. // - if(address >= 0xa0 && address <= 0xd0) { - const auto index = address & 0xf; - if(index > 8) return; + const auto index = address & 0xf; + if(index > 8) return; - switch(address & 0xf0) { - case 0xa0: - channels_[index].frequency = (channels_[index].frequency & ~0xff) | value; - break; - case 0xb0: - channels_[index].frequency = (channels_[index].frequency & 0xff) | ((value & 3) << 8); - channels_[index].octave = (value >> 2) & 0x7; - channels_[index].key_on = value & 0x20;; - break; - case 0xc0: - channels_[index].feedback_strength = (value >> 1) & 0x7; - channels_[index].use_fm_synthesis = value & 1; - break; - } - - return; - } - - - // - // Modal modifications. - // - - switch(address) { - case 0x01: waveform_enable_ = value & 0x20; break; - case 0x08: - // b7: "composite sine wave mode on/off"? - csm_keyboard_split_ = value; - // b6: "Controls the split point of the keyboard. When 0, the keyboard split is the - // second bit from the bit 8 of the F-Number. When 1, the MSB of the F-Number is used." - break; - case 0xbd: depth_rhythm_control_ = value; break; + switch(address & 0xf0) { + case 0xa0: channels_[index].set_frequency_low(value); break; + case 0xb0: channels_[index].set_10bit_frequency_octave_key_on(value); break; + case 0xc0: channels_[index].set_feedback_mode(value); break; default: break; } diff --git a/Components/OPL2/OPL2.hpp b/Components/OPL2/OPL2.hpp index c3f5102c2..4bcfc729f 100644 --- a/Components/OPL2/OPL2.hpp +++ b/Components/OPL2/OPL2.hpp @@ -18,6 +18,31 @@ namespace Yamaha { namespace OPL { +/*! + Describes the ephemeral state of an operator. +*/ +struct OperatorState { + public: + int phase = 0; // Will be in the range [0, 1023], mapping into a 1024-unit sine curve. + int volume = 0; + + private: + int divider_ = 0; + int raw_phase_ = 0; + + friend class Operator; +}; + +/*! + Describes parts of an operator that are genuinely stored per-operator on the OPLL; + these can be provided to the Operator in order to have it ignore its local values + if the host is an OPLL or VRC7. +*/ +struct OperatorOverrides { + int output_level = 0; + bool hold_sustain_level = false; +}; + /*! Models an operator. @@ -70,7 +95,7 @@ class Operator { frequency_multiple = value & 0xf; } - void update(int channel_frequency, int channel_octave) { + void update(OperatorState &state, int channel_frequency, int channel_octave, OperatorOverrides *overrides = nullptr) { // Per the documentation: // F-Num = Music Frequency * 2^(20-Block) / 49716 // @@ -90,9 +115,9 @@ class Operator { // Update the raw phase. const int octave_divider = (10 - channel_octave) << 9; - divider_ += multipliers[frequency_multiple] * channel_frequency; - raw_phase_ += divider_ / octave_divider; - divider_ %= octave_divider; + state.divider_ += multipliers[frequency_multiple] * channel_frequency; + state.raw_phase_ += state.divider_ / octave_divider; + state.divider_ %= octave_divider; // Hence calculate phase (TODO: by also taking account of vibrato). constexpr int waveforms[4][4] = { @@ -101,16 +126,12 @@ class Operator { {511, 511, 511, 511}, // AbsSine: endlessly repeat the first half of the sine wave. {255, 0, 255, 0}, // PulseSine: act as if the first quadrant is in the first and third; lock the other two to 0. }; - phase = raw_phase_ & waveforms[int(waveform)][(raw_phase_ >> 8) & 3]; + state.phase = state.raw_phase_ & waveforms[int(waveform)][(state.raw_phase_ >> 8) & 3]; // TODO: calculate output volume properly; apply: ADSR and amplitude modulation (tremolo, I assume?) - volume = output_level; + state.volume = output_level; } - // Outputs. - int phase = 0; // Will be in the range [0, 1023], mapping into a 1024-unit sine curve. - int volume = 0; - private: /// If true then an amplitude modulation of "3.7Hz" is applied, /// with a depth "determined by the AM-DEPTH of the BD register"? @@ -149,10 +170,6 @@ class Operator { enum class Waveform { Sine, HalfSine, AbsSine, PulseSine } waveform = Waveform::Sine; - - // Ephemeral state. - int raw_phase_ = 0; - int divider_ = 0; }; /*! @@ -160,28 +177,62 @@ class Operator { Assuming FM synthesis is enabled, the channel modulates the output of the carrier with that of the modulator. */ -struct Channel { - /// 'F-Num' in the spec; this plus the current octave determines channel frequency. - int frequency = 0; +class Channel { + public: + /// Sets the low 8 bits of frequency control. + void set_frequency_low(uint8_t value) { + frequency = (frequency &~0xff) | value; + } - /// Linked with the frequency, determines the channel frequency. - int octave = 0; + /// Sets the high two bits of a 10-bit frequency control, along with this channel's + /// block/octave, and key on or off. + void set_10bit_frequency_octave_key_on(uint8_t value) { + frequency = (frequency & 0xff) | ((value & 3) << 8); + octave = (value >> 2) & 0x7; + key_on = value & 0x20;; + } - /// Sets sets this channel on or off, as an input to the ADSR envelope, - bool key_on = false; + /// Sets the high two bits of a 9-bit frequency control, along with this channel's + /// block/octave, and key on or off. + void set_9bit_frequency_octave_key_on(uint8_t value) { + frequency = (frequency & 0xff) | ((value & 1) << 8); + octave = (value >> 1) & 0x7; + key_on = value & 0x10;; + } - /// Sets the degree of feedback applied to the modulator. - int feedback_strength = 0; + /// Sets the amount of feedback provided to the first operator (i.e. the modulator) + /// associated with this channel, and whether FM synthesis is in use. + void set_feedback_mode(uint8_t value) { + feedback_strength = (value >> 1) & 0x7; + use_fm_synthesis = value & 1; + } - /// Selects between FM synthesis, using the modulator to modulate the carrier, or simple mixing of the two - /// underlying operators as completely disjoint entities. - bool use_fm_synthesis = true; + // This should be called at a rate of around 49,716 Hz. + void update(Operator *carrier, Operator *modulator) { + modulator->update(modulator_state_, frequency, octave); + carrier->update(carrier_state_, frequency, octave); + } - // This should be called at a rate of around 49,716 Hz. - void update(Operator *carrier, Operator *modulator) { - modulator->update(frequency, octave); - carrier->update(frequency, octave); - } + private: + /// 'F-Num' in the spec; this plus the current octave determines channel frequency. + int frequency = 0; + + /// Linked with the frequency, determines the channel frequency. + int octave = 0; + + /// Sets sets this channel on or off, as an input to the ADSR envelope, + bool key_on = false; + + /// Sets the degree of feedback applied to the modulator. + int feedback_strength = 0; + + /// Selects between FM synthesis, using the modulator to modulate the carrier, or simple mixing of the two + /// underlying operators as completely disjoint entities. + bool use_fm_synthesis = true; + + // Stored separately because carrier/modulator may not be unique per channel — + // on the OPLL there's an extra level of indirection. + OperatorState carrier_state_, modulator_state_; }; template class OPLBase: public ::Outputs::Speaker::SampleSource { @@ -253,11 +304,14 @@ struct OPLL: public OPLBase { private: friend OPLBase; - Operator operators_[32]; + Operator operators_[38]; // There's an extra level of indirection with the OPLL; these 38 + // operators are to describe 19 hypothetical channels, being + // one user-configurable channel, 15 hard-coded channels, and + // three channels configured for rhythm generation. + struct Channel: public ::Yamaha::OPL::Channel { - int output_level = 0; - bool hold_sustain_level = false; Operator *modulator; // Implicitly, the carrier is modulator+1. + OperatorOverrides overrides; }; Channel channels_[9];