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:
parent
84b115f15f
commit
7a5f23c0a5
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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];
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user