1
0
mirror of https://github.com/TomHarte/CLK.git synced 2025-01-20 21:30:59 +00:00

Adds accommodations for the OPLL.

This commit is contained in:
Thomas Harte 2020-04-10 22:05:22 -04:00
parent 84b115f15f
commit 7a5f23c0a5
2 changed files with 120 additions and 80 deletions

View File

@ -124,8 +124,10 @@ OPLL::OPLL(Concurrency::DeferringAsyncTaskQueue &task_queue, bool is_vrc7): OPLB
patch_set += 8; patch_set += 8;
} }
// TODO: install percussion. // Install rhythm patches.
(void)percussion_patch_set; for(int c = 0; c < 3; ++c) {
setup_fixed_instrument(c+16, &percussion_patch_set[c * 8]);
}
} }
bool OPLL::is_zero_level() { bool OPLL::is_zero_level() {
@ -169,22 +171,17 @@ void OPLL::write_register(uint8_t address, uint8_t value) {
switch(address & 0xf0) { switch(address & 0xf0) {
case 0x30: case 0x30:
// Select an instrument in the top nibble, set a channel volume in the lower. // 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]; channels_[index].modulator = &operators_[(value >> 4) * 2];
break; break;
case 0x10: case 0x10: channels_[index].set_frequency_low(value); break;
// Set the bottom part of the channel frequency.
channels_[index].frequency = (channels_[index].frequency & ~0xff) | value;
break;
case 0x20: case 0x20:
// Set sustain on/off, key on/off, octave and a single extra bit of frequency. // 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. // 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].set_9bit_frequency_octave_key_on(value);
channels_[index].octave = (value >> 1) & 0x7; channels_[index].overrides.hold_sustain_level = value & 0x20;
channels_[index].key_on = value & 0x10;
channels_[index].hold_sustain_level = value & 0x20;
break; break;
default: break; default: break;
@ -262,6 +259,24 @@ void OPL2::write_register(uint8_t address, uint8_t value) {
// Enqueue any changes that affect audio output. // Enqueue any changes that affect audio output.
task_queue_.enqueue([this, address, value] { 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. // Operator modifications.
// //
@ -295,42 +310,13 @@ void OPL2::write_register(uint8_t address, uint8_t value) {
// Channel modifications. // Channel modifications.
// //
if(address >= 0xa0 && address <= 0xd0) { const auto index = address & 0xf;
const auto index = address & 0xf; if(index > 8) return;
if(index > 8) return;
switch(address & 0xf0) { switch(address & 0xf0) {
case 0xa0: case 0xa0: channels_[index].set_frequency_low(value); break;
channels_[index].frequency = (channels_[index].frequency & ~0xff) | value; case 0xb0: channels_[index].set_10bit_frequency_octave_key_on(value); break;
break; case 0xc0: channels_[index].set_feedback_mode(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;
default: break; default: break;
} }

View File

@ -18,6 +18,31 @@ namespace Yamaha {
namespace OPL { 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. Models an operator.
@ -70,7 +95,7 @@ class Operator {
frequency_multiple = value & 0xf; 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: // Per the documentation:
// F-Num = Music Frequency * 2^(20-Block) / 49716 // F-Num = Music Frequency * 2^(20-Block) / 49716
// //
@ -90,9 +115,9 @@ class Operator {
// Update the raw phase. // Update the raw phase.
const int octave_divider = (10 - channel_octave) << 9; const int octave_divider = (10 - channel_octave) << 9;
divider_ += multipliers[frequency_multiple] * channel_frequency; state.divider_ += multipliers[frequency_multiple] * channel_frequency;
raw_phase_ += divider_ / octave_divider; state.raw_phase_ += state.divider_ / octave_divider;
divider_ %= octave_divider; state.divider_ %= octave_divider;
// Hence calculate phase (TODO: by also taking account of vibrato). // Hence calculate phase (TODO: by also taking account of vibrato).
constexpr int waveforms[4][4] = { 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. {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. {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?) // 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: private:
/// If true then an amplitude modulation of "3.7Hz" is applied, /// If true then an amplitude modulation of "3.7Hz" is applied,
/// with a depth "determined by the AM-DEPTH of the BD register"? /// with a depth "determined by the AM-DEPTH of the BD register"?
@ -149,10 +170,6 @@ class Operator {
enum class Waveform { enum class Waveform {
Sine, HalfSine, AbsSine, PulseSine Sine, HalfSine, AbsSine, PulseSine
} waveform = Waveform::Sine; } 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. Assuming FM synthesis is enabled, the channel modulates the output of the carrier with that of the modulator.
*/ */
struct Channel { class Channel {
/// 'F-Num' in the spec; this plus the current octave determines channel frequency. public:
int frequency = 0; /// 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. /// Sets the high two bits of a 10-bit frequency control, along with this channel's
int octave = 0; /// 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, /// Sets the high two bits of a 9-bit frequency control, along with this channel's
bool key_on = false; /// 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. /// Sets the amount of feedback provided to the first operator (i.e. the modulator)
int feedback_strength = 0; /// 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 // This should be called at a rate of around 49,716 Hz.
/// underlying operators as completely disjoint entities. void update(Operator *carrier, Operator *modulator) {
bool use_fm_synthesis = true; modulator->update(modulator_state_, frequency, octave);
carrier->update(carrier_state_, frequency, octave);
}
// This should be called at a rate of around 49,716 Hz. private:
void update(Operator *carrier, Operator *modulator) { /// 'F-Num' in the spec; this plus the current octave determines channel frequency.
modulator->update(frequency, octave); int frequency = 0;
carrier->update(frequency, octave);
} /// 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 <typename Child> class OPLBase: public ::Outputs::Speaker::SampleSource { template <typename Child> class OPLBase: public ::Outputs::Speaker::SampleSource {
@ -253,11 +304,14 @@ struct OPLL: public OPLBase<OPLL> {
private: private:
friend OPLBase<OPLL>; friend OPLBase<OPLL>;
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 { struct Channel: public ::Yamaha::OPL::Channel {
int output_level = 0;
bool hold_sustain_level = false;
Operator *modulator; // Implicitly, the carrier is modulator+1. Operator *modulator; // Implicitly, the carrier is modulator+1.
OperatorOverrides overrides;
}; };
Channel channels_[9]; Channel channels_[9];