From effdce42bed16472cc714fe8b711ecd600f3f1a8 Mon Sep 17 00:00:00 2001 From: Gordon JC Pearce Date: Tue, 15 Oct 2024 22:56:18 +0100 Subject: [PATCH] basics of oscillator --- plugin/Makefile | 1 + plugin/ic29.cpp | 11 +------ plugin/ic29.hpp | 4 +++ plugin/oscillator.cpp | 73 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 79 insertions(+), 10 deletions(-) create mode 100644 plugin/oscillator.cpp diff --git a/plugin/Makefile b/plugin/Makefile index 70a5719..6c0a4ec 100644 --- a/plugin/Makefile +++ b/plugin/Makefile @@ -12,6 +12,7 @@ NAME = peacock FILES_DSP = \ peacock.cpp \ ic1.cpp \ + oscillator.cpp \ ic29.cpp include ../dpf/Makefile.plugins.mk diff --git a/plugin/ic29.cpp b/plugin/ic29.cpp index b7dfc13..84aa3ff 100644 --- a/plugin/ic29.cpp +++ b/plugin/ic29.cpp @@ -101,6 +101,7 @@ void Envelope::run() { } Voice::Voice() { + subosc = 1; } void Voice::calcPitch() { @@ -156,13 +157,3 @@ void Voice::off() { env.off(); } } - -void Voice::run(float *buffer, uint32_t samples) { - float gain = env.level / 16384.0; - gain *= 0.125; - for (uint32_t i = 0; i < samples; i++) { - phase += omega; - if (phase > 1.0f) phase -= 1.0f; - buffer[i] += phase * gain; - } -} diff --git a/plugin/ic29.hpp b/plugin/ic29.hpp index f39be0d..a41c17c 100644 --- a/plugin/ic29.hpp +++ b/plugin/ic29.hpp @@ -59,6 +59,10 @@ class Voice { private: Envelope env; // calculated envelope value uint16_t pitch = 0x1818; // calculated pitch value with porta and master pitch etc + float delay; // delay slot for polyblep + bool pulseStage; + float lastpw; + float subosc = -1; float phase = 0, omega = 0; enum { V_DONE, V_OFF, diff --git a/plugin/oscillator.cpp b/plugin/oscillator.cpp new file mode 100644 index 0000000..6dcdcbf --- /dev/null +++ b/plugin/oscillator.cpp @@ -0,0 +1,73 @@ +/* + Peacock-8 VA polysynth + + Copyright 2024 Gordon JC Pearce + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#include "ic29.hpp" + +static inline float poly3blep0(float t) { + float t2 = t * t; + return 2 * (t * t2 - 0.5f * t2 * t2); +} + +static inline float poly3blep1(float t) { + return -poly3blep0(1 - t); +} + +void Voice::run(float *buffer, uint32_t samples) { + // generate a full block of samples for the oscillator + + float y, out, pw = 0.0, t; + + 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; + + // 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 + y -= 0.63 * poly3blep0(t); // magic numbers observed on oscilloscope from real synth + delay -= 0.63 * poly3blep1(t); + pulseStage = true; + } + if (pulseStage) { + if (phase < 1) break; // it's not time to reset the saw + t = (phase - 1) / omega; + y += poly3blep0(t) * (0.8 + 0.63 - subosc); + delay += poly3blep1(t) * (0.8 + 0.63 - subosc); + pulseStage = 0; + phase -= 1; + subosc = -subosc; + } + } + + delay += (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; + buffer[i] += out * gain; + } +}