juno style oscillator

This commit is contained in:
Gordon JC Pearce 2024-09-07 20:46:57 +01:00
parent e3add1d306
commit f2c4727a42
3 changed files with 56 additions and 77 deletions

View File

@ -59,13 +59,13 @@ void Chassis::initParameter(uint32_t index, Parameter &parameter) {
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);
// 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]);
switch (ev[i].data[0]) {
case 0x90:
noteOn(ev[i].data[1]);
}
if (ev[i].data[0] == 0x80) {
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;

View File

@ -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);
}
}

View File

@ -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;