mirror of
https://github.com/TomHarte/CLK.git
synced 2026-04-22 08:16:42 +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 */
|
||||
Reference in New Issue
Block a user