diff --git a/plugin/ic29.cpp b/plugin/ic29.cpp index 57f9007..1b998b7 100644 --- a/plugin/ic29.cpp +++ b/plugin/ic29.cpp @@ -25,11 +25,11 @@ Synth ic29; Synth::Synth() { d_debug("initialising synth\n"); envAtk = 0x00; - envDcy = 0x50; - envStn = 0x7f; - envRls = 0x3f; + envDcy = 0x1f; + envStn = 0x00; + envRls = 0x1f; portaCoeff = 0x0; - lfo.speed = 0x1f; + lfo.speed = 0x06; } void Synth::buildTables(double sampleRate) { @@ -49,6 +49,17 @@ void Synth::run() { 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++) { ic29.voices[i].update(); } @@ -91,7 +102,7 @@ void LFO::run() { phase = 1; } - //printf("lfoOut=%04x\n", lfoOut); + // printf("lfoOut=%04x\n", lfoOut); } Envelope::Envelope() { @@ -126,7 +137,7 @@ void Envelope::run() { } Voice::Voice() { - subosc = .11; + subosc = .1; } void Voice::calcPitch() { @@ -173,6 +184,7 @@ void Voice::update() { // calculate the once-per-block values env.run(); calcPitch(); + pw = 0.5 - ic29.pwm; // do filter values } diff --git a/plugin/ic29.hpp b/plugin/ic29.hpp index c0320c6..569cd29 100644 --- a/plugin/ic29.hpp +++ b/plugin/ic29.hpp @@ -81,7 +81,7 @@ class Voice { uint16_t pitch = 0x1818; // calculated pitch value with porta and master pitch etc float delay; // delay slot for polyblep bool pulseStage; - float lastpw; + float pw, lastpw, pwrc; float subosc = -1; float phase = 0, omega = 0; enum { V_DONE, @@ -119,6 +119,8 @@ class Synth { Voice voices[NUM_VOICES]; LFO lfo; + float pwm; + }; // global diff --git a/plugin/oscillator.cpp b/plugin/oscillator.cpp index 685c0d0..6357c14 100644 --- a/plugin/oscillator.cpp +++ b/plugin/oscillator.cpp @@ -30,24 +30,25 @@ static inline float poly3blep1(float t) { void Voice::run(float *buffer, uint32_t samples) { // generate a full block of samples for the oscillator - float y, out, pw = .50, t; - + float y, out, t; float saw = 0; - pw = 0.5-(ic29.lfo.lfoOut + 0x2000) / 61600.0f; - float gain = env.level / 16384.0; + + // this uses an adaptation of Mystran's Polyblep oscillator for (uint32_t i = 0; i < samples; i++) { y = delay; delay = 0; phase += omega; + pwrc = ((pw - pwrc) *.01 ) + pwrc; + // this is the clever bit while (true) { if (!pulseStage) { - if (phase < pw) break; // it's not time for the PWM output to step - t = (phase - pw) / (lastpw - pw + omega); // calculate fractional sample allowing for PW amount + if (phase < pwrc) break; // it's not time for the PWM output to step + 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 delay -= 0.63 * poly3blep1(t); pulseStage = true; @@ -63,14 +64,15 @@ void Voice::run(float *buffer, uint32_t samples) { } } - delay += saw * (0.8 - (1.6 * phase)); // magic numbers observed on oscilloscope from real synth + delay += saw * (0.8 - (1.6 * phase)); // magic numbers observed on oscilloscope from real synth delay += (0.63 - (pw * 1.26)) + (pulseStage ? -0.63f : 0.63f); // add in the scaled pulsewidth to restore DC level // the DC correction is important because the hardware synth is AC-coupled effectively high-passing // the signal at about 10Hz or so, preventing any PWM rumble from leaking through! delay += subosc; out = y * 0.15; - lastpw = pw; + lastpw = pwrc; buffer[i] += out * gain; } + }