mirror of
https://github.com/TomHarte/CLK.git
synced 2025-12-20 06:16:41 +00:00
Clean up: pull out noise generation, remove code from header.
This commit is contained in:
@@ -18,6 +18,8 @@ SID::SID(Concurrency::AsyncTaskQueue<false> &audio_queue) :
|
||||
15000.0f
|
||||
) {}
|
||||
|
||||
// MARK: - Programmer interface.
|
||||
|
||||
void SID::write(const Numeric::SizedInt<5> address, const uint8_t value) {
|
||||
last_write_ = value;
|
||||
audio_queue_.enqueue([=, this] {
|
||||
@@ -111,10 +113,23 @@ void SID::update_filter() {
|
||||
type,
|
||||
1'000'000.0f,
|
||||
30.0f + float(filter_cutoff_.get()) * 5.8f,
|
||||
0.707f + float(filter_resonance_.get()) * 0.125f,
|
||||
0.707f + float(filter_resonance_.get()) * 0.2862f,
|
||||
6.0f,
|
||||
true
|
||||
);
|
||||
|
||||
// Filter cutoff: the data sheet provides that it is linear, and "approximate Cutoff Frequency
|
||||
// ranges between 30Hz and 12KHz [with recommended externally-supplied capacitors]."
|
||||
//
|
||||
// It's an 11-bit number, so the above is "approximate"ly right.
|
||||
|
||||
// Resonance: a complete from-thin-air guess. The data sheet says merely:
|
||||
//
|
||||
// "There are 16 Resonance settings ranging from about 0.707 (Critical Damping) for a count of 0
|
||||
// to a maximum for a count of 15"
|
||||
//
|
||||
// i.e. no information is given on the maximum. I've taken it to be 5-ish per commentary on more general sites
|
||||
// that 5 is a typical ceiling for the resonance factor.
|
||||
}
|
||||
|
||||
uint8_t SID::read(const Numeric::SizedInt<5> address) {
|
||||
@@ -122,6 +137,208 @@ uint8_t SID::read(const Numeric::SizedInt<5> address) {
|
||||
return last_write_;
|
||||
}
|
||||
|
||||
// MARK: - Oscillators.
|
||||
|
||||
void Voice::Oscillator::reset_phase() {
|
||||
phase = PhaseReload;
|
||||
}
|
||||
|
||||
bool Voice::Oscillator::did_raise_b23() const {
|
||||
return previous_phase > phase;
|
||||
}
|
||||
|
||||
bool Voice::Oscillator::did_raise_b19() const {
|
||||
static constexpr int NoiseBit = 1 << (19 + 8);
|
||||
return (previous_phase ^ phase) & phase & NoiseBit;
|
||||
}
|
||||
|
||||
uint16_t Voice::Oscillator::sawtooth_output() const {
|
||||
return (phase >> 20) ^ 0x800;
|
||||
}
|
||||
|
||||
// MARK: - Noise generator.
|
||||
|
||||
uint16_t Voice::NoiseGenerator::output() const {
|
||||
// Uses bits: 20, 18, 14, 11, 9, 5, 2 and 0, plus four more zero bits.
|
||||
return
|
||||
((noise >> 9) & 0b1000'0000'0000) | // b20 -> b11
|
||||
((noise >> 8) & 0b0100'0000'0000) | // b18 -> b10
|
||||
((noise >> 5) & 0b0010'0000'0000) | // b14 -> b9
|
||||
((noise >> 3) & 0b0001'0000'0000) | // b11 -> b8
|
||||
((noise >> 2) & 0b0000'1000'0000) | // b9 -> b7
|
||||
((noise << 1) & 0b0000'0100'0000) | // b5 -> b6
|
||||
((noise << 3) & 0b0000'0010'0000) | // b2 -> b5
|
||||
((noise << 4) & 0b0000'0001'0000); // b0 -> b4
|
||||
}
|
||||
|
||||
void Voice::NoiseGenerator::update(const bool test) {
|
||||
noise =
|
||||
(noise << 1) |
|
||||
(((noise >> 17) ^ ((noise >> 22) | test)) & 1);
|
||||
}
|
||||
|
||||
// MARK: - ADSR.
|
||||
|
||||
void Voice::ADSR::set_phase(const Phase new_phase) {
|
||||
static constexpr uint16_t rate_prescaler[] = {
|
||||
9, 32, 63, 95, 149, 220, 267, 313, 392, 977, 1954, 3126, 3907, 11720, 19532, 31251
|
||||
};
|
||||
static_assert(sizeof(rate_prescaler) / sizeof(*rate_prescaler) == 16);
|
||||
|
||||
phase = new_phase;
|
||||
switch(phase) {
|
||||
case Phase::Attack: rate_counter_target = rate_prescaler[attack.get()]; break;
|
||||
case Phase::DecayAndHold: rate_counter_target = rate_prescaler[decay.get()]; break;
|
||||
case Phase::Release: rate_counter_target = rate_prescaler[release.get()]; break;
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Voices.
|
||||
|
||||
void Voice::set_control(const uint8_t new_control) {
|
||||
const bool old_gate = gate();
|
||||
control = new_control;
|
||||
if(gate() && !old_gate) {
|
||||
adsr.set_phase(ADSR::Phase::Attack);
|
||||
} else if(!gate() && old_gate) {
|
||||
adsr.set_phase(ADSR::Phase::Release);
|
||||
}
|
||||
}
|
||||
|
||||
bool Voice::noise() const { return control.bit<7>(); }
|
||||
bool Voice::pulse() const { return control.bit<6>(); }
|
||||
bool Voice::sawtooth() const { return control.bit<5>(); }
|
||||
bool Voice::triangle() const { return control.bit<4>(); }
|
||||
bool Voice::test() const { return control.bit<3>(); }
|
||||
bool Voice::ring_mod() const { return control.bit<2>(); }
|
||||
bool Voice::sync() const { return control.bit<1>(); }
|
||||
bool Voice::gate() const { return control.bit<0>(); }
|
||||
|
||||
void Voice::update() {
|
||||
// Oscillator.
|
||||
oscillator.previous_phase = oscillator.phase;
|
||||
if(test()) {
|
||||
oscillator.phase = 0;
|
||||
} else {
|
||||
oscillator.phase += oscillator.pitch;
|
||||
|
||||
if(oscillator.did_raise_b19()) {
|
||||
noise_generator.update(test());
|
||||
}
|
||||
}
|
||||
|
||||
// ADSR.
|
||||
|
||||
// First prescalar, which is a function of the programmer-set rate.
|
||||
++ adsr.rate_counter;
|
||||
if(adsr.rate_counter == adsr.rate_counter_target) {
|
||||
adsr.rate_counter = 0;
|
||||
|
||||
// Second prescalar, which approximates an exponential.
|
||||
static constexpr uint8_t exponential_prescaler[] = {
|
||||
1, // 0
|
||||
30, 30, 30, 30, 30, 30, // 1–6
|
||||
16, 16, 16, 16, 16, 16, 16, 16, // 7–14
|
||||
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, // 15–26
|
||||
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
|
||||
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, // 27–54
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 55–94
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1,
|
||||
};
|
||||
static_assert(sizeof(exponential_prescaler) == 256);
|
||||
static_assert(exponential_prescaler[0] == 1);
|
||||
static_assert(exponential_prescaler[1] == 30);
|
||||
static_assert(exponential_prescaler[6] == 30);
|
||||
static_assert(exponential_prescaler[7] == 16);
|
||||
static_assert(exponential_prescaler[14] == 16);
|
||||
static_assert(exponential_prescaler[15] == 8);
|
||||
static_assert(exponential_prescaler[26] == 8);
|
||||
static_assert(exponential_prescaler[27] == 4);
|
||||
static_assert(exponential_prescaler[54] == 4);
|
||||
static_assert(exponential_prescaler[55] == 2);
|
||||
static_assert(exponential_prescaler[94] == 2);
|
||||
static_assert(exponential_prescaler[95] == 1);
|
||||
static_assert(exponential_prescaler[255] == 1);
|
||||
|
||||
if(adsr.phase == ADSR::Phase::Attack) {
|
||||
++adsr.envelope;
|
||||
// TODO: what really resets the exponential counter? If anything?
|
||||
adsr.exponential_counter = 0;
|
||||
|
||||
if(adsr.envelope == 0xff) {
|
||||
adsr.set_phase(ADSR::Phase::DecayAndHold);
|
||||
}
|
||||
} else {
|
||||
++adsr.exponential_counter;
|
||||
if(adsr.exponential_counter == exponential_prescaler[adsr.envelope]) {
|
||||
adsr.exponential_counter = 0;
|
||||
|
||||
if(adsr.envelope && (adsr.envelope != adsr.sustain || adsr.phase != ADSR::Phase::DecayAndHold)) {
|
||||
--adsr.envelope;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Voice::synchronise(const Voice &prior) {
|
||||
// Only oscillator work to do here.
|
||||
if(
|
||||
sync() &&
|
||||
prior.oscillator.did_raise_b23()
|
||||
) {
|
||||
oscillator.phase = Oscillator::PhaseReload;
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t Voice::pulse_output() const {
|
||||
return (
|
||||
(oscillator.phase ^ 0x8000'0000) < oscillator.pulse_width
|
||||
) ? 0 : MaxWaveformValue;
|
||||
}
|
||||
|
||||
uint16_t Voice::triangle_output(const Voice &prior) const {
|
||||
const uint16_t sawtooth = oscillator.sawtooth_output();
|
||||
const uint16_t xor_mask1 = sawtooth;
|
||||
const uint16_t xor_mask2 = ring_mod() ? prior.sawtooth() : 0;
|
||||
const uint16_t xor_mask = ((xor_mask1 ^ xor_mask2) & 0x800) ? 0xfff : 0x000;
|
||||
return ((sawtooth << 1) ^ xor_mask) & 0xfff;
|
||||
}
|
||||
|
||||
uint16_t Voice::output(const Voice &prior) const {
|
||||
// TODO: true composite waves.
|
||||
//
|
||||
// My current understanding on this: if multiple waveforms are enabled, the pull to zero beats the
|
||||
// pull to one on any line where the two compete. But the twist is that the lines are not necessarily
|
||||
// one per bit since they lead to a common ground. Ummm, I think.
|
||||
//
|
||||
// Anyway, first pass: logical AND. It's not right. It will temporarily do.
|
||||
|
||||
uint16_t output = MaxWaveformValue;
|
||||
|
||||
if(pulse()) output &= pulse_output();
|
||||
if(sawtooth()) output &= oscillator.sawtooth_output();
|
||||
if(triangle()) output &= triangle_output(prior);
|
||||
if(noise()) output &= noise_generator.output();
|
||||
|
||||
return (output * adsr.envelope) / 255;
|
||||
}
|
||||
|
||||
// MARK: - Wave generation
|
||||
|
||||
void SID::set_sample_volume_range(const std::int16_t range) {
|
||||
range_ = range;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user