mirror of
https://github.com/TomHarte/CLK.git
synced 2026-04-25 11:17:26 +00:00
Finish updating components.
This commit is contained in:
@@ -31,229 +31,229 @@ namespace Yamaha::OPL {
|
||||
TODO: use envelope_precision.
|
||||
*/
|
||||
template <int envelope_precision, int period_precision> class EnvelopeGenerator {
|
||||
public:
|
||||
/*!
|
||||
Advances the envelope generator a single step, given the current state of the low-frequency oscillator, @c oscillator.
|
||||
*/
|
||||
void update(const LowFrequencyOscillator &oscillator) {
|
||||
// Apply tremolo, which is fairly easy.
|
||||
tremolo_ = tremolo_enable_ * oscillator.tremolo << 4;
|
||||
public:
|
||||
/*!
|
||||
Advances the envelope generator a single step, given the current state of the low-frequency oscillator, @c oscillator.
|
||||
*/
|
||||
void update(const LowFrequencyOscillator &oscillator) {
|
||||
// Apply tremolo, which is fairly easy.
|
||||
tremolo_ = tremolo_enable_ * oscillator.tremolo << 4;
|
||||
|
||||
// Something something something...
|
||||
const int key_scaling_rate = key_scale_rate_ >> key_scale_rate_shift_;
|
||||
switch(phase_) {
|
||||
case Phase::Damp:
|
||||
update_decay(oscillator, 12 << 2);
|
||||
if(attenuation_ == 511) {
|
||||
(*will_attack_)();
|
||||
phase_ = Phase::Attack;
|
||||
}
|
||||
break;
|
||||
|
||||
case Phase::Attack:
|
||||
update_attack(oscillator, attack_rate_ + key_scaling_rate);
|
||||
|
||||
// Two possible terminating conditions: (i) the attack rate is 15; (ii) full volume has been reached.
|
||||
if(attenuation_ <= 0) {
|
||||
attenuation_ = 0;
|
||||
phase_ = Phase::Decay;
|
||||
}
|
||||
break;
|
||||
|
||||
case Phase::Decay:
|
||||
update_decay(oscillator, decay_rate_ + key_scaling_rate);
|
||||
if(attenuation_ >= sustain_level_) {
|
||||
attenuation_ = sustain_level_;
|
||||
phase_ = use_sustain_level_ ? Phase::Sustain : Phase::Release;
|
||||
}
|
||||
break;
|
||||
|
||||
case Phase::Sustain:
|
||||
// Nothing to do.
|
||||
break;
|
||||
|
||||
case Phase::Release:
|
||||
update_decay(oscillator, release_rate_ + key_scaling_rate);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
@returns The current attenuation from this envelope generator. This is independent of the envelope precision.
|
||||
*/
|
||||
int attenuation() const {
|
||||
// TODO: if this envelope is fully released, should tremolo still be able to vocalise it?
|
||||
return (attenuation_ << 3) + tremolo_;
|
||||
}
|
||||
|
||||
/*!
|
||||
Enables or disables damping on this envelope generator. If damping is enabled then this envelope generator will
|
||||
use the damping phase when necessary (i.e. when transitioning to key on if attenuation is not already at maximum)
|
||||
and in any case will call @c will_attack before transitioning from any other state to attack.
|
||||
|
||||
@param will_attack Supply a will_attack callback to enable damping mode; supply nullopt to disable damping mode.
|
||||
*/
|
||||
void set_should_damp(const std::optional<std::function<void(void)>> &will_attack) {
|
||||
will_attack_ = will_attack;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the current state of the key-on input.
|
||||
*/
|
||||
void set_key_on(bool key_on) {
|
||||
// Do nothing if this is not a leading or trailing edge.
|
||||
if(key_on == key_on_) return;
|
||||
key_on_ = key_on;
|
||||
|
||||
// Always transition to release upon a key off.
|
||||
if(!key_on_) {
|
||||
phase_ = Phase::Release;
|
||||
return;
|
||||
}
|
||||
|
||||
// On key on: if this is an envelope generator with damping, and damping is required,
|
||||
// schedule that. If damping is not required, announce a pending attack now and
|
||||
// transition to attack.
|
||||
if(will_attack_) {
|
||||
if(attenuation_ != 511) {
|
||||
phase_ = Phase::Damp;
|
||||
return;
|
||||
// Something something something...
|
||||
const int key_scaling_rate = key_scale_rate_ >> key_scale_rate_shift_;
|
||||
switch(phase_) {
|
||||
case Phase::Damp:
|
||||
update_decay(oscillator, 12 << 2);
|
||||
if(attenuation_ == 511) {
|
||||
(*will_attack_)();
|
||||
phase_ = Phase::Attack;
|
||||
}
|
||||
break;
|
||||
|
||||
(*will_attack_)();
|
||||
}
|
||||
phase_ = Phase::Attack;
|
||||
case Phase::Attack:
|
||||
update_attack(oscillator, attack_rate_ + key_scaling_rate);
|
||||
|
||||
// Two possible terminating conditions: (i) the attack rate is 15; (ii) full volume has been reached.
|
||||
if(attenuation_ <= 0) {
|
||||
attenuation_ = 0;
|
||||
phase_ = Phase::Decay;
|
||||
}
|
||||
break;
|
||||
|
||||
case Phase::Decay:
|
||||
update_decay(oscillator, decay_rate_ + key_scaling_rate);
|
||||
if(attenuation_ >= sustain_level_) {
|
||||
attenuation_ = sustain_level_;
|
||||
phase_ = use_sustain_level_ ? Phase::Sustain : Phase::Release;
|
||||
}
|
||||
break;
|
||||
|
||||
case Phase::Sustain:
|
||||
// Nothing to do.
|
||||
break;
|
||||
|
||||
case Phase::Release:
|
||||
update_decay(oscillator, release_rate_ + key_scaling_rate);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
@returns The current attenuation from this envelope generator. This is independent of the envelope precision.
|
||||
*/
|
||||
int attenuation() const {
|
||||
// TODO: if this envelope is fully released, should tremolo still be able to vocalise it?
|
||||
return (attenuation_ << 3) + tremolo_;
|
||||
}
|
||||
|
||||
/*!
|
||||
Enables or disables damping on this envelope generator. If damping is enabled then this envelope generator will
|
||||
use the damping phase when necessary (i.e. when transitioning to key on if attenuation is not already at maximum)
|
||||
and in any case will call @c will_attack before transitioning from any other state to attack.
|
||||
|
||||
@param will_attack Supply a will_attack callback to enable damping mode; supply nullopt to disable damping mode.
|
||||
*/
|
||||
void set_should_damp(const std::optional<std::function<void(void)>> &will_attack) {
|
||||
will_attack_ = will_attack;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the current state of the key-on input.
|
||||
*/
|
||||
void set_key_on(const bool key_on) {
|
||||
// Do nothing if this is not a leading or trailing edge.
|
||||
if(key_on == key_on_) return;
|
||||
key_on_ = key_on;
|
||||
|
||||
// Always transition to release upon a key off.
|
||||
if(!key_on_) {
|
||||
phase_ = Phase::Release;
|
||||
return;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the attack rate, which should be in the range 0–15.
|
||||
*/
|
||||
void set_attack_rate(int rate) {
|
||||
attack_rate_ = rate << 2;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the decay rate, which should be in the range 0–15.
|
||||
*/
|
||||
void set_decay_rate(int rate) {
|
||||
decay_rate_ = rate << 2;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the release rate, which should be in the range 0–15.
|
||||
*/
|
||||
void set_release_rate(int rate) {
|
||||
release_rate_ = rate << 2;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the sustain level, which should be in the range 0–15.
|
||||
*/
|
||||
void set_sustain_level(int level) {
|
||||
sustain_level_ = level << 3;
|
||||
// TODO: verify the shift level here. Especially re: precision.
|
||||
}
|
||||
|
||||
/*!
|
||||
Enables or disables use of the sustain level. If this is disabled, the envelope proceeds
|
||||
directly from decay to release.
|
||||
*/
|
||||
void set_use_sustain_level(bool use) {
|
||||
use_sustain_level_ = use;
|
||||
}
|
||||
|
||||
/*!
|
||||
Enables or disables key-rate scaling.
|
||||
*/
|
||||
void set_key_scaling_rate_enabled(bool enabled) {
|
||||
key_scale_rate_shift_ = int(enabled) * 2;
|
||||
}
|
||||
|
||||
/*!
|
||||
Enables or disables application of the low-frequency oscillator's tremolo.
|
||||
*/
|
||||
void set_tremolo_enabled(bool enabled) {
|
||||
tremolo_enable_ = int(enabled);
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the current period associated with the channel that owns this envelope generator;
|
||||
this is used to select a key scaling rate if key-rate scaling is enabled.
|
||||
*/
|
||||
void set_period(int period, int octave) {
|
||||
key_scale_rate_ = (octave << 1) | (period >> (period_precision - 1));
|
||||
}
|
||||
|
||||
private:
|
||||
enum class Phase {
|
||||
Attack, Decay, Sustain, Release, Damp
|
||||
} phase_ = Phase::Release;
|
||||
int attenuation_ = 511, tremolo_ = 0;
|
||||
|
||||
bool key_on_ = false;
|
||||
std::optional<std::function<void(void)>> will_attack_;
|
||||
|
||||
int key_scale_rate_ = 0;
|
||||
int key_scale_rate_shift_ = 0;
|
||||
|
||||
int tremolo_enable_ = 0;
|
||||
|
||||
int attack_rate_ = 0;
|
||||
int decay_rate_ = 0;
|
||||
int release_rate_ = 0;
|
||||
int sustain_level_ = 0;
|
||||
bool use_sustain_level_ = false;
|
||||
|
||||
static constexpr int dithering_patterns[4][8] = {
|
||||
{0, 1, 0, 1, 0, 1, 0, 1},
|
||||
{0, 1, 0, 1, 1, 1, 0, 1},
|
||||
{0, 1, 1, 1, 0, 1, 1, 1},
|
||||
{0, 1, 1, 1, 1, 1, 1, 1},
|
||||
};
|
||||
|
||||
void update_attack(const LowFrequencyOscillator &oscillator, int rate) {
|
||||
// Special case: no attack.
|
||||
if(rate < 4) {
|
||||
// On key on: if this is an envelope generator with damping, and damping is required,
|
||||
// schedule that. If damping is not required, announce a pending attack now and
|
||||
// transition to attack.
|
||||
if(will_attack_) {
|
||||
if(attenuation_ != 511) {
|
||||
phase_ = Phase::Damp;
|
||||
return;
|
||||
}
|
||||
|
||||
// Special case: instant attack.
|
||||
if(rate >= 60) {
|
||||
attenuation_ = 0;
|
||||
return;
|
||||
}
|
||||
(*will_attack_)();
|
||||
}
|
||||
phase_ = Phase::Attack;
|
||||
}
|
||||
|
||||
// Work out the number of cycles between each adjustment tick, and stop now
|
||||
// if not at the next adjustment boundary.
|
||||
const int shift_size = 13 - (std::min(rate, 52) >> 2);
|
||||
if(oscillator.counter & ((1 << shift_size) - 1)) {
|
||||
return;
|
||||
}
|
||||
/*!
|
||||
Sets the attack rate, which should be in the range 0–15.
|
||||
*/
|
||||
void set_attack_rate(const int rate) {
|
||||
attack_rate_ = rate << 2;
|
||||
}
|
||||
|
||||
// Apply dithered adjustment.
|
||||
const int rate_shift = (rate > 55);
|
||||
const int step = dithering_patterns[rate & 3][(oscillator.counter >> shift_size) & 7];
|
||||
attenuation_ -= ((attenuation_ >> (3 - rate_shift)) + 1) * step;
|
||||
/*!
|
||||
Sets the decay rate, which should be in the range 0–15.
|
||||
*/
|
||||
void set_decay_rate(const int rate) {
|
||||
decay_rate_ = rate << 2;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the release rate, which should be in the range 0–15.
|
||||
*/
|
||||
void set_release_rate(const int rate) {
|
||||
release_rate_ = rate << 2;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the sustain level, which should be in the range 0–15.
|
||||
*/
|
||||
void set_sustain_level(const int level) {
|
||||
sustain_level_ = level << 3;
|
||||
// TODO: verify the shift level here. Especially re: precision.
|
||||
}
|
||||
|
||||
/*!
|
||||
Enables or disables use of the sustain level. If this is disabled, the envelope proceeds
|
||||
directly from decay to release.
|
||||
*/
|
||||
void set_use_sustain_level(const bool use) {
|
||||
use_sustain_level_ = use;
|
||||
}
|
||||
|
||||
/*!
|
||||
Enables or disables key-rate scaling.
|
||||
*/
|
||||
void set_key_scaling_rate_enabled(const bool enabled) {
|
||||
key_scale_rate_shift_ = int(enabled) * 2;
|
||||
}
|
||||
|
||||
/*!
|
||||
Enables or disables application of the low-frequency oscillator's tremolo.
|
||||
*/
|
||||
void set_tremolo_enabled(const bool enabled) {
|
||||
tremolo_enable_ = int(enabled);
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the current period associated with the channel that owns this envelope generator;
|
||||
this is used to select a key scaling rate if key-rate scaling is enabled.
|
||||
*/
|
||||
void set_period(const int period, const int octave) {
|
||||
key_scale_rate_ = (octave << 1) | (period >> (period_precision - 1));
|
||||
}
|
||||
|
||||
private:
|
||||
enum class Phase {
|
||||
Attack, Decay, Sustain, Release, Damp
|
||||
} phase_ = Phase::Release;
|
||||
int attenuation_ = 511, tremolo_ = 0;
|
||||
|
||||
bool key_on_ = false;
|
||||
std::optional<std::function<void(void)>> will_attack_;
|
||||
|
||||
int key_scale_rate_ = 0;
|
||||
int key_scale_rate_shift_ = 0;
|
||||
|
||||
int tremolo_enable_ = 0;
|
||||
|
||||
int attack_rate_ = 0;
|
||||
int decay_rate_ = 0;
|
||||
int release_rate_ = 0;
|
||||
int sustain_level_ = 0;
|
||||
bool use_sustain_level_ = false;
|
||||
|
||||
static constexpr int dithering_patterns[4][8] = {
|
||||
{0, 1, 0, 1, 0, 1, 0, 1},
|
||||
{0, 1, 0, 1, 1, 1, 0, 1},
|
||||
{0, 1, 1, 1, 0, 1, 1, 1},
|
||||
{0, 1, 1, 1, 1, 1, 1, 1},
|
||||
};
|
||||
|
||||
void update_attack(const LowFrequencyOscillator &oscillator, const int rate) {
|
||||
// Special case: no attack.
|
||||
if(rate < 4) {
|
||||
return;
|
||||
}
|
||||
|
||||
void update_decay(const LowFrequencyOscillator &oscillator, int rate) {
|
||||
// Special case: no decay.
|
||||
if(rate < 4) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Work out the number of cycles between each adjustment tick, and stop now
|
||||
// if not at the next adjustment boundary.
|
||||
const int shift_size = 13 - (std::min(rate, 52) >> 2);
|
||||
if(oscillator.counter & ((1 << shift_size) - 1)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Apply dithered adjustment and clamp.
|
||||
const int rate_shift = 1 + (rate > 59) + (rate > 55);
|
||||
attenuation_ += dithering_patterns[rate & 3][(oscillator.counter >> shift_size) & 7] * (4 << rate_shift);
|
||||
attenuation_ = std::min(attenuation_, 511);
|
||||
// Special case: instant attack.
|
||||
if(rate >= 60) {
|
||||
attenuation_ = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
// Work out the number of cycles between each adjustment tick, and stop now
|
||||
// if not at the next adjustment boundary.
|
||||
const int shift_size = 13 - (std::min(rate, 52) >> 2);
|
||||
if(oscillator.counter & ((1 << shift_size) - 1)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Apply dithered adjustment.
|
||||
const int rate_shift = (rate > 55);
|
||||
const int step = dithering_patterns[rate & 3][(oscillator.counter >> shift_size) & 7];
|
||||
attenuation_ -= ((attenuation_ >> (3 - rate_shift)) + 1) * step;
|
||||
}
|
||||
|
||||
void update_decay(const LowFrequencyOscillator &oscillator, const int rate) {
|
||||
// Special case: no decay.
|
||||
if(rate < 4) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Work out the number of cycles between each adjustment tick, and stop now
|
||||
// if not at the next adjustment boundary.
|
||||
const int shift_size = 13 - (std::min(rate, 52) >> 2);
|
||||
if(oscillator.counter & ((1 << shift_size) - 1)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Apply dithered adjustment and clamp.
|
||||
const int rate_shift = 1 + (rate > 59) + (rate > 55);
|
||||
attenuation_ += dithering_patterns[rate & 3][(oscillator.counter >> shift_size) & 7] * (4 << rate_shift);
|
||||
attenuation_ = std::min(attenuation_, 511);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -11,42 +11,42 @@
|
||||
namespace Yamaha::OPL {
|
||||
|
||||
template <int frequency_precision> class KeyLevelScaler {
|
||||
public:
|
||||
public:
|
||||
|
||||
/*!
|
||||
Sets the current period associated with the channel that owns this envelope generator;
|
||||
this is used to select a key scaling rate if key-rate scaling is enabled.
|
||||
*/
|
||||
void set_period(int period, int octave) {
|
||||
constexpr int key_level_scales[16] = {0, 48, 64, 74, 80, 86, 90, 94, 96, 100, 102, 104, 106, 108, 110, 112};
|
||||
constexpr int masks[2] = {~0, 0};
|
||||
/*!
|
||||
Sets the current period associated with the channel that owns this envelope generator;
|
||||
this is used to select a key scaling rate if key-rate scaling is enabled.
|
||||
*/
|
||||
void set_period(const int period, const int octave) {
|
||||
constexpr int key_level_scales[16] = {0, 48, 64, 74, 80, 86, 90, 94, 96, 100, 102, 104, 106, 108, 110, 112};
|
||||
constexpr int masks[2] = {~0, 0};
|
||||
|
||||
// A two's complement assumption is embedded below; the use of masks relies
|
||||
// on the sign bit to clamp to zero.
|
||||
level_ = key_level_scales[period >> (frequency_precision - 4)];
|
||||
level_ -= 16 * (octave ^ 7);
|
||||
level_ &= masks[(level_ >> ((sizeof(int) * 8) - 1)) & 1];
|
||||
}
|
||||
// A two's complement assumption is embedded below; the use of masks relies
|
||||
// on the sign bit to clamp to zero.
|
||||
level_ = key_level_scales[period >> (frequency_precision - 4)];
|
||||
level_ -= 16 * (octave ^ 7);
|
||||
level_ &= masks[(level_ >> ((sizeof(int) * 8) - 1)) & 1];
|
||||
}
|
||||
|
||||
/*!
|
||||
Enables or disables key-rate scaling.
|
||||
*/
|
||||
void set_key_scaling_level(int level) {
|
||||
// '7' is just a number large enough to render all possible scaling coefficients as 0.
|
||||
constexpr int key_level_scale_shifts[4] = {7, 1, 2, 0};
|
||||
shift_ = key_level_scale_shifts[level];
|
||||
}
|
||||
/*!
|
||||
Enables or disables key-rate scaling.
|
||||
*/
|
||||
void set_key_scaling_level(const int level) {
|
||||
// '7' is just a number large enough to render all possible scaling coefficients as 0.
|
||||
constexpr int key_level_scale_shifts[4] = {7, 1, 2, 0};
|
||||
shift_ = key_level_scale_shifts[level];
|
||||
}
|
||||
|
||||
/*!
|
||||
@returns The current attenuation level due to key-level scaling.
|
||||
*/
|
||||
int attenuation() const {
|
||||
return level_ >> shift_;
|
||||
}
|
||||
/*!
|
||||
@returns The current attenuation level due to key-level scaling.
|
||||
*/
|
||||
int attenuation() const {
|
||||
return level_ >> shift_;
|
||||
}
|
||||
|
||||
private:
|
||||
int level_ = 0;
|
||||
int shift_ = 0;
|
||||
private:
|
||||
int level_ = 0;
|
||||
int shift_ = 0;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -18,46 +18,46 @@ namespace Yamaha::OPL {
|
||||
as part of their ADSR envelope.
|
||||
*/
|
||||
class LowFrequencyOscillator {
|
||||
public:
|
||||
/// Current attenuation due to tremolo / amplitude modulation, between 0 and 26.
|
||||
int tremolo = 0;
|
||||
public:
|
||||
/// Current attenuation due to tremolo / amplitude modulation, between 0 and 26.
|
||||
int tremolo = 0;
|
||||
|
||||
/// A number between 0 and 7 indicating the current vibrato offset; this should be combined by operators
|
||||
/// with their frequency number to get the actual vibrato.
|
||||
int vibrato = 0;
|
||||
/// A number between 0 and 7 indicating the current vibrato offset; this should be combined by operators
|
||||
/// with their frequency number to get the actual vibrato.
|
||||
int vibrato = 0;
|
||||
|
||||
/// A counter of the number of operator update cycles (i.e. input clock / 72) since an arbitrary time.
|
||||
int counter = 0;
|
||||
/// A counter of the number of operator update cycles (i.e. input clock / 72) since an arbitrary time.
|
||||
int counter = 0;
|
||||
|
||||
/// Describes the current output of the LFSR; will be either 0 or 1.
|
||||
int lfsr = 0;
|
||||
/// Describes the current output of the LFSR; will be either 0 or 1.
|
||||
int lfsr = 0;
|
||||
|
||||
/// Updates the oscillator outputs. Should be called at the (input clock/72) rate.
|
||||
void update() {
|
||||
++counter;
|
||||
/// Updates the oscillator outputs. Should be called at the (input clock/72) rate.
|
||||
void update() {
|
||||
++counter;
|
||||
|
||||
// This produces output of:
|
||||
//
|
||||
// four instances of 0, four instances of 1... _three_ instances of 26,
|
||||
// four instances of 25, four instances of 24... _three_ instances of 0.
|
||||
//
|
||||
// ... advancing once every 64th update.
|
||||
const int tremolo_index = (counter >> 6) % 210;
|
||||
const int tremolo_levels[2] = {tremolo_index >> 2, 52 - ((tremolo_index+1) >> 2)};
|
||||
tremolo = tremolo_levels[tremolo_index / 107];
|
||||
// This produces output of:
|
||||
//
|
||||
// four instances of 0, four instances of 1... _three_ instances of 26,
|
||||
// four instances of 25, four instances of 24... _three_ instances of 0.
|
||||
//
|
||||
// ... advancing once every 64th update.
|
||||
const int tremolo_index = (counter >> 6) % 210;
|
||||
const int tremolo_levels[2] = {tremolo_index >> 2, 52 - ((tremolo_index+1) >> 2)};
|
||||
tremolo = tremolo_levels[tremolo_index / 107];
|
||||
|
||||
// Vibrato is relatively simple: it's just three bits from the counter.
|
||||
vibrato = (counter >> 10) & 7;
|
||||
}
|
||||
// Vibrato is relatively simple: it's just three bits from the counter.
|
||||
vibrato = (counter >> 10) & 7;
|
||||
}
|
||||
|
||||
/// Updartes the LFSR output. Should be called at the input clock rate.
|
||||
void update_lfsr() {
|
||||
lfsr = noise_source_.next();
|
||||
}
|
||||
/// Updartes the LFSR output. Should be called at the input clock rate.
|
||||
void update_lfsr() {
|
||||
lfsr = noise_source_.next();
|
||||
}
|
||||
|
||||
private:
|
||||
// This is the correct LSFR per forums.submarine.org.uk.
|
||||
Numeric::LFSR<int, 0x800302> noise_source_;
|
||||
private:
|
||||
// This is the correct LSFR per forums.submarine.org.uk.
|
||||
Numeric::LFSR<int, 0x800302> noise_source_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -14,22 +14,22 @@
|
||||
namespace Yamaha::OPL {
|
||||
|
||||
template <typename Child, bool stereo> class OPLBase: public ::Outputs::Speaker::BufferSource<Child, stereo> {
|
||||
public:
|
||||
void write(uint16_t address, uint8_t value) {
|
||||
if(address & 1) {
|
||||
static_cast<Child *>(this)->write_register(selected_register_, value);
|
||||
} else {
|
||||
selected_register_ = value;
|
||||
}
|
||||
public:
|
||||
void write(const uint16_t address, const uint8_t value) {
|
||||
if(address & 1) {
|
||||
static_cast<Child *>(this)->write_register(selected_register_, value);
|
||||
} else {
|
||||
selected_register_ = value;
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
OPLBase(Concurrency::AsyncTaskQueue<false> &task_queue) : task_queue_(task_queue) {}
|
||||
protected:
|
||||
OPLBase(Concurrency::AsyncTaskQueue<false> &task_queue) : task_queue_(task_queue) {}
|
||||
|
||||
Concurrency::AsyncTaskQueue<false> &task_queue_;
|
||||
Concurrency::AsyncTaskQueue<false> &task_queue_;
|
||||
|
||||
private:
|
||||
uint8_t selected_register_ = 0;
|
||||
private:
|
||||
uint8_t selected_register_ = 0;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -19,102 +19,102 @@ namespace Yamaha::OPL {
|
||||
multiple, and whether to apply vibrato, this will then appropriately update and return phase.
|
||||
*/
|
||||
template <int precision> class PhaseGenerator {
|
||||
public:
|
||||
/*!
|
||||
Advances the phase generator a single step, given the current state of the low-frequency oscillator, @c oscillator.
|
||||
*/
|
||||
void update(const LowFrequencyOscillator &oscillator) {
|
||||
constexpr int vibrato_shifts[4] = {3, 1, 0, 1};
|
||||
constexpr int vibrato_signs[2] = {1, -1};
|
||||
public:
|
||||
/*!
|
||||
Advances the phase generator a single step, given the current state of the low-frequency oscillator, @c oscillator.
|
||||
*/
|
||||
void update(const LowFrequencyOscillator &oscillator) {
|
||||
constexpr int vibrato_shifts[4] = {3, 1, 0, 1};
|
||||
constexpr int vibrato_signs[2] = {1, -1};
|
||||
|
||||
// Get just the top three bits of the period_.
|
||||
const int top_freq = period_ >> (precision - 3);
|
||||
// Get just the top three bits of the period_.
|
||||
const int top_freq = period_ >> (precision - 3);
|
||||
|
||||
// Cacluaute applicable vibrato as a function of (i) the top three bits of the
|
||||
// oscillator period; (ii) the current low-frequency oscillator vibrato output; and
|
||||
// (iii) whether vibrato is enabled.
|
||||
const int vibrato = (top_freq >> vibrato_shifts[oscillator.vibrato & 3]) * vibrato_signs[oscillator.vibrato >> 2] * enable_vibrato_;
|
||||
// Cacluaute applicable vibrato as a function of (i) the top three bits of the
|
||||
// oscillator period; (ii) the current low-frequency oscillator vibrato output; and
|
||||
// (iii) whether vibrato is enabled.
|
||||
const int vibrato = (top_freq >> vibrato_shifts[oscillator.vibrato & 3]) * vibrato_signs[oscillator.vibrato >> 2] * enable_vibrato_;
|
||||
|
||||
// Apply phase update with vibrato from the low-frequency oscillator.
|
||||
phase_ += (multiple_ * ((period_ << 1) + vibrato) << octave_) >> 1;
|
||||
}
|
||||
// Apply phase update with vibrato from the low-frequency oscillator.
|
||||
phase_ += (multiple_ * ((period_ << 1) + vibrato) << octave_) >> 1;
|
||||
}
|
||||
|
||||
|
||||
/*!
|
||||
@returns Current phase; real hardware provides only the low ten bits of this result.
|
||||
*/
|
||||
int phase() const {
|
||||
// My table if multipliers is multiplied by two, so shift by one more
|
||||
// than the stated precision.
|
||||
return phase_ >> precision_shift;
|
||||
}
|
||||
/*!
|
||||
@returns Current phase; real hardware provides only the low ten bits of this result.
|
||||
*/
|
||||
int phase() const {
|
||||
// My table if multipliers is multiplied by two, so shift by one more
|
||||
// than the stated precision.
|
||||
return phase_ >> precision_shift;
|
||||
}
|
||||
|
||||
/*!
|
||||
@returns Current phase, scaled up by (1 << precision).
|
||||
*/
|
||||
int scaled_phase() const {
|
||||
return phase_ >> 1;
|
||||
}
|
||||
/*!
|
||||
@returns Current phase, scaled up by (1 << precision).
|
||||
*/
|
||||
int scaled_phase() const {
|
||||
return phase_ >> 1;
|
||||
}
|
||||
|
||||
/*!
|
||||
Applies feedback based on two historic samples of a total output level,
|
||||
plus the degree of feedback to apply
|
||||
*/
|
||||
void apply_feedback(LogSign first, LogSign second, int level) {
|
||||
constexpr int masks[] = {0, ~0, ~0, ~0, ~0, ~0, ~0, ~0};
|
||||
phase_ += ((second.level(precision) + first.level(precision)) >> (8 - level)) & masks[level];
|
||||
}
|
||||
/*!
|
||||
Applies feedback based on two historic samples of a total output level,
|
||||
plus the degree of feedback to apply
|
||||
*/
|
||||
void apply_feedback(const LogSign first, const LogSign second, const int level) {
|
||||
constexpr int masks[] = {0, ~0, ~0, ~0, ~0, ~0, ~0, ~0};
|
||||
phase_ += ((second.level(precision) + first.level(precision)) >> (8 - level)) & masks[level];
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the multiple for this phase generator, in the same terms as an OPL programmer,
|
||||
i.e. a 4-bit number that is used as a lookup into the internal multiples table.
|
||||
*/
|
||||
void set_multiple(int multiple) {
|
||||
// This encodes the MUL -> multiple table given on page 12,
|
||||
// multiplied by two.
|
||||
constexpr int multipliers[] = {
|
||||
1, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 20, 24, 24, 30, 30
|
||||
};
|
||||
assert(multiple < 16);
|
||||
multiple_ = multipliers[multiple];
|
||||
}
|
||||
/*!
|
||||
Sets the multiple for this phase generator, in the same terms as an OPL programmer,
|
||||
i.e. a 4-bit number that is used as a lookup into the internal multiples table.
|
||||
*/
|
||||
void set_multiple(const int multiple) {
|
||||
// This encodes the MUL -> multiple table given on page 12,
|
||||
// multiplied by two.
|
||||
constexpr int multipliers[] = {
|
||||
1, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 20, 24, 24, 30, 30
|
||||
};
|
||||
assert(multiple < 16);
|
||||
multiple_ = multipliers[multiple];
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the period of this generator, along with its current octave.
|
||||
/*!
|
||||
Sets the period of this generator, along with its current octave.
|
||||
|
||||
Yamaha tends to refer to the period as the 'f-number', and used both 'octave' and 'block' for octave.
|
||||
*/
|
||||
void set_period(int period, int octave) {
|
||||
period_ = period;
|
||||
octave_ = octave;
|
||||
Yamaha tends to refer to the period as the 'f-number', and used both 'octave' and 'block' for octave.
|
||||
*/
|
||||
void set_period(const int period, const int octave) {
|
||||
period_ = period;
|
||||
octave_ = octave;
|
||||
|
||||
assert(octave_ < 8);
|
||||
assert(period_ < (1 << precision));
|
||||
}
|
||||
assert(octave_ < 8);
|
||||
assert(period_ < (1 << precision));
|
||||
}
|
||||
|
||||
/*!
|
||||
Enables or disables vibrato.
|
||||
*/
|
||||
void set_vibrato_enabled(bool enabled) {
|
||||
enable_vibrato_ = int(enabled);
|
||||
}
|
||||
/*!
|
||||
Enables or disables vibrato.
|
||||
*/
|
||||
void set_vibrato_enabled(const bool enabled) {
|
||||
enable_vibrato_ = int(enabled);
|
||||
}
|
||||
|
||||
/*!
|
||||
Resets the current phase.
|
||||
*/
|
||||
void reset() {
|
||||
phase_ = 0;
|
||||
}
|
||||
/*!
|
||||
Resets the current phase.
|
||||
*/
|
||||
void reset() {
|
||||
phase_ = 0;
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr int precision_shift = 1 + precision;
|
||||
private:
|
||||
static constexpr int precision_shift = 1 + precision;
|
||||
|
||||
int phase_ = 0;
|
||||
int phase_ = 0;
|
||||
|
||||
int multiple_ = 0;
|
||||
int period_ = 0;
|
||||
int octave_ = 0;
|
||||
int enable_vibrato_ = 0;
|
||||
int multiple_ = 0;
|
||||
int period_ = 0;
|
||||
int octave_ = 0;
|
||||
int enable_vibrato_ = 0;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -32,12 +32,12 @@ struct LogSign {
|
||||
sign = 1;
|
||||
}
|
||||
|
||||
LogSign &operator +=(int attenuation) {
|
||||
LogSign &operator +=(const int attenuation) {
|
||||
log += attenuation;
|
||||
return *this;
|
||||
}
|
||||
|
||||
LogSign &operator +=(LogSign log_sign) {
|
||||
LogSign &operator +=(const LogSign log_sign) {
|
||||
log += log_sign.log;
|
||||
sign *= log_sign.sign;
|
||||
return *this;
|
||||
@@ -49,7 +49,7 @@ struct LogSign {
|
||||
/*!
|
||||
@returns Negative log sin of x, assuming a 1024-unit circle.
|
||||
*/
|
||||
constexpr LogSign negative_log_sin(int x) {
|
||||
constexpr LogSign negative_log_sin(const int x) {
|
||||
/// Defines the first quadrant of 1024-unit negative log to the base two of sine (that conveniently misses sin(0)).
|
||||
///
|
||||
/// Expected branchless usage for a full 1024 unit output:
|
||||
@@ -107,7 +107,7 @@ constexpr LogSign negative_log_sin(int x) {
|
||||
Computes the linear value represented by the log-sign @c ls, shifted left by @c fractional prior
|
||||
to loss of precision.
|
||||
*/
|
||||
constexpr int power_two(LogSign ls, int fractional = 0) {
|
||||
constexpr int power_two(const LogSign ls, const int fractional = 0) {
|
||||
/// A derivative of the exponent table in a real OPL2; mapped_exp[x] = (source[c ^ 0xff] << 1) | 0x800.
|
||||
///
|
||||
/// The ahead-of-time transformation represents fixed work the OPL2 does when reading its table
|
||||
@@ -215,7 +215,7 @@ constexpr uint8_t percussion_patch_set[] = {
|
||||
};
|
||||
|
||||
|
||||
inline int LogSign::level(int fractional) const {
|
||||
inline int LogSign::level(const int fractional) const {
|
||||
return power_two(*this, fractional);
|
||||
}
|
||||
|
||||
|
||||
@@ -18,70 +18,74 @@ enum class Waveform {
|
||||
};
|
||||
|
||||
template <int phase_precision> class WaveformGenerator {
|
||||
public:
|
||||
/*!
|
||||
@returns The output of waveform @c form at [integral] phase @c phase.
|
||||
*/
|
||||
static constexpr LogSign wave(Waveform form, int phase) {
|
||||
constexpr int waveforms[4][4] = {
|
||||
{1023, 1023, 1023, 1023}, // Sine: don't mask in any quadrant.
|
||||
{511, 511, 0, 0}, // Half sine: keep the first half intact, lock to 0 in the second half.
|
||||
{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.
|
||||
};
|
||||
return negative_log_sin(phase & waveforms[int(form)][(phase >> 8) & 3]);
|
||||
}
|
||||
public:
|
||||
/*!
|
||||
@returns The output of waveform @c form at [integral] phase @c phase.
|
||||
*/
|
||||
static constexpr LogSign wave(Waveform form, int phase) {
|
||||
constexpr int waveforms[4][4] = {
|
||||
{1023, 1023, 1023, 1023}, // Sine: don't mask in any quadrant.
|
||||
{511, 511, 0, 0}, // Half sine: keep the first half intact, lock to 0 in the second half.
|
||||
{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.
|
||||
};
|
||||
return negative_log_sin(phase & waveforms[int(form)][(phase >> 8) & 3]);
|
||||
}
|
||||
|
||||
/*!
|
||||
@returns The output of waveform @c form at [scaled] phase @c scaled_phase given the modulation input @c modulation.
|
||||
*/
|
||||
static constexpr LogSign wave(Waveform form, int scaled_phase, LogSign modulation) {
|
||||
const int scaled_phase_offset = modulation.level(phase_precision);
|
||||
const int phase = (scaled_phase + scaled_phase_offset) >> phase_precision;
|
||||
return wave(form, phase);
|
||||
}
|
||||
/*!
|
||||
@returns The output of waveform @c form at [scaled] phase @c scaled_phase given the modulation input @c modulation.
|
||||
*/
|
||||
static constexpr LogSign wave(const Waveform form, const int scaled_phase, const LogSign modulation) {
|
||||
const int scaled_phase_offset = modulation.level(phase_precision);
|
||||
const int phase = (scaled_phase + scaled_phase_offset) >> phase_precision;
|
||||
return wave(form, phase);
|
||||
}
|
||||
|
||||
/*!
|
||||
@returns Snare output, calculated from the current LFSR state as captured in @c oscillator and an operator's phase.
|
||||
*/
|
||||
static constexpr LogSign snare(const LowFrequencyOscillator &oscillator, int phase) {
|
||||
// If noise is 0, output is positive.
|
||||
// If noise is 1, output is negative.
|
||||
// If (noise ^ sign) is 0, output is 0. Otherwise it is max.
|
||||
const int sign = phase & 0x200;
|
||||
const int level = ((phase >> 9) & 1) ^ oscillator.lfsr;
|
||||
return negative_log_sin(sign + (level << 8));
|
||||
}
|
||||
/*!
|
||||
@returns Snare output, calculated from the current LFSR state as captured in @c oscillator and an operator's phase.
|
||||
*/
|
||||
static constexpr LogSign snare(const LowFrequencyOscillator &oscillator, const int phase) {
|
||||
// If noise is 0, output is positive.
|
||||
// If noise is 1, output is negative.
|
||||
// If (noise ^ sign) is 0, output is 0. Otherwise it is max.
|
||||
const int sign = phase & 0x200;
|
||||
const int level = ((phase >> 9) & 1) ^ oscillator.lfsr;
|
||||
return negative_log_sin(sign + (level << 8));
|
||||
}
|
||||
|
||||
/*!
|
||||
@returns Cymbal output, calculated from an operator's phase and a modulator's phase.
|
||||
*/
|
||||
static constexpr LogSign cymbal(int carrier_phase, int modulator_phase) {
|
||||
return negative_log_sin(256 + (phase_combination(carrier_phase, modulator_phase) << 9));
|
||||
}
|
||||
/*!
|
||||
@returns Cymbal output, calculated from an operator's phase and a modulator's phase.
|
||||
*/
|
||||
static constexpr LogSign cymbal(const int carrier_phase, const int modulator_phase) {
|
||||
return negative_log_sin(256 + (phase_combination(carrier_phase, modulator_phase) << 9));
|
||||
}
|
||||
|
||||
/*!
|
||||
@returns High-hat output, calculated from the current LFSR state as captured in @c oscillator, an operator's phase and a modulator's phase.
|
||||
*/
|
||||
static constexpr LogSign high_hat(const LowFrequencyOscillator &oscillator, int carrier_phase, int modulator_phase) {
|
||||
constexpr int angles[] = {0x234, 0xd0, 0x2d0, 0x34};
|
||||
return negative_log_sin(angles[
|
||||
phase_combination(carrier_phase, modulator_phase) |
|
||||
(oscillator.lfsr << 1)
|
||||
]);
|
||||
}
|
||||
/*!
|
||||
@returns High-hat output, calculated from the current LFSR state as captured in @c oscillator, an operator's phase and a modulator's phase.
|
||||
*/
|
||||
static constexpr LogSign high_hat(
|
||||
const LowFrequencyOscillator &oscillator,
|
||||
const int carrier_phase,
|
||||
const int modulator_phase
|
||||
) {
|
||||
constexpr int angles[] = {0x234, 0xd0, 0x2d0, 0x34};
|
||||
return negative_log_sin(angles[
|
||||
phase_combination(carrier_phase, modulator_phase) |
|
||||
(oscillator.lfsr << 1)
|
||||
]);
|
||||
}
|
||||
|
||||
private:
|
||||
/*!
|
||||
@returns The phase bit used for cymbal and high-hat generation, which is a function of two operators' phases.
|
||||
*/
|
||||
static constexpr int phase_combination(int carrier_phase, int modulator_phase) {
|
||||
return (
|
||||
((carrier_phase >> 5) ^ (carrier_phase >> 3)) &
|
||||
((modulator_phase >> 7) ^ (modulator_phase >> 2)) &
|
||||
((carrier_phase >> 5) ^ (modulator_phase >> 3))
|
||||
) & 1;
|
||||
}
|
||||
private:
|
||||
/*!
|
||||
@returns The phase bit used for cymbal and high-hat generation, which is a function of two operators' phases.
|
||||
*/
|
||||
static constexpr int phase_combination(const int carrier_phase, const int modulator_phase) {
|
||||
return (
|
||||
((carrier_phase >> 5) ^ (carrier_phase >> 3)) &
|
||||
((modulator_phase >> 7) ^ (modulator_phase >> 2)) &
|
||||
((carrier_phase >> 5) ^ (modulator_phase >> 3))
|
||||
) & 1;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user