From 8c66392e97e3f57322a84d620beb874cf5e6e624 Mon Sep 17 00:00:00 2001 From: Gordon JC Pearce Date: Wed, 17 Dec 2025 22:06:09 +0000 Subject: [PATCH] add basics of voice definitions and module --- plugin/Makefile | 2 + plugin/module.cpp | 23 ++++++++++++ plugin/module.hpp | 73 +++++++++++++++++++++++++++++++++++++ plugin/voice.cpp | 93 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 191 insertions(+) create mode 100644 plugin/module.cpp create mode 100644 plugin/module.hpp create mode 100644 plugin/voice.cpp diff --git a/plugin/Makefile b/plugin/Makefile index ba26ee0..306d0cb 100644 --- a/plugin/Makefile +++ b/plugin/Makefile @@ -11,6 +11,8 @@ NAME = peacock FILES_DSP = \ assigner.cpp \ + module.cpp \ + voice.cpp \ peacock.cpp include ../dpf/Makefile.plugins.mk diff --git a/plugin/module.cpp b/plugin/module.cpp new file mode 100644 index 0000000..5ccd2f3 --- /dev/null +++ b/plugin/module.cpp @@ -0,0 +1,23 @@ +/* + Peacock-8 VA polysynth + + Copyright 2025 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 "module.hpp" + +Module::Module() { + +} diff --git a/plugin/module.hpp b/plugin/module.hpp new file mode 100644 index 0000000..e2728f6 --- /dev/null +++ b/plugin/module.hpp @@ -0,0 +1,73 @@ +/* + Peacock-8 VA polysynth + + Copyright 2025 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. +*/ + +#ifndef _MODULE_HPP +#define _MODULE_HPP + +#include + +#include "DistrhoPluginInfo.h" + +class Module { + public: + Module(); + //Voice voices[NUM_VOICES]; + float vcfCutoff = 0, vcfReso = 0; + + // precomputed values for all voices + float pw, saw, square, sub; + + struct { + uint8_t lfoRate = 0x30; // lookup value defaults to 0x0200 + uint8_t lfoDelay = 0x00; + uint8_t vcoLfo = 0x0a; + uint8_t pwmLfo = 0x30; + uint8_t noise = 0x00; + uint8_t vcfFreq = 0x3c; // 0x3f80 + uint8_t vcfReso = 0x00; + uint8_t vcfEnv = 0x2e; + uint8_t vcfLfo = 0; + uint8_t vcfKey = 0x47; + uint8_t vca = 0x28; + uint8_t env_a = 0x1b; + uint8_t env_d = 0x39; + uint8_t env_s = 0x39; // 0x3f80 + uint8_t env_r = 0x30; + uint8_t sub = 0x00; + uint8_t switch1 = 0x1a; + uint8_t switch2 = 0x18; + } patchRam; +}; + +class Voice { + public: + Voice(); + void on(uint8_t note); + void off(); + void run(Module *m, float *buffer, uint32_t samples); + + private: + float omega = 0, theta = 0; // phase increment and angle + float delay = 0, lastpw = 0; // delay slots for antialiasing + uint8_t pulseStage = 1; // pulse wave phase + float subosc = 1; // sub oscillator flipflop output + float amp = 0; // output amplitude +}; + + +#endif \ No newline at end of file diff --git a/plugin/voice.cpp b/plugin/voice.cpp new file mode 100644 index 0000000..e300783 --- /dev/null +++ b/plugin/voice.cpp @@ -0,0 +1,93 @@ +/* + Peacock-8 VA polysynth + + Copyright 2025 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 + +#include "module.hpp" + + +// antialiasing using polybleps, as described in KVRAudio forum by Mystran + +static inline float poly3blep0(float t) { + float t2 = t * t; + return t * t2 - 0.5f * t2 * t2; +} + +static inline float poly3blep1(float t) { + return -poly3blep0(1 - t); +} + +Voice::Voice() { + omega = 0.0; + theta = 0.0; + amp = 0; +} + +void Voice::on(uint8_t note) { + omega = 261.63 * powf(2, (note - 60) / 12.0f) / 48000.0f; + amp = 1; +} + +void Voice::off() { + amp = 0; +} + +void Voice::run(Module *m, float* buffer, uint32_t samples) { + // carry out per-voice calculations for each block of samples + float out, t; + + for (uint32_t i = 0; i < samples; i++) { + out = delay; + delay = 0; + + theta += omega; + + while (true) { + if (pulseStage == 0) { + if (theta < m->pw) break; + t = (theta - m->pw) / (lastpw - m->pw + omega); + out -= poly3blep0(t) * m->square; + delay -= poly3blep1(t) * m->square; + pulseStage = 1; + } + + if (pulseStage == 1) { + if (theta < 1) break; // no need to blep yet + t = (theta - 1) / omega; // scaled remainder of phase + out += poly3blep0(t) * (m->saw + m->square); + delay += poly3blep1(t) * (m->saw + m->square); + + out -= poly3blep0(t) * (m->sub * subosc); + delay -= poly3blep1(t) * (m->sub * subosc); + pulseStage = 0; + subosc = -subosc; + + theta -= 1; + } + } + + delay += m->saw * (1 - (2 * theta)); + delay += m->square * (pulseStage ? -1.f : 1.f); + delay += m->sub * subosc; + // delay += (1-(m->noisegen/(float)(1<<30))) * m->noise; FIXME figure out what to do about noise + + buffer[i] = 0.125 * amp * out; + lastpw = m->pw; + } + +}