mirror of
https://github.com/TomHarte/CLK.git
synced 2026-04-25 11:17:26 +00:00
Renames folder as per intended scope.
This commit is contained in:
@@ -0,0 +1,263 @@
|
||||
//
|
||||
// EnvelopeGenerator.hpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 01/05/2020.
|
||||
// Copyright © 2020 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef EnvelopeGenerator_h
|
||||
#define EnvelopeGenerator_h
|
||||
|
||||
#include <optional>
|
||||
#include <functional>
|
||||
#include "LowFrequencyOscillator.hpp"
|
||||
|
||||
namespace Yamaha {
|
||||
namespace OPL {
|
||||
|
||||
/*!
|
||||
Models an OPL-style envelope generator.
|
||||
|
||||
Damping is optional; if damping is enabled then if there is a transition to key-on while
|
||||
attenuation is less than maximum then attenuation will be quickly transitioned to maximum
|
||||
before the attack phase can begin.
|
||||
|
||||
in real hardware damping is used by the envelope generators associated with
|
||||
carriers, with phases being reset upon the transition from damping to attack.
|
||||
|
||||
This code considers application of tremolo to be a function of the envelope generator;
|
||||
this is largely for logical conformity with the phase generator that necessarily has to
|
||||
apply vibrato.
|
||||
|
||||
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;
|
||||
|
||||
// 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 {
|
||||
return (attenuation_ + tremolo_) << 3;
|
||||
}
|
||||
|
||||
/*!
|
||||
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;
|
||||
}
|
||||
|
||||
(*will_attack_)();
|
||||
}
|
||||
phase_ = Phase::Attack;
|
||||
}
|
||||
|
||||
/*!
|
||||
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) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 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, 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);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* EnvelopeGenerator_h */
|
||||
@@ -0,0 +1,58 @@
|
||||
//
|
||||
// KeyLevelScaler.hpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 02/05/2020.
|
||||
// Copyright © 2020 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef KeyLevelScaler_h
|
||||
#define KeyLevelScaler_h
|
||||
|
||||
namespace Yamaha {
|
||||
namespace OPL {
|
||||
|
||||
template <int frequency_precision> class KeyLevelScaler {
|
||||
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};
|
||||
|
||||
// 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];
|
||||
}
|
||||
|
||||
/*!
|
||||
@returns The current attenuation level due to key-level scaling.
|
||||
*/
|
||||
int attenuation() const {
|
||||
return level_ >> shift_;
|
||||
}
|
||||
|
||||
private:
|
||||
int level_ = 0;
|
||||
int shift_ = 0;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* KeyLevelScaler_h */
|
||||
@@ -0,0 +1,68 @@
|
||||
//
|
||||
// LowFrequencyOscillator.hpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 23/04/2020.
|
||||
// Copyright © 2020 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef LowFrequencyOscillator_hpp
|
||||
#define LowFrequencyOscillator_hpp
|
||||
|
||||
#include "../../../Numeric/LFSR.hpp"
|
||||
|
||||
namespace Yamaha {
|
||||
namespace OPL {
|
||||
|
||||
/*!
|
||||
Models the output of the OPL low-frequency oscillator, which provides a couple of optional fixed-frequency
|
||||
modifications to an operator: tremolo and vibrato. Also exposes a global time counter, which oscillators use
|
||||
as part of their ADSR envelope.
|
||||
*/
|
||||
class LowFrequencyOscillator {
|
||||
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 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;
|
||||
|
||||
/// 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];
|
||||
|
||||
// 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();
|
||||
}
|
||||
|
||||
private:
|
||||
// This is the correct LSFR per forums.submarine.org.uk.
|
||||
Numeric::LFSR<int, 0x800302> noise_source_;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* LowFrequencyOscillator_hpp */
|
||||
@@ -0,0 +1,40 @@
|
||||
//
|
||||
// OPLBase.hpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 03/05/2020.
|
||||
// Copyright © 2020 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef OPLBase_h
|
||||
#define OPLBase_h
|
||||
|
||||
#include "../../../Outputs/Speaker/Implementation/SampleSource.hpp"
|
||||
#include "../../../Concurrency/AsyncTaskQueue.hpp"
|
||||
|
||||
namespace Yamaha {
|
||||
namespace OPL {
|
||||
|
||||
template <typename Child> class OPLBase: public ::Outputs::Speaker::SampleSource {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
OPLBase(Concurrency::DeferringAsyncTaskQueue &task_queue) : task_queue_(task_queue) {}
|
||||
|
||||
Concurrency::DeferringAsyncTaskQueue &task_queue_;
|
||||
|
||||
private:
|
||||
uint8_t selected_register_ = 0;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* OPLBase_h */
|
||||
@@ -0,0 +1,125 @@
|
||||
//
|
||||
// PhaseGenerator.h
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 30/04/2020.
|
||||
// Copyright © 2020 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef PhaseGenerator_h
|
||||
#define PhaseGenerator_h
|
||||
|
||||
#include <cassert>
|
||||
#include "LowFrequencyOscillator.hpp"
|
||||
#include "Tables.hpp"
|
||||
|
||||
namespace Yamaha {
|
||||
namespace OPL {
|
||||
|
||||
/*!
|
||||
Models an OPL-style phase generator of templated precision; having been told its period ('f-num'), octave ('block') and
|
||||
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[8] = {3, 1, 0, 1, 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);
|
||||
|
||||
// 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]) * vibrato_signs[oscillator.vibrato >> 2] * enable_vibrato_;
|
||||
|
||||
// Apply phase update with vibrato from the low-frequency oscillator.
|
||||
phase_ += multiple_ * (period_ + vibrato) << octave_;
|
||||
}
|
||||
|
||||
|
||||
/*!
|
||||
@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;
|
||||
}
|
||||
|
||||
/*!
|
||||
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];
|
||||
}
|
||||
|
||||
/*!
|
||||
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 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;
|
||||
|
||||
assert(octave_ < 8);
|
||||
assert(period_ < (1 << precision));
|
||||
}
|
||||
|
||||
/*!
|
||||
Enables or disables vibrato.
|
||||
*/
|
||||
void set_vibrato_enabled(bool enabled) {
|
||||
enable_vibrato_ = int(enabled);
|
||||
}
|
||||
|
||||
/*!
|
||||
Resets the current phase.
|
||||
*/
|
||||
void reset() {
|
||||
phase_ = 0;
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr int precision_shift = 1 + precision;
|
||||
|
||||
int phase_ = 0;
|
||||
|
||||
int multiple_ = 0;
|
||||
int period_ = 0;
|
||||
int octave_ = 0;
|
||||
int enable_vibrato_ = 0;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* PhaseGenerator_h */
|
||||
@@ -0,0 +1,227 @@
|
||||
//
|
||||
// Tables.hpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 15/04/2020.
|
||||
// Copyright © 2020 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef Tables_hpp
|
||||
#define Tables_hpp
|
||||
|
||||
namespace Yamaha {
|
||||
namespace OPL {
|
||||
|
||||
/*
|
||||
These are the OPL's built-in log-sin and exponentiation tables, as recovered by
|
||||
Matthew Gambrell and Olli Niemitalo in 'OPLx decapsulated'. Despite the formulas
|
||||
being well known, I've elected not to generate these at runtime because even if I
|
||||
did, I'd just end up with the proper values laid out in full in a unit test, and
|
||||
they're very compact.
|
||||
*/
|
||||
|
||||
/*!
|
||||
Represents both the logarithm of a value and its sign.
|
||||
|
||||
It's actually the negative logarithm, in base two, in fixed point.
|
||||
*/
|
||||
struct LogSign {
|
||||
int log;
|
||||
int sign;
|
||||
|
||||
void reset() {
|
||||
log = 0;
|
||||
sign = 1;
|
||||
}
|
||||
|
||||
LogSign &operator +=(int attenuation) {
|
||||
log += attenuation;
|
||||
return *this;
|
||||
}
|
||||
|
||||
LogSign &operator +=(LogSign log_sign) {
|
||||
log += log_sign.log;
|
||||
sign *= log_sign.sign;
|
||||
return *this;
|
||||
}
|
||||
|
||||
int level(int fractional = 0) const;
|
||||
};
|
||||
|
||||
/*!
|
||||
@returns Negative log sin of x, assuming a 1024-unit circle.
|
||||
*/
|
||||
constexpr LogSign negative_log_sin(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:
|
||||
///
|
||||
/// constexpr int multiplier[] = { 1, -1 };
|
||||
/// constexpr int mask[] = { 0, 255 };
|
||||
///
|
||||
/// value = exp( log_sin[angle & 255] ^ mask[(angle >> 8) & 1]) * multitplier[(angle >> 9) & 1]
|
||||
///
|
||||
/// ... where exp(x) = 2 ^ -x / 256
|
||||
constexpr int16_t log_sin[] = {
|
||||
2137, 1731, 1543, 1419, 1326, 1252, 1190, 1137,
|
||||
1091, 1050, 1013, 979, 949, 920, 894, 869,
|
||||
846, 825, 804, 785, 767, 749, 732, 717,
|
||||
701, 687, 672, 659, 646, 633, 621, 609,
|
||||
598, 587, 576, 566, 556, 546, 536, 527,
|
||||
518, 509, 501, 492, 484, 476, 468, 461,
|
||||
453, 446, 439, 432, 425, 418, 411, 405,
|
||||
399, 392, 386, 380, 375, 369, 363, 358,
|
||||
352, 347, 341, 336, 331, 326, 321, 316,
|
||||
311, 307, 302, 297, 293, 289, 284, 280,
|
||||
276, 271, 267, 263, 259, 255, 251, 248,
|
||||
244, 240, 236, 233, 229, 226, 222, 219,
|
||||
215, 212, 209, 205, 202, 199, 196, 193,
|
||||
190, 187, 184, 181, 178, 175, 172, 169,
|
||||
167, 164, 161, 159, 156, 153, 151, 148,
|
||||
146, 143, 141, 138, 136, 134, 131, 129,
|
||||
127, 125, 122, 120, 118, 116, 114, 112,
|
||||
110, 108, 106, 104, 102, 100, 98, 96,
|
||||
94, 92, 91, 89, 87, 85, 83, 82,
|
||||
80, 78, 77, 75, 74, 72, 70, 69,
|
||||
67, 66, 64, 63, 62, 60, 59, 57,
|
||||
56, 55, 53, 52, 51, 49, 48, 47,
|
||||
46, 45, 43, 42, 41, 40, 39, 38,
|
||||
37, 36, 35, 34, 33, 32, 31, 30,
|
||||
29, 28, 27, 26, 25, 24, 23, 23,
|
||||
22, 21, 20, 20, 19, 18, 17, 17,
|
||||
16, 15, 15, 14, 13, 13, 12, 12,
|
||||
11, 10, 10, 9, 9, 8, 8, 7,
|
||||
7, 7, 6, 6, 5, 5, 5, 4,
|
||||
4, 4, 3, 3, 3, 2, 2, 2,
|
||||
2, 1, 1, 1, 1, 1, 1, 1,
|
||||
0, 0, 0, 0, 0, 0, 0, 0
|
||||
};
|
||||
constexpr int16_t sign[] = { 1, -1 };
|
||||
constexpr int16_t mask[] = { 0, 255 };
|
||||
|
||||
return {
|
||||
.log = log_sin[(x & 255) ^ mask[(x >> 8) & 1]],
|
||||
.sign = sign[(x >> 9) & 1]
|
||||
};
|
||||
}
|
||||
|
||||
/*!
|
||||
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) {
|
||||
/// 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
|
||||
/// independent on the input.
|
||||
///
|
||||
/// The original table is a 0.10 fixed-point representation of 2^x - 1 with bit 10 implicitly set, where x is
|
||||
/// in 0.8 fixed point.
|
||||
///
|
||||
/// Since the log_sin table represents sine in a negative base-2 logarithm, values from it would need
|
||||
/// to be negatived before being put into the original table. That's haned with the ^ 0xff. The | 0x800 is to
|
||||
/// set the implicit bit 10 (subject to the shift).
|
||||
///
|
||||
/// The shift by 1 is to allow the chip's exploitation of the recursive symmetry of the exponential table to
|
||||
/// be achieved more easily. Specifically, to convert a logarithmic attenuation to a linear one, just perform:
|
||||
///
|
||||
/// result = mapped_exp[x & 0xff] >> (x >> 8)
|
||||
constexpr int16_t mapped_exp[] = {
|
||||
4084, 4074, 4062, 4052, 4040, 4030, 4020, 4008,
|
||||
3998, 3986, 3976, 3966, 3954, 3944, 3932, 3922,
|
||||
3912, 3902, 3890, 3880, 3870, 3860, 3848, 3838,
|
||||
3828, 3818, 3808, 3796, 3786, 3776, 3766, 3756,
|
||||
3746, 3736, 3726, 3716, 3706, 3696, 3686, 3676,
|
||||
3666, 3656, 3646, 3636, 3626, 3616, 3606, 3596,
|
||||
3588, 3578, 3568, 3558, 3548, 3538, 3530, 3520,
|
||||
3510, 3500, 3492, 3482, 3472, 3464, 3454, 3444,
|
||||
3434, 3426, 3416, 3408, 3398, 3388, 3380, 3370,
|
||||
3362, 3352, 3344, 3334, 3326, 3316, 3308, 3298,
|
||||
3290, 3280, 3272, 3262, 3254, 3246, 3236, 3228,
|
||||
3218, 3210, 3202, 3192, 3184, 3176, 3168, 3158,
|
||||
3150, 3142, 3132, 3124, 3116, 3108, 3100, 3090,
|
||||
3082, 3074, 3066, 3058, 3050, 3040, 3032, 3024,
|
||||
3016, 3008, 3000, 2992, 2984, 2976, 2968, 2960,
|
||||
2952, 2944, 2936, 2928, 2920, 2912, 2904, 2896,
|
||||
2888, 2880, 2872, 2866, 2858, 2850, 2842, 2834,
|
||||
2826, 2818, 2812, 2804, 2796, 2788, 2782, 2774,
|
||||
2766, 2758, 2752, 2744, 2736, 2728, 2722, 2714,
|
||||
2706, 2700, 2692, 2684, 2678, 2670, 2664, 2656,
|
||||
2648, 2642, 2634, 2628, 2620, 2614, 2606, 2600,
|
||||
2592, 2584, 2578, 2572, 2564, 2558, 2550, 2544,
|
||||
2536, 2530, 2522, 2516, 2510, 2502, 2496, 2488,
|
||||
2482, 2476, 2468, 2462, 2456, 2448, 2442, 2436,
|
||||
2428, 2422, 2416, 2410, 2402, 2396, 2390, 2384,
|
||||
2376, 2370, 2364, 2358, 2352, 2344, 2338, 2332,
|
||||
2326, 2320, 2314, 2308, 2300, 2294, 2288, 2282,
|
||||
2276, 2270, 2264, 2258, 2252, 2246, 2240, 2234,
|
||||
2228, 2222, 2216, 2210, 2204, 2198, 2192, 2186,
|
||||
2180, 2174, 2168, 2162, 2156, 2150, 2144, 2138,
|
||||
2132, 2128, 2122, 2116, 2110, 2104, 2098, 2092,
|
||||
2088, 2082, 2076, 2070, 2064, 2060, 2054, 2048,
|
||||
};
|
||||
|
||||
return ((mapped_exp[ls.log & 0xff] << fractional) >> (ls.log >> 8)) * ls.sign;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
Credit for the fixed register lists goes to Nuke.YKT; I found them at:
|
||||
https://siliconpr0n.org/archive/doku.php?id=vendor:yamaha:opl2#ym2413_instrument_rom
|
||||
|
||||
The arrays below begin with channel 1, then each line is a single channel defined
|
||||
in exactly the same terms as the OPL's user-defined channel.
|
||||
|
||||
*/
|
||||
|
||||
constexpr uint8_t opll_patch_set[] = {
|
||||
0x71, 0x61, 0x1e, 0x17, 0xd0, 0x78, 0x00, 0x17,
|
||||
0x13, 0x41, 0x1a, 0x0d, 0xd8, 0xf7, 0x23, 0x13,
|
||||
0x13, 0x01, 0x99, 0x00, 0xf2, 0xc4, 0x11, 0x23,
|
||||
0x31, 0x61, 0x0e, 0x07, 0xa8, 0x64, 0x70, 0x27,
|
||||
0x32, 0x21, 0x1e, 0x06, 0xe0, 0x76, 0x00, 0x28,
|
||||
0x31, 0x22, 0x16, 0x05, 0xe0, 0x71, 0x00, 0x18,
|
||||
0x21, 0x61, 0x1d, 0x07, 0x82, 0x81, 0x10, 0x07,
|
||||
0x23, 0x21, 0x2d, 0x14, 0xa2, 0x72, 0x00, 0x07,
|
||||
0x61, 0x61, 0x1b, 0x06, 0x64, 0x65, 0x10, 0x17,
|
||||
0x41, 0x61, 0x0b, 0x18, 0x85, 0xf7, 0x71, 0x07,
|
||||
0x13, 0x01, 0x83, 0x11, 0xfa, 0xe4, 0x10, 0x04,
|
||||
0x17, 0xc1, 0x24, 0x07, 0xf8, 0xf8, 0x22, 0x12,
|
||||
0x61, 0x50, 0x0c, 0x05, 0xc2, 0xf5, 0x20, 0x42,
|
||||
0x01, 0x01, 0x55, 0x03, 0xc9, 0x95, 0x03, 0x02,
|
||||
0x61, 0x41, 0x89, 0x03, 0xf1, 0xe4, 0x40, 0x13,
|
||||
};
|
||||
|
||||
constexpr uint8_t vrc7_patch_set[] = {
|
||||
0x03, 0x21, 0x05, 0x06, 0xe8, 0x81, 0x42, 0x27,
|
||||
0x13, 0x41, 0x14, 0x0d, 0xd8, 0xf6, 0x23, 0x12,
|
||||
0x11, 0x11, 0x08, 0x08, 0xfa, 0xb2, 0x20, 0x12,
|
||||
0x31, 0x61, 0x0c, 0x07, 0xa8, 0x64, 0x61, 0x27,
|
||||
0x32, 0x21, 0x1e, 0x06, 0xe1, 0x76, 0x01, 0x28,
|
||||
0x02, 0x01, 0x06, 0x00, 0xa3, 0xe2, 0xf4, 0xf4,
|
||||
0x21, 0x61, 0x1d, 0x07, 0x82, 0x81, 0x11, 0x07,
|
||||
0x23, 0x21, 0x22, 0x17, 0xa2, 0x72, 0x01, 0x17,
|
||||
0x35, 0x11, 0x25, 0x00, 0x40, 0x73, 0x72, 0x01,
|
||||
0xb5, 0x01, 0x0f, 0x0f, 0xa8, 0xa5, 0x51, 0x02,
|
||||
0x17, 0xc1, 0x24, 0x07, 0xf8, 0xf8, 0x22, 0x12,
|
||||
0x71, 0x23, 0x11, 0x06, 0x65, 0x74, 0x18, 0x16,
|
||||
0x01, 0x02, 0xd3, 0x05, 0xc9, 0x95, 0x03, 0x02,
|
||||
0x61, 0x63, 0x0c, 0x00, 0x94, 0xc0, 0x33, 0xf6,
|
||||
0x21, 0x72, 0x0d, 0x00, 0xc1, 0xd5, 0x56, 0x06,
|
||||
};
|
||||
|
||||
constexpr uint8_t percussion_patch_set[] = {
|
||||
0x01, 0x01, 0x18, 0x0f, 0xdf, 0xf8, 0x6a, 0x6d,
|
||||
0x01, 0x01, 0x00, 0x00, 0xc8, 0xd8, 0xa7, 0x48,
|
||||
0x05, 0x01, 0x00, 0x00, 0xf8, 0xaa, 0x59, 0x55,
|
||||
};
|
||||
|
||||
|
||||
inline int LogSign::level(int fractional) const {
|
||||
return power_two(*this, fractional);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* Tables_hpp */
|
||||
@@ -0,0 +1,92 @@
|
||||
//
|
||||
// WaveformGenerator.hpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 03/05/2020.
|
||||
// Copyright © 2020 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef WaveformGenerator_h
|
||||
#define WaveformGenerator_h
|
||||
|
||||
#include "Tables.hpp"
|
||||
#include "LowFrequencyOscillator.hpp"
|
||||
|
||||
namespace Yamaha {
|
||||
namespace OPL {
|
||||
|
||||
enum class Waveform {
|
||||
Sine, HalfSine, AbsSine, PulseSine
|
||||
};
|
||||
|
||||
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]);
|
||||
}
|
||||
|
||||
/*!
|
||||
@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 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 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 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)
|
||||
]);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* WaveformGenerator_h */
|
||||
@@ -0,0 +1,431 @@
|
||||
//
|
||||
// OPLL.cpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 03/05/2020.
|
||||
// Copyright © 2020 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#include "OPLL.hpp"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
using namespace Yamaha::OPL;
|
||||
|
||||
OPLL::OPLL(Concurrency::DeferringAsyncTaskQueue &task_queue, int audio_divider, bool is_vrc7):
|
||||
OPLBase(task_queue), audio_divider_(audio_divider), is_vrc7_(is_vrc7) {
|
||||
// Due to the way that sound mixing works on the OPLL, the audio divider may not
|
||||
// be larger than 4.
|
||||
assert(audio_divider <= 4);
|
||||
|
||||
// Setup the rhythm envelope generators.
|
||||
|
||||
// Treat the bass exactly as if it were a melodic channel.
|
||||
rhythm_envelope_generators_[BassCarrier].set_should_damp([this] {
|
||||
// Propagate attack mode to the modulator, and reset both phases.
|
||||
rhythm_envelope_generators_[BassModulator].set_key_on(true);
|
||||
phase_generators_[6 + 0].reset();
|
||||
phase_generators_[6 + 9].reset();
|
||||
});
|
||||
|
||||
// Crib the proper rhythm envelope generator settings by installing
|
||||
// the rhythm instruments and copying them over.
|
||||
rhythm_mode_enabled_ = true;
|
||||
install_instrument(6);
|
||||
install_instrument(7);
|
||||
install_instrument(8);
|
||||
|
||||
rhythm_envelope_generators_[BassCarrier] = envelope_generators_[6];
|
||||
rhythm_envelope_generators_[BassModulator] = envelope_generators_[6 + 9];
|
||||
rhythm_envelope_generators_[HighHat] = envelope_generators_[7 + 9];
|
||||
rhythm_envelope_generators_[Cymbal] = envelope_generators_[8];
|
||||
rhythm_envelope_generators_[TomTom] = envelope_generators_[8 + 9];
|
||||
rhythm_envelope_generators_[Snare] = envelope_generators_[7];
|
||||
|
||||
// Return to ordinary default mode.
|
||||
rhythm_mode_enabled_ = false;
|
||||
|
||||
// Set up proper damping management.
|
||||
for(int c = 0; c < 9; ++c) {
|
||||
envelope_generators_[c].set_should_damp([this, c] {
|
||||
// Propagate attack mode to the modulator, and reset both phases.
|
||||
envelope_generators_[c + 9].set_key_on(true);
|
||||
phase_generators_[c + 0].reset();
|
||||
phase_generators_[c + 9].reset();
|
||||
});
|
||||
}
|
||||
|
||||
// Set default instrument.
|
||||
for(int c = 0; c < 9; ++c) {
|
||||
install_instrument(c);
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Machine-facing programmatic input.
|
||||
|
||||
void OPLL::write_register(uint8_t address, uint8_t value) {
|
||||
// The OPLL doesn't have timers or other non-audio functions, so all writes
|
||||
// go to the audio queue.
|
||||
task_queue_.defer([this, address, value] {
|
||||
// The first 8 locations are used to define the custom instrument, and have
|
||||
// exactly the same format as the patch set arrays at the head of this file.
|
||||
if(address < 8) {
|
||||
custom_instrument_[address] = value;
|
||||
|
||||
// Update all channels that refer to instrument 0.
|
||||
for(int c = 0; c < 9; ++c) {
|
||||
if(!channels_[c].instrument) {
|
||||
install_instrument(c);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Register 0xe enables or disables rhythm mode and contains the
|
||||
// percussion key-on bits.
|
||||
if(address == 0xe) {
|
||||
const bool old_rhythm_mode = rhythm_mode_enabled_;
|
||||
rhythm_mode_enabled_ = value & 0x20;
|
||||
if(old_rhythm_mode != rhythm_mode_enabled_) {
|
||||
// Change the instlled instruments for channels 6, 7 and 8
|
||||
// if this was a transition into or out of rhythm mode.
|
||||
install_instrument(6);
|
||||
install_instrument(7);
|
||||
install_instrument(8);
|
||||
}
|
||||
rhythm_envelope_generators_[HighHat].set_key_on(value & 0x01);
|
||||
rhythm_envelope_generators_[Cymbal].set_key_on(value & 0x02);
|
||||
rhythm_envelope_generators_[TomTom].set_key_on(value & 0x04);
|
||||
rhythm_envelope_generators_[Snare].set_key_on(value & 0x08);
|
||||
if(value & 0x10) {
|
||||
rhythm_envelope_generators_[BassCarrier].set_key_on(true);
|
||||
} else {
|
||||
rhythm_envelope_generators_[BassCarrier].set_key_on(false);
|
||||
rhythm_envelope_generators_[BassModulator].set_key_on(false);
|
||||
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// That leaves only per-channel selections, for which the addressing
|
||||
// is completely orthogonal; check that a valid channel is being requested.
|
||||
const auto index = address & 0xf;
|
||||
if(index > 8) return;
|
||||
|
||||
switch(address & 0xf0) {
|
||||
default: break;
|
||||
|
||||
// Address 1x sets the low 8 bits of the period for channel x.
|
||||
case 0x10:
|
||||
channels_[index].period = (channels_[index].period & ~0xff) | value;
|
||||
set_channel_period(index);
|
||||
return;
|
||||
|
||||
// Address 2x Sets the octave and a single bit of the frequency, as well
|
||||
// as setting key on and sustain mode.
|
||||
case 0x20:
|
||||
channels_[index].period = (channels_[index].period & 0xff) | ((value & 1) << 8);
|
||||
channels_[index].octave = (value >> 1) & 7;
|
||||
set_channel_period(index);
|
||||
|
||||
// In this implementation the first 9 envelope generators are for
|
||||
// channel carriers, and their will_attack callback is used to trigger
|
||||
// key-on for modulators. But key-off needs to be set to both envelope
|
||||
// generators now.
|
||||
if(value & 0x10) {
|
||||
envelope_generators_[index].set_key_on(true);
|
||||
} else {
|
||||
envelope_generators_[index + 0].set_key_on(false);
|
||||
envelope_generators_[index + 9].set_key_on(false);
|
||||
}
|
||||
|
||||
// Set sustain bit to both the relevant operators.
|
||||
channels_[index].use_sustain = value & 0x20;
|
||||
set_use_sustain(index);
|
||||
return;
|
||||
|
||||
// Address 3x selects the instrument and attenuation for a channel;
|
||||
// in rhythm mode some of the nibbles that ordinarily identify instruments
|
||||
// instead nominate additional attenuations. This code reads those back
|
||||
// from the stored instrument values.
|
||||
case 0x30:
|
||||
channels_[index].attenuation = value & 0xf;
|
||||
|
||||
// Install an instrument only if it's new.
|
||||
if(channels_[index].instrument != value >> 4) {
|
||||
channels_[index].instrument = value >> 4;
|
||||
if(index < 6 || !rhythm_mode_enabled_) {
|
||||
install_instrument(index);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void OPLL::set_channel_period(int channel) {
|
||||
phase_generators_[channel + 0].set_period(channels_[channel].period, channels_[channel].octave);
|
||||
phase_generators_[channel + 9].set_period(channels_[channel].period, channels_[channel].octave);
|
||||
|
||||
envelope_generators_[channel + 0].set_period(channels_[channel].period, channels_[channel].octave);
|
||||
envelope_generators_[channel + 9].set_period(channels_[channel].period, channels_[channel].octave);
|
||||
|
||||
key_level_scalers_[channel + 0].set_period(channels_[channel].period, channels_[channel].octave);
|
||||
key_level_scalers_[channel + 9].set_period(channels_[channel].period, channels_[channel].octave);
|
||||
}
|
||||
|
||||
const uint8_t *OPLL::instrument_definition(int instrument, int channel) {
|
||||
// Divert to the appropriate rhythm instrument if in rhythm mode.
|
||||
if(channel >= 6 && rhythm_mode_enabled_) {
|
||||
return &percussion_patch_set[(channel - 6) * 8];
|
||||
}
|
||||
|
||||
// Instrument 0 is the custom instrument.
|
||||
if(!instrument) return custom_instrument_;
|
||||
|
||||
// Instruments other than 0 are taken from the fixed set.
|
||||
const int index = (instrument - 1) * 8;
|
||||
return is_vrc7_ ? &vrc7_patch_set[index] : &opll_patch_set[index];
|
||||
}
|
||||
|
||||
void OPLL::install_instrument(int channel) {
|
||||
auto &carrier_envelope = envelope_generators_[channel + 0];
|
||||
auto &carrier_phase = phase_generators_[channel + 0];
|
||||
auto &carrier_scaler = key_level_scalers_[channel + 0];
|
||||
|
||||
auto &modulator_envelope = envelope_generators_[channel + 9];
|
||||
auto &modulator_phase = phase_generators_[channel + 9];
|
||||
auto &modulator_scaler = key_level_scalers_[channel + 9];
|
||||
|
||||
const uint8_t *const instrument = instrument_definition(channels_[channel].instrument, channel);
|
||||
|
||||
// Bytes 0 (modulator) and 1 (carrier):
|
||||
//
|
||||
// b0-b3: multiplier;
|
||||
// b4: key-scale rate enable;
|
||||
// b5: sustain-level enable;
|
||||
// b6: vibrato enable;
|
||||
// b7: tremolo enable.
|
||||
modulator_phase.set_multiple(instrument[0] & 0xf);
|
||||
channels_[channel].modulator_key_rate_scale_multiplier = (instrument[0] >> 4) & 1;
|
||||
modulator_phase.set_vibrato_enabled(instrument[0] & 0x40);
|
||||
modulator_envelope.set_tremolo_enabled(instrument[0] & 0x80);
|
||||
|
||||
carrier_phase.set_multiple(instrument[1] & 0xf);
|
||||
channels_[channel].carrier_key_rate_scale_multiplier = (instrument[1] >> 4) & 1;
|
||||
carrier_phase.set_vibrato_enabled(instrument[1] & 0x40);
|
||||
carrier_envelope.set_tremolo_enabled(instrument[1] & 0x80);
|
||||
|
||||
// Pass off bit 5.
|
||||
set_use_sustain(channel);
|
||||
|
||||
// Byte 2:
|
||||
//
|
||||
// b0–b5: modulator attenuation;
|
||||
// b6–b7: modulator key-scale level.
|
||||
modulator_scaler.set_key_scaling_level(instrument[3] >> 6);
|
||||
channels_[channel].modulator_attenuation = instrument[2] & 0x3f;
|
||||
|
||||
// Byte 3:
|
||||
//
|
||||
// b0–b2: modulator feedback level;
|
||||
// b3: modulator waveform selection;
|
||||
// b4: carrier waveform selection;
|
||||
// b5: [unused]
|
||||
// b6–b7: carrier key-scale level.
|
||||
channels_[channel].modulator_feedback = instrument[3] & 7;
|
||||
channels_[channel].modulator_waveform = Waveform((instrument[3] >> 3) & 1);
|
||||
channels_[channel].carrier_waveform = Waveform((instrument[3] >> 4) & 1);
|
||||
carrier_scaler.set_key_scaling_level(instrument[3] >> 6);
|
||||
|
||||
// Bytes 4 (modulator) and 5 (carrier):
|
||||
//
|
||||
// b0–b3: decay rate;
|
||||
// b4–b7: attack rate.
|
||||
modulator_envelope.set_decay_rate(instrument[4] & 0xf);
|
||||
modulator_envelope.set_attack_rate(instrument[4] >> 4);
|
||||
carrier_envelope.set_decay_rate(instrument[5] & 0xf);
|
||||
carrier_envelope.set_attack_rate(instrument[5] >> 4);
|
||||
|
||||
// Bytes 6 (modulator) and 7 (carrier):
|
||||
//
|
||||
// b0–b3: release rate;
|
||||
// b4–b7: sustain level.
|
||||
modulator_envelope.set_release_rate(instrument[6] & 0xf);
|
||||
modulator_envelope.set_sustain_level(instrument[6] >> 4);
|
||||
carrier_envelope.set_release_rate(instrument[7] & 0xf);
|
||||
carrier_envelope.set_sustain_level(instrument[7] >> 4);
|
||||
}
|
||||
|
||||
void OPLL::set_use_sustain(int channel) {
|
||||
const uint8_t *const instrument = instrument_definition(channels_[channel].instrument, channel);
|
||||
envelope_generators_[channel + 0].set_use_sustain_level((instrument[1] & 0x20) || channels_[channel].use_sustain);
|
||||
envelope_generators_[channel + 9].set_use_sustain_level((instrument[0] & 0x20) || channels_[channel].use_sustain);
|
||||
}
|
||||
|
||||
// MARK: - Output generation.
|
||||
|
||||
void OPLL::set_sample_volume_range(std::int16_t range) {
|
||||
total_volume_ = range;
|
||||
}
|
||||
|
||||
void OPLL::get_samples(std::size_t number_of_samples, std::int16_t *target) {
|
||||
// Both the OPLL and the OPL2 divide the input clock by 72 to get the base tick frequency;
|
||||
// unlike the OPL2 the OPLL time-divides the output for 'mixing'.
|
||||
|
||||
const int update_period = 72 / audio_divider_;
|
||||
const int channel_output_period = 4 / audio_divider_;
|
||||
|
||||
// TODO: the conditional below is terrible. Fix.
|
||||
while(number_of_samples--) {
|
||||
if(!audio_offset_) update_all_channels();
|
||||
|
||||
*target = output_levels_[audio_offset_ / channel_output_period];
|
||||
++target;
|
||||
audio_offset_ = (audio_offset_ + 1) % update_period;
|
||||
}
|
||||
}
|
||||
|
||||
void OPLL::update_all_channels() {
|
||||
oscillator_.update();
|
||||
|
||||
// Update all phase generators. That's guaranteed.
|
||||
for(int c = 0; c < 18; ++c) {
|
||||
phase_generators_[c].update(oscillator_);
|
||||
}
|
||||
|
||||
// Update the ADSR envelopes that are guaranteed to be melodic.
|
||||
for(int c = 0; c < 6; ++c) {
|
||||
envelope_generators_[c + 0].update(oscillator_);
|
||||
envelope_generators_[c + 9].update(oscillator_);
|
||||
}
|
||||
|
||||
#define VOLUME(x) int16_t(((x) * total_volume_) >> 12)
|
||||
|
||||
if(rhythm_mode_enabled_) {
|
||||
// Advance the rhythm envelope generators.
|
||||
for(int c = 0; c < 6; ++c) {
|
||||
rhythm_envelope_generators_[c].update(oscillator_);
|
||||
}
|
||||
|
||||
// Fill in the melodic channels.
|
||||
output_levels_[3] = VOLUME(melodic_output(0));
|
||||
output_levels_[4] = VOLUME(melodic_output(1));
|
||||
output_levels_[5] = VOLUME(melodic_output(2));
|
||||
|
||||
output_levels_[9] = VOLUME(melodic_output(3));
|
||||
output_levels_[10] = VOLUME(melodic_output(4));
|
||||
output_levels_[11] = VOLUME(melodic_output(5));
|
||||
|
||||
// Bass drum, which is a regular FM effect.
|
||||
output_levels_[2] = output_levels_[15] = VOLUME(bass_drum());
|
||||
oscillator_.update_lfsr();
|
||||
|
||||
// Tom tom, which is a single operator.
|
||||
output_levels_[1] = output_levels_[14] = VOLUME(tom_tom());
|
||||
oscillator_.update_lfsr();
|
||||
|
||||
// Snare.
|
||||
output_levels_[6] = output_levels_[16] = VOLUME(snare_drum());
|
||||
oscillator_.update_lfsr();
|
||||
|
||||
// Cymbal.
|
||||
output_levels_[7] = output_levels_[17] = VOLUME(cymbal());
|
||||
oscillator_.update_lfsr();
|
||||
|
||||
// High-hat.
|
||||
output_levels_[0] = output_levels_[13] = VOLUME(high_hat());
|
||||
oscillator_.update_lfsr();
|
||||
|
||||
// Unutilised slots.
|
||||
output_levels_[8] = output_levels_[12] = 0;
|
||||
oscillator_.update_lfsr();
|
||||
} else {
|
||||
for(int c = 6; c < 9; ++c) {
|
||||
envelope_generators_[c + 0].update(oscillator_);
|
||||
envelope_generators_[c + 9].update(oscillator_);
|
||||
}
|
||||
|
||||
// All melodic. Fairly easy.
|
||||
output_levels_[0] = output_levels_[1] = output_levels_[2] =
|
||||
output_levels_[6] = output_levels_[7] = output_levels_[8] =
|
||||
output_levels_[12] = output_levels_[13] = output_levels_[14] = 0;
|
||||
|
||||
output_levels_[3] = VOLUME(melodic_output(0));
|
||||
output_levels_[4] = VOLUME(melodic_output(1));
|
||||
output_levels_[5] = VOLUME(melodic_output(2));
|
||||
|
||||
output_levels_[9] = VOLUME(melodic_output(3));
|
||||
output_levels_[10] = VOLUME(melodic_output(4));
|
||||
output_levels_[11] = VOLUME(melodic_output(5));
|
||||
|
||||
output_levels_[15] = VOLUME(melodic_output(6));
|
||||
output_levels_[16] = VOLUME(melodic_output(7));
|
||||
output_levels_[17] = VOLUME(melodic_output(8));
|
||||
}
|
||||
|
||||
#undef VOLUME
|
||||
|
||||
// TODO: batch updates of the LFSR.
|
||||
}
|
||||
|
||||
// TODO: verify attenuation scales pervasively below.
|
||||
|
||||
int OPLL::melodic_output(int channel) {
|
||||
// The modulator always updates after the carrier, oddly enough. So calculate actual output first, based on the modulator's last value.
|
||||
auto carrier = WaveformGenerator<period_precision>::wave(channels_[channel].carrier_waveform, phase_generators_[channel].scaled_phase(), channels_[channel].modulator_output);
|
||||
carrier += envelope_generators_[channel].attenuation() + (channels_[channel].attenuation << 7) + key_level_scalers_[channel].attenuation();
|
||||
|
||||
// Get the modulator's new value.
|
||||
auto modulation = WaveformGenerator<period_precision>::wave(channels_[channel].modulator_waveform, phase_generators_[channel + 9].phase());
|
||||
modulation += envelope_generators_[channel + 9].attenuation() + (channels_[channel].modulator_attenuation << 5) + key_level_scalers_[channel + 9].attenuation();
|
||||
|
||||
// Apply feedback, if any.
|
||||
phase_generators_[channel + 9].apply_feedback(channels_[channel].modulator_output, modulation, channels_[channel].modulator_feedback);
|
||||
channels_[channel].modulator_output = modulation;
|
||||
|
||||
return carrier.level();
|
||||
}
|
||||
|
||||
int OPLL::bass_drum() {
|
||||
// Use modulator 6 and carrier 6, attenuated as per the bass-specific envelope generators and the attenuation level for channel 6.
|
||||
auto modulation = WaveformGenerator<period_precision>::wave(Waveform::Sine, phase_generators_[6 + 9].phase());
|
||||
modulation += rhythm_envelope_generators_[RhythmIndices::BassModulator].attenuation();
|
||||
|
||||
auto carrier = WaveformGenerator<period_precision>::wave(Waveform::Sine, phase_generators_[6].scaled_phase(), modulation);
|
||||
carrier += rhythm_envelope_generators_[RhythmIndices::BassCarrier].attenuation() + (channels_[6].attenuation << 7);
|
||||
return carrier.level();
|
||||
}
|
||||
|
||||
int OPLL::tom_tom() {
|
||||
// Use modulator 8 and the 'instrument' selection for channel 8 as an attenuation.
|
||||
auto tom_tom = WaveformGenerator<period_precision>::wave(Waveform::Sine, phase_generators_[8 + 9].phase());
|
||||
tom_tom += rhythm_envelope_generators_[RhythmIndices::TomTom].attenuation();
|
||||
tom_tom += channels_[8].instrument << 7;
|
||||
return tom_tom.level();
|
||||
}
|
||||
|
||||
int OPLL::snare_drum() {
|
||||
// Use modulator 7 and the carrier attenuation level for channel 7.
|
||||
LogSign snare = WaveformGenerator<period_precision>::snare(oscillator_, phase_generators_[7 + 9].phase());
|
||||
snare += rhythm_envelope_generators_[RhythmIndices::Snare].attenuation();
|
||||
snare += channels_[7].attenuation << 7;
|
||||
return snare.level();
|
||||
}
|
||||
|
||||
int OPLL::cymbal() {
|
||||
// Use modulator 7, carrier 8 and the attenuation level for channel 8.
|
||||
LogSign cymbal = WaveformGenerator<period_precision>::cymbal(phase_generators_[8].phase(), phase_generators_[7 + 9].phase());
|
||||
cymbal += rhythm_envelope_generators_[RhythmIndices::Cymbal].attenuation();
|
||||
cymbal += channels_[8].attenuation << 7;
|
||||
return cymbal.level();
|
||||
}
|
||||
|
||||
int OPLL::high_hat() {
|
||||
// Use modulator 7, carrier 8 a and the 'instrument' selection for channel 7 as an attenuation.
|
||||
LogSign high_hat = WaveformGenerator<period_precision>::high_hat(oscillator_, phase_generators_[8].phase(), phase_generators_[7 + 9].phase());
|
||||
high_hat += rhythm_envelope_generators_[RhythmIndices::HighHat].attenuation();
|
||||
high_hat += channels_[7].instrument << 7;
|
||||
return high_hat.level();
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
//
|
||||
// OPLL.hpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 03/05/2020.
|
||||
// Copyright © 2020 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef OPLL_hpp
|
||||
#define OPLL_hpp
|
||||
|
||||
#include "Implementation/OPLBase.hpp"
|
||||
#include "Implementation/EnvelopeGenerator.hpp"
|
||||
#include "Implementation/KeyLevelScaler.hpp"
|
||||
#include "Implementation/PhaseGenerator.hpp"
|
||||
#include "Implementation/LowFrequencyOscillator.hpp"
|
||||
#include "Implementation/WaveformGenerator.hpp"
|
||||
|
||||
#include <atomic>
|
||||
|
||||
namespace Yamaha {
|
||||
namespace OPL {
|
||||
|
||||
class OPLL: public OPLBase<OPLL> {
|
||||
public:
|
||||
/// Creates a new OPLL or VRC7.
|
||||
OPLL(Concurrency::DeferringAsyncTaskQueue &task_queue, int audio_divider = 1, bool is_vrc7 = false);
|
||||
|
||||
/// As per ::SampleSource; provides audio output.
|
||||
void get_samples(std::size_t number_of_samples, std::int16_t *target);
|
||||
void set_sample_volume_range(std::int16_t range);
|
||||
|
||||
// The OPLL is generally 'half' as loud as it's told to be. This won't strictly be true in
|
||||
// rhythm mode, but it's correct for melodic output.
|
||||
double get_average_output_peak() const { return 0.5; }
|
||||
|
||||
/// Reads from the OPL.
|
||||
uint8_t read(uint16_t address);
|
||||
|
||||
private:
|
||||
friend OPLBase<OPLL>;
|
||||
void write_register(uint8_t address, uint8_t value);
|
||||
|
||||
int audio_divider_ = 0;
|
||||
int audio_offset_ = 0;
|
||||
std::atomic<int> total_volume_;
|
||||
|
||||
int16_t output_levels_[18];
|
||||
void update_all_channels();
|
||||
|
||||
int melodic_output(int channel);
|
||||
int bass_drum();
|
||||
int tom_tom();
|
||||
int snare_drum();
|
||||
int cymbal();
|
||||
int high_hat();
|
||||
|
||||
static constexpr int period_precision = 9;
|
||||
static constexpr int envelope_precision = 7;
|
||||
|
||||
// Standard melodic phase and envelope generators;
|
||||
//
|
||||
// These are assigned as:
|
||||
//
|
||||
// [x], 0 <= x < 9 = carrier for channel x;
|
||||
// [x+9] = modulator for channel x.
|
||||
//
|
||||
PhaseGenerator<period_precision> phase_generators_[18];
|
||||
EnvelopeGenerator<envelope_precision, period_precision> envelope_generators_[18];
|
||||
KeyLevelScaler<period_precision> key_level_scalers_[18];
|
||||
|
||||
// Dedicated rhythm envelope generators and attenuations.
|
||||
EnvelopeGenerator<envelope_precision, period_precision> rhythm_envelope_generators_[6];
|
||||
enum RhythmIndices {
|
||||
HighHat = 0,
|
||||
Cymbal = 1,
|
||||
TomTom = 2,
|
||||
Snare = 3,
|
||||
BassCarrier = 4,
|
||||
BassModulator = 5
|
||||
};
|
||||
|
||||
// Channel specifications.
|
||||
struct Channel {
|
||||
int octave = 0;
|
||||
int period = 0;
|
||||
int instrument = 0;
|
||||
|
||||
int attenuation = 0;
|
||||
int modulator_attenuation = 0;
|
||||
|
||||
Waveform carrier_waveform = Waveform::Sine;
|
||||
Waveform modulator_waveform = Waveform::Sine;
|
||||
|
||||
int carrier_key_rate_scale_multiplier = 0;
|
||||
int modulator_key_rate_scale_multiplier = 0;
|
||||
|
||||
LogSign modulator_output;
|
||||
int modulator_feedback = 0;
|
||||
|
||||
bool use_sustain = false;
|
||||
} channels_[9];
|
||||
|
||||
// The low-frequency oscillator.
|
||||
LowFrequencyOscillator oscillator_;
|
||||
bool rhythm_mode_enabled_ = false;
|
||||
bool is_vrc7_ = false;
|
||||
|
||||
// Contains the current configuration of the custom instrument.
|
||||
uint8_t custom_instrument_[8] = {0, 0, 0, 0, 0, 0, 0, 0};
|
||||
|
||||
// Helpers to push per-channel information.
|
||||
|
||||
/// Pushes the current octave and period to channel @c channel.
|
||||
void set_channel_period(int channel);
|
||||
|
||||
/// Installs the appropriate instrument on channel @c channel.
|
||||
void install_instrument(int channel);
|
||||
|
||||
/// Sets whether the sustain level is used for channel @c channel based on its current instrument
|
||||
/// and the user's selection.
|
||||
void set_use_sustain(int channel);
|
||||
|
||||
/// @returns The 8-byte definition of instrument @c instrument.
|
||||
const uint8_t *instrument_definition(int instrument, int channel);
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* OPLL_hpp */
|
||||
Reference in New Issue
Block a user