From f2c4727a421d32774137ec0811e69c445dfbd22b Mon Sep 17 00:00:00 2001 From: Gordon JC Pearce Date: Sat, 7 Sep 2024 20:46:57 +0100 Subject: [PATCH] juno style oscillator --- plugin/chassis.cpp | 47 ++++++++++++++++++--------- plugin/voice.cpp | 81 ++++++++++++---------------------------------- plugin/voice.hpp | 5 +-- 3 files changed, 56 insertions(+), 77 deletions(-) diff --git a/plugin/chassis.cpp b/plugin/chassis.cpp index c54198c..81efd3e 100644 --- a/plugin/chassis.cpp +++ b/plugin/chassis.cpp @@ -59,13 +59,13 @@ void Chassis::initParameter(uint32_t index, Parameter ¶meter) { void Chassis::setParameterValue(uint32_t index, float value) { switch (index) { case k_saw: - s.p.saw = value / 127.0f; + s.patch.saw = value / 127.0f; break; case k_sqr: - s.p.sqr = value / 127.0f; + s.patch.sqr = value / 127.0f; break; case k_sub: - s.p.sub = value / 127.0f; + s.patch.sub = value / 127.0f; break; } } @@ -111,10 +111,10 @@ void Chassis::noteOn(uint8_t note) { vPtr++; if (vPtr == NUM_VOICES) vPtr = 0; if (s.voice[vPtr].isFree()) { - //printf("voice %d is free, existing note = %d, note = %d\n", vPtr, s.voice[i].note, note); - // if it's an existing note don't reset + // printf("voice %d is free, existing note = %d, note = %d\n", vPtr, s.voice[i].note, note); + // if it's an existing note don't reset s.voice[vPtr].on(note, s.voice[i].note != note); - //printf("note on %d for voice %d\n", note, vPtr); + // printf("note on %d for voice %d\n", note, vPtr); break; } } @@ -130,25 +130,42 @@ void Chassis::noteOff(uint8_t note) { for (uint32_t i = 0; i < NUM_VOICES; i++) { if (s.voice[i].note == note && !s.voice[i].isFree()) { s.voice[i].off(); - //printf("note off %d for voice %d\n", note, i); + // printf("note off %d for voice %d\n", note, i); break; } } } void Chassis::doMidi(const MidiEvent *ev, uint32_t count, uint32_t timeLimit) { - //printf("doMidi() handling events from %d to %d\n", lastEvent, timeLimit); + // printf("doMidi() handling events from %d to %d\n", lastEvent, timeLimit); uint32_t i; + uint8_t val = 0; if (count == 0) return; for (i = lastEvent; i < count; i++) { - //printf("doMidi event number %d of %d %02x %02x\n", i, count, ev[i].data[0], ev[i].data[1]); + // printf("doMidi event number %d of %d %02x %02x\n", i, count, ev[i].data[0], ev[i].data[1]); if (ev[i].frame > timeLimit) break; - if (ev[i].data[0] == 0x90) { - // printf("in doMidi event %d note on %d\n", i, ev[i].data[1]); - noteOn(ev[i].data[1]); - } - if (ev[i].data[0] == 0x80) { - noteOff(ev[i].data[1]); + switch (ev[i].data[0]) { + case 0x90: + noteOn(ev[i].data[1]); + break; + case 0x80: + noteOff(ev[i].data[1]); + break; + case 0xb0: + //printf("cc %02x %02x\n", ev[i].data[1], ev[i].data[2]); + val = ev[i].data[2]; // get control value + switch (ev[i].data[1]) { + case 16: + s.patch.sqr = val / 127.0f; + break; + case 17: + s.patch.saw = val / 127.0f; + break; + case 18: + s.patch.sub = val / 127.0f; + break; + } + break; } } lastEvent = i; diff --git a/plugin/voice.cpp b/plugin/voice.cpp index ac76a39..aa6cd60 100644 --- a/plugin/voice.cpp +++ b/plugin/voice.cpp @@ -63,33 +63,16 @@ void Voice::off() { } void Voice::gate() { - /* - if (keyState == K_WAIT) { - envState = ATTACK; - keyState = K_ON; - target = 1; - } - - if (keyState == K_OFF) { - envState = RELEASE; - target = 0; - } -*/ env = ((target - env) * 0.005f) + env; if (env < 0.001) env = 0; + env = target; } static inline float poly3blep0(float t) { - // these are just sanity checks - // correct code doesn't need them - if (t < 0) return 0; - if (t > 1) return 1; - float t2 = t * t; - return t * t2 - 0.5f * t2 * t2; + return 2 * (t * t2 - 0.5f * t2 * t2); } -// And second sample as wrapper, optimize if you want. static inline float poly3blep1(float t) { return -poly3blep0(1 - t); } @@ -97,12 +80,7 @@ static inline float poly3blep1(float t) { void Voice::run(Synth &s, float *buffer, uint32_t samples) { float y, out, pw = 0, t; - s.p.sqr = 1; - s.p.saw = 1; - - float mix = 1; for (uint32_t i = 0; i < samples; i++) { -#if 1 y = delay; delay = 0; @@ -121,8 +99,8 @@ void Voice::run(Synth &s, float *buffer, uint32_t samples) { #else float t = (phase - pulseWidth) / (widthDelay - pulseWidth + freq); #endif - y += mix * poly3blep0(t); - delay += mix * poly3blep1(t); + y -= poly3blep0(t) * s.patch.sqr; + delay -= poly3blep1(t) * s.patch.sqr; pulseStage = 1; } @@ -130,46 +108,29 @@ void Voice::run(Synth &s, float *buffer, uint32_t samples) { if (phase < 1) break; float t = (phase - 1) / omega; - y -= poly3blep0(t); - delay -= poly3blep1(t); + y += poly3blep0(t) * (s.patch.saw + s.patch.sqr); + delay += poly3blep1(t) * (s.patch.saw + s.patch.sqr); + + y -= poly3blep0(t) * (s.patch.sub * subosc); + delay -= poly3blep1(t) * (s.patch.sub * subosc); + pulseStage = 0; phase -= 1; - subosc = (1-subosc); + subosc = -subosc; } } - delay += (1 - mix) * phase + mix * (pulseStage ? 1.f : 0.f); - delay += subosc; - out = (2 * y) - 1; + delay += s.patch.saw * (1 - (2 * phase)); + + delay += s.patch.sqr * (pulseStage ? -1.f : 1.f); + + delay += s.patch.sub * subosc; + + // out = (2 * y) - 1; + out = y; // widthDelay = pulseWidth; -#else - // sawtooth - - y = 1 - (2 * phase); - y += blep(phase, omega); - - // need this if either saw or square is on; - y *= (s.p.saw + s.p.sqr); - - // bodged for 16ms 48000Hz - // pw_rc = ((pw - pw_rc) * 0.000625) + pw_rc; - pw_rc = 0.5; - - // phase shifted saw - offset = phase + pw_rc; - if (offset > 1) offset -= 1; - - y -= (1 - (2 * offset)) * s.p.sqr; - y -= blep(offset, omega) * s.p.sqr; - // s.lastpw = pw_rc; - - phase += omega; - if (phase > 1) { - // printf("step\n"); - phase -= 1; - } -#endif - buffer[i] += (0.25 * out * env); + vr58c106 += ((env - vr58c106) * 0.0075); + buffer[i] += (0.25 * out * vr58c106); } } diff --git a/plugin/voice.hpp b/plugin/voice.hpp index 60eea31..3cc7dda 100644 --- a/plugin/voice.hpp +++ b/plugin/voice.hpp @@ -51,18 +51,19 @@ class Voice { K_ON, K_SUSTAIN } keyState = K_OFF; - float phase = 0, omega = 260 / 48000.0, subosc = 0; + float phase = 0, omega = 260 / 48000.0, subosc = 1; float env, target; float delay; uint8_t pulseStage = 0; float pw_rc = 0; + float vr58c106 = 0; }; class Synth { public: - Patch p; + Patch patch; Voice voice[8]; float lfo = 0, lfosw = 0.01; float lastpw = 0;