1
0
mirror of https://github.com/TomHarte/CLK.git synced 2026-04-21 02:17:08 +00:00

Divide state, start adding waveforms.

This commit is contained in:
Thomas Harte
2025-11-10 17:27:32 -05:00
parent af82a0bcda
commit 6e2cd0ace6
3 changed files with 90 additions and 32 deletions
+19 -9
View File
@@ -14,19 +14,29 @@ SID::SID(Concurrency::AsyncTaskQueue<false> &audio_queue) : audio_queue_(audio_q
void SID::write(const Numeric::SizedInt<5> address, const uint8_t value) {
audio_queue_.enqueue([&] {
const auto voice = [&]() -> Voice & {
return voices_[address.get() / 7];
};
const auto oscillator = [&]() -> Voice::Oscillator & {
return voice().oscillator;
};
const auto adsr = [&]() -> Voice::ADSR & {
return voice().adsr;
};
switch(address.get()) {
case 0x00: case 0x07: case 0x0e: voices_[address.get() / 7].pitch.load<0>(value); break;
case 0x01: case 0x08: case 0x0f: voices_[address.get() / 7].pitch.load<8>(value); break;
case 0x02: case 0x09: case 0x10: voices_[address.get() / 7].pulse_width.load<0>(value); break;
case 0x03: case 0x0a: case 0x11: voices_[address.get() / 7].pulse_width.load<8>(value); break;
case 0x04: case 0x0b: case 0x12: voices_[address.get() / 7].control = value; break;
case 0x00: case 0x07: case 0x0e: oscillator().pitch.load<0>(value); break;
case 0x01: case 0x08: case 0x0f: oscillator().pitch.load<8>(value); break;
case 0x02: case 0x09: case 0x10: oscillator().pulse_width.load<0>(value); break;
case 0x03: case 0x0a: case 0x11: oscillator().pulse_width.load<8>(value); break;
case 0x04: case 0x0b: case 0x12: voice().control = value; break;
case 0x05: case 0x0c: case 0x13:
voices_[address.get() / 7].attack = value >> 4;
voices_[address.get() / 7].decay = value;
adsr().attack = value >> 4;
adsr().decay = value;
break;
case 0x06: case 0x0d: case 0x14:
voices_[address.get() / 7].sustain = value >> 4;
voices_[address.get() / 7].release = value;
adsr().sustain = value >> 4;
adsr().release = value;
break;
}
});
+69 -22
View File
@@ -32,40 +32,87 @@ private:
Concurrency::AsyncTaskQueue<false> &audio_queue_;
struct Voice {
Numeric::SizedInt<16> pitch;
Numeric::SizedInt<12> pulse_width;
struct Oscillator {
// Programmer inputs.
Numeric::SizedInt<16> pitch;
Numeric::SizedInt<12> pulse_width;
// State.
Numeric::SizedInt<24> phase;
Numeric::SizedInt<24> previous_phase;
} oscillator;
struct ADSR {
// Programmer inputs.
Numeric::SizedInt<4> attack;
Numeric::SizedInt<4> decay;
Numeric::SizedInt<4> sustain;
Numeric::SizedInt<4> release;
// State.
Numeric::SizedInt<8> envelope;
Numeric::SizedInt<15> rate_counter;
enum class Phase {
Attack,
DecayAndHold,
Release,
} phase_ = Phase::Attack;
} adsr;
Numeric::SizedInt<8> control;
Numeric::SizedInt<4> attack;
Numeric::SizedInt<4> decay;
Numeric::SizedInt<4> sustain;
Numeric::SizedInt<4> release;
bool noise() const { return control.bit<7>(); }
bool pulse() const { return control.bit<6>(); }
bool sawtooth() const { return control.bit<5>(); }
bool triangle() const { return control.bit<4>(); }
bool test() const { return control.bit<3>(); } // Implemented.
bool ring_mod() const { return control.bit<2>(); }
bool sync() const { return control.bit<1>(); } // Implemented.
bool gate() const { return control.bit<0>(); }
Numeric::SizedInt<24> phase;
Numeric::SizedInt<24> previous_phase;
bool noise() const { return control.bit<7>(); }
bool pulse() const { return control.bit<6>(); }
bool sawtooth() const { return control.bit<5>(); }
bool triangle() const { return control.bit<4>(); }
bool test() const { return control.bit<3>(); } // Implemented.
bool ring_mod() const { return control.bit<2>(); }
bool sync() const { return control.bit<1>(); } // Implemented.
bool gate() const { return control.bit<0>(); }
void update() {
previous_phase = phase;
// Oscillator.
oscillator.previous_phase = oscillator.phase;
if(test()) {
phase = 0;
oscillator.phase = 0;
} else {
phase += pitch.get();
oscillator.phase += oscillator.pitch.get();
}
// TODO: ADSR.
}
void synchronise(const Voice &prior) {
if(sync() && !prior.previous_phase.bit<23>() && prior.phase.bit<23>()) {
phase = 0;
// Only oscillator work to do here.
if(
sync() &&
!prior.oscillator.previous_phase.bit<23>() &&
prior.oscillator.phase.bit<23>()
) {
oscillator.phase = 0;
}
}
static constexpr uint16_t MaxWaveformValue = (1 << 12) - 1;
uint16_t pulse_output() const {
return oscillator.phase.get<12>() > oscillator.pulse_width.get() ? MaxWaveformValue : 0;
}
uint16_t output() 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();
return output;
}
};
Voice voices_[3];
+2 -1
View File
@@ -24,8 +24,9 @@ struct SizedInt {
constexpr SizedInt(const IntT start_value) noexcept : counter_(start_value & Mask) {}
SizedInt() = default;
template <int begin = 0>
IntT get() const {
return counter_;
return counter_ >> begin;
}
SizedInt operator +(const SizedInt offset) const { return SizedInt<bits>(counter_ + offset.counter_); }