improved PWM code

This commit is contained in:
Gordon JC Pearce 2024-10-17 00:01:46 +01:00
parent b8265f6938
commit 18dd0947d4
3 changed files with 31 additions and 15 deletions

View File

@ -25,11 +25,11 @@ Synth ic29;
Synth::Synth() { Synth::Synth() {
d_debug("initialising synth\n"); d_debug("initialising synth\n");
envAtk = 0x00; envAtk = 0x00;
envDcy = 0x50; envDcy = 0x1f;
envStn = 0x7f; envStn = 0x00;
envRls = 0x3f; envRls = 0x1f;
portaCoeff = 0x0; portaCoeff = 0x0;
lfo.speed = 0x1f; lfo.speed = 0x06;
} }
void Synth::buildTables(double sampleRate) { void Synth::buildTables(double sampleRate) {
@ -49,6 +49,17 @@ void Synth::run() {
masterPitch = 0x1818; masterPitch = 0x1818;
// need to calculate VCF "base" setting
// need to calculate PWM
// various on/off switches
// PWM is bit 0 sw2, 0 = fixed 1 = lfo
// 0 sets EA to 0x3fff, 1 adds
uint16_t pwmVal = 0x2000 - ic29.lfo.lfoOut;
if (0) pwmVal = 0x3fff;
ic29.pwm = pwmVal / 40960.0f * (1); // 0.5 is knob val
for (uint8_t i = 0; i < NUM_VOICES; i++) { for (uint8_t i = 0; i < NUM_VOICES; i++) {
ic29.voices[i].update(); ic29.voices[i].update();
} }
@ -126,7 +137,7 @@ void Envelope::run() {
} }
Voice::Voice() { Voice::Voice() {
subosc = .11; subosc = .1;
} }
void Voice::calcPitch() { void Voice::calcPitch() {
@ -173,6 +184,7 @@ void Voice::update() {
// calculate the once-per-block values // calculate the once-per-block values
env.run(); env.run();
calcPitch(); calcPitch();
pw = 0.5 - ic29.pwm;
// do filter values // do filter values
} }

View File

@ -81,7 +81,7 @@ class Voice {
uint16_t pitch = 0x1818; // calculated pitch value with porta and master pitch etc uint16_t pitch = 0x1818; // calculated pitch value with porta and master pitch etc
float delay; // delay slot for polyblep float delay; // delay slot for polyblep
bool pulseStage; bool pulseStage;
float lastpw; float pw, lastpw, pwrc;
float subosc = -1; float subosc = -1;
float phase = 0, omega = 0; float phase = 0, omega = 0;
enum { V_DONE, enum { V_DONE,
@ -119,6 +119,8 @@ class Synth {
Voice voices[NUM_VOICES]; Voice voices[NUM_VOICES];
LFO lfo; LFO lfo;
float pwm;
}; };
// global // global

View File

@ -30,24 +30,25 @@ static inline float poly3blep1(float t) {
void Voice::run(float *buffer, uint32_t samples) { void Voice::run(float *buffer, uint32_t samples) {
// generate a full block of samples for the oscillator // generate a full block of samples for the oscillator
float y, out, pw = .50, t; float y, out, t;
float saw = 0; float saw = 0;
pw = 0.5-(ic29.lfo.lfoOut + 0x2000) / 61600.0f;
float gain = env.level / 16384.0; float gain = env.level / 16384.0;
// this uses an adaptation of Mystran's Polyblep oscillator // this uses an adaptation of Mystran's Polyblep oscillator
for (uint32_t i = 0; i < samples; i++) { for (uint32_t i = 0; i < samples; i++) {
y = delay; y = delay;
delay = 0; delay = 0;
phase += omega; phase += omega;
pwrc = ((pw - pwrc) *.01 ) + pwrc;
// this is the clever bit // this is the clever bit
while (true) { while (true) {
if (!pulseStage) { if (!pulseStage) {
if (phase < pw) break; // it's not time for the PWM output to step if (phase < pwrc) break; // it's not time for the PWM output to step
t = (phase - pw) / (lastpw - pw + omega); // calculate fractional sample allowing for PW amount t = (phase - pwrc) / (lastpw - pwrc + omega); // calculate fractional sample allowing for PW amount
y -= 0.63 * poly3blep0(t); // magic numbers observed on oscilloscope from real synth y -= 0.63 * poly3blep0(t); // magic numbers observed on oscilloscope from real synth
delay -= 0.63 * poly3blep1(t); delay -= 0.63 * poly3blep1(t);
pulseStage = true; pulseStage = true;
@ -70,7 +71,8 @@ void Voice::run(float *buffer, uint32_t samples) {
delay += subosc; delay += subosc;
out = y * 0.15; out = y * 0.15;
lastpw = pw; lastpw = pwrc;
buffer[i] += out * gain; buffer[i] += out * gain;
} }
} }