From 3bd35fb83829a6a17fd80be63c9fb3269861b1c9 Mon Sep 17 00:00:00 2001 From: Gordon JC Pearce Date: Fri, 19 Dec 2025 23:21:12 +0000 Subject: [PATCH] initial parameter support --- plugin/Makefile | 1 + plugin/parameters.cpp | 498 ++++++++++++++++++++++++++++++++++++++++++ plugin/peacock.hpp | 42 +++- 3 files changed, 538 insertions(+), 3 deletions(-) create mode 100644 plugin/parameters.cpp diff --git a/plugin/Makefile b/plugin/Makefile index ea645b8..10cdf36 100644 --- a/plugin/Makefile +++ b/plugin/Makefile @@ -14,6 +14,7 @@ FILES_DSP = \ module.cpp \ voice.cpp \ tables.cpp \ + parameters.cpp \ peacock.cpp include ../dpf/Makefile.plugins.mk diff --git a/plugin/parameters.cpp b/plugin/parameters.cpp new file mode 100644 index 0000000..b800d9e --- /dev/null +++ b/plugin/parameters.cpp @@ -0,0 +1,498 @@ +/* + Chassis polysynth framework + + 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 "peacock.hpp" + +void Peacock::initParameter(uint32_t index, Parameter& parameter) { + switch (index) { + case paramLFORate: + parameter.hints = kParameterIsAutomatable; + parameter.name = "LFO Rate"; + parameter.symbol = "ch_lforate"; + parameter.ranges.min = 0.0f; + parameter.ranges.max = 127.0f; + parameter.ranges.def = 48.0f; + parameter.midiCC = 3; + break; + + case paramLFODelay: + parameter.hints = kParameterIsAutomatable; + parameter.name = "LFO Delay"; + parameter.symbol = "ch_lfodelay"; + parameter.ranges.min = 0.0f; + parameter.ranges.max = 127.0f; + parameter.ranges.def = 0.0f; + parameter.midiCC = 9; + break; + + case paramVCORange: + parameter.hints = kParameterIsAutomatable | kParameterIsInteger; + parameter.name = "Range"; + parameter.symbol = "ch_vcorange"; + parameter.ranges.min = 0.0f; + parameter.ranges.max = 2.0f; + parameter.ranges.def = 1.0f; + parameter.midiCC = 12; + parameter.enumValues.count = 3; + parameter.enumValues.restrictedMode = true; + { + ParameterEnumerationValue* const enumValues = new ParameterEnumerationValue[3]; + enumValues[0].value = 0.0f; + enumValues[0].label = "16'"; + enumValues[1].value = 1.0f; + enumValues[1].label = "8'"; + enumValues[2].value = 2.0f; + enumValues[2].label = "4'"; + parameter.enumValues.values = enumValues; + } + + break; + + case paramVCOLFO: + parameter.hints = kParameterIsAutomatable; + parameter.name = "LFO"; + parameter.symbol = "ch_lfo"; + parameter.ranges.min = 0.0f; + parameter.ranges.max = 127.0f; + parameter.ranges.def = 10.0f; + parameter.midiCC = 13; + break; + + case paramPWMLFO: + parameter.hints = kParameterIsAutomatable; + parameter.name = "PWM"; + parameter.symbol = "ch_pwm"; + parameter.ranges.min = 0.0f; + parameter.ranges.max = 127.0f; + parameter.ranges.def = 48.0f; + parameter.midiCC = 14; + break; + + case paramPWMMode: + parameter.hints = kParameterIsAutomatable | kParameterIsBoolean; + parameter.name = "PWM Mode"; + parameter.symbol = "ch_pwmmode"; + parameter.ranges.min = 0.0f; + parameter.ranges.max = 1.0f; + parameter.ranges.def = 1.0f; + parameter.midiCC = 15; + parameter.enumValues.count = 2; + parameter.enumValues.restrictedMode = true; + { + ParameterEnumerationValue* const enumValues = new ParameterEnumerationValue[2]; + enumValues[0].value = 0.0f; + enumValues[0].label = "LFO"; + enumValues[1].value = 1.0f; + enumValues[1].label = "MAN"; + parameter.enumValues.values = enumValues; + } + + break; + + case paramSaw: + parameter.hints = kParameterIsAutomatable | kParameterIsBoolean; + parameter.name = "Saw"; + parameter.symbol = "ch_saw"; + parameter.ranges.min = 0.0f; + parameter.ranges.max = 1.0f; + parameter.ranges.def = 1.0f; + parameter.midiCC = 17; + break; + + case paramSqr: + parameter.hints = kParameterIsAutomatable | kParameterIsBoolean; + parameter.name = "Square"; + parameter.symbol = "ch_sqr"; + parameter.ranges.min = 0.0f; + parameter.ranges.max = 1.0f; + parameter.ranges.def = 1.0f; + parameter.midiCC = 16; + break; + + case paramSub: + parameter.hints = kParameterIsAutomatable; + parameter.name = "Sub Osc"; + parameter.symbol = "ch_sub"; + parameter.ranges.min = 0.0f; + parameter.ranges.max = 127.0f; + parameter.ranges.def = 0.0f; + parameter.midiCC = 18; + break; + + case paramNoise: + parameter.hints = kParameterIsAutomatable; + parameter.name = "Noise"; + parameter.symbol = "ch_noise"; + parameter.ranges.min = 0.0f; + parameter.ranges.max = 127.0f; + parameter.ranges.def = 0.0f; + parameter.midiCC = 19; + break; + + case paramHPF: + parameter.hints = kParameterIsAutomatable | kParameterIsInteger; + parameter.name = "HPF"; + parameter.symbol = "ch_hpf"; + parameter.ranges.min = 0.0f; + parameter.ranges.max = 3.9f; + parameter.ranges.def = 0.0f; + parameter.midiCC = 20; + break; + + case paramVCFFreq: + parameter.hints = kParameterIsAutomatable; + parameter.name = "Freq"; + parameter.symbol = "ch_freq"; + parameter.ranges.min = 0.0f; + parameter.ranges.max = 127.0f; + parameter.ranges.def = 60.0f; + parameter.midiCC = 74; + break; + case paramVCFReso: + parameter.hints = kParameterIsAutomatable; + parameter.name = "Res"; + parameter.symbol = "ch_reso"; + parameter.ranges.min = 0.0f; + parameter.ranges.max = 127.0f; + parameter.ranges.def = 0.0f; + parameter.midiCC = 71; + break; + case paramVCFMode: + parameter.hints = kParameterIsAutomatable | kParameterIsInteger; + parameter.name = "Polarity"; + parameter.symbol = "ch_vcfmode"; + parameter.ranges.min = 0.0f; + parameter.ranges.max = 1.0f; + parameter.ranges.def = 1.0f; + parameter.midiCC = 21; + parameter.enumValues.count = 2; + parameter.enumValues.restrictedMode = true; + { + ParameterEnumerationValue* const enumValues = new ParameterEnumerationValue[2]; + enumValues[0].value = 0.0f; + enumValues[0].label = "POS"; + enumValues[1].value = 1.0f; + enumValues[1].label = "INV"; + parameter.enumValues.values = enumValues; + } + + break; + case paramVCFEnv: + parameter.hints = kParameterIsAutomatable; + parameter.name = "Env"; + parameter.symbol = "ch_vcfenv"; + parameter.ranges.min = 0.0f; + parameter.ranges.max = 127.0f; + parameter.ranges.def = 46.0f; + parameter.midiCC = 22; + break; + case paramVCFLFO: + parameter.hints = kParameterIsAutomatable; + parameter.name = "LFO"; + parameter.symbol = "ch_vcflfo"; + parameter.ranges.min = 0.0f; + parameter.ranges.max = 127.0f; + parameter.ranges.def = 0.0f; + parameter.midiCC = 23; + break; + case paramVCFKey: + parameter.hints = kParameterIsAutomatable; + parameter.name = "Kybd"; + parameter.symbol = "ch_vcfkey"; + parameter.ranges.min = 0.0f; + parameter.ranges.max = 127.0f; + parameter.ranges.def = 71.0f; + parameter.midiCC = 24; + break; + + case paramAttack: + parameter.hints = kParameterIsAutomatable; + parameter.name = "Attack"; + parameter.symbol = "ch_attack"; + parameter.ranges.min = 0.0f; + parameter.ranges.max = 127.0f; + parameter.ranges.def = 27.0f; + parameter.midiCC = 73; + break; + + case paramDecay: + parameter.hints = kParameterIsAutomatable; + parameter.name = "Decay"; + parameter.symbol = "ch_decay"; + parameter.ranges.min = 0.0f; + parameter.ranges.max = 127.0f; + parameter.ranges.def = 57.0f; + parameter.midiCC = 75; + break; + + case paramSustain: + parameter.hints = kParameterIsAutomatable; + parameter.name = "Sustain"; + parameter.symbol = "ch_sustain"; + parameter.ranges.min = 0.0f; + parameter.ranges.max = 127.0f; + parameter.ranges.def = 57.0f; + parameter.midiCC = 27; + break; + + case paramRelease: + parameter.hints = kParameterIsAutomatable; + parameter.name = "Release"; + parameter.symbol = "ch_release"; + parameter.ranges.min = 0.0f; + parameter.ranges.max = 127.0f; + parameter.ranges.def = 48.0f; + parameter.midiCC = 72; + break; + + case paramEnvGate: + parameter.hints = kParameterIsAutomatable | kParameterIsInteger; // | kParameterIsBoolean; + parameter.name = "Env-Gate"; + parameter.symbol = "ch_envgate"; + parameter.ranges.min = 0.0f; + parameter.ranges.max = 1.0f; + parameter.ranges.def = 1.0f; + parameter.midiCC = 25; + parameter.enumValues.count = 2; + parameter.enumValues.restrictedMode = true; + { + ParameterEnumerationValue* const enumValues = new ParameterEnumerationValue[2]; + enumValues[0].value = 0.0f; + enumValues[0].label = "ENV"; + enumValues[1].value = 1.0f; + enumValues[1].label = "GATE"; + parameter.enumValues.values = enumValues; + } + break; + + case paramVCALevel: + parameter.hints = kParameterIsAutomatable; + parameter.name = "VCA Level"; + parameter.symbol = "ch_vcalevel"; + parameter.ranges.min = 0.0f; + parameter.ranges.max = 127.0f; + parameter.ranges.def = 40.0f; + parameter.midiCC = 26; + break; + + case paramModWheel: + parameter.hints = kParameterIsAutomatable | kParameterIsHidden; + parameter.name = "Mod wheel"; + parameter.symbol = "ch_modwheel"; + parameter.ranges.min = 0.0f; + parameter.ranges.max = 127.0f; + parameter.ranges.def = 0.0f; + parameter.midiCC = 1; + break; + } + // chorus, porta, bend range, key mode still to do +} + +void Peacock::setParameterValue(uint32_t index, float value) { + // should be trapped by host, but let's be safe + if (value < 0.0f) value = 0.0f; + if (value > 127.0f) value = 127.0f; + + switch (index) { + case paramLFORate: + m->patchRam.lfoRate = value; + break; + case paramLFODelay: + m->patchRam.lfoDelay = value; + break; + + case paramVCOLFO: + m->patchRam.vcoLfo = value; + break; + case paramPWMLFO: + m->patchRam.pwmLfo = value / 1.27; + break; + case paramSub: + m->patchRam.sub = value; + break; + case paramNoise: + m->patchRam.noise = value; + break; + + case paramVCFFreq: + m->patchRam.vcfFreq = value; + break; + case paramVCFReso: + m->patchRam.vcfReso = value; + break; + case paramVCFEnv: + m->patchRam.vcfEnv = value; + break; + case paramVCFLFO: + m->patchRam.vcfLfo = value; + break; + case paramVCFKey: + m->patchRam.vcfKey = value; + break; + + case paramVCALevel: + m->patchRam.vca = value; + break; + + case paramAttack: + m->patchRam.env_a = value; + break; + case paramDecay: + m->patchRam.env_d = value; + break; + case paramSustain: + m->patchRam.env_s = value; + break; + case paramRelease: + m->patchRam.env_r = value; + break; + + // switch 1 params + case paramVCORange: // bits 0-2 of switch 1 + // doesn't look great in Carla because of odd behaviour with small integer knobs + m->patchRam.switch1 &= 0xf8; + m->patchRam.switch1 |= (1 << (int)(value - 1)); + break; + case paramSqr: // bit 3 of switch 1 + m->patchRam.switch1 &= 0xf7; + m->patchRam.switch1 |= (value >= 0.5) << 3; + break; + + case paramSaw: // bit 4 of switch 1 + m->patchRam.switch1 &= 0xef; + m->patchRam.switch1 |= (value >= 0.5) << 4; + break; + + // missing chorus switch + + // switch 2 params + case paramPWMMode: // bit 0 of switch 2 + m->patchRam.switch2 &= 0xfe; + m->patchRam.switch2 |= (value >= 0.5); + break; + + case paramVCFMode: // bit 1 of switch 2 + m->patchRam.switch2 &= 0xfd; + m->patchRam.switch2 |= (value >= 0.5) << 1; + break; + case paramEnvGate: + m->patchRam.switch2 &= 0xfb; + m->patchRam.switch2 |= (value >= 0.5) << 2; + break; + + case paramHPF: // bits 3-4 of switch 2 + // doesn't look great in Carla because of odd behaviour with small integer knobs + printf("setPV %d %f\n", index, value); + if (value > 3) value = 3; + m->patchRam.switch2 &= 0xe7; + m->patchRam.switch2 |= (3-(int)value )<< 3; + break; + + case paramModWheel: + //s.ff64 = (int)value << 1; + break; + } +} + +float Peacock::getParameterValue(uint32_t index) const { + switch (index) { + case paramLFORate: + return m->patchRam.lfoRate; + break; + case paramLFODelay: + return m->patchRam.lfoDelay; + break; + case paramVCORange: + // FIXME this needs to be better generally + switch (m->patchRam.switch1 & 0x07) { + case 1: + return 0; + break; + case 4: + return 2; + break; + default: + return 1; + } + break; + case paramVCOLFO: + return m->patchRam.vcoLfo; + break; + case paramPWMLFO: + return m->patchRam.pwmLfo * 1.27f; + break; + + case paramPWMMode: + return (m->patchRam.switch2 & 0x01) != 0; + break; + case paramSaw: + return (m->patchRam.switch1 & 0x10) != 0; + break; + case paramSqr: + return (m->patchRam.switch1 & 0x08) != 0; + + case paramSub: + return m->patchRam.sub; + break; + case paramNoise: + return m->patchRam.noise; + break; + case paramHPF: + return 3-((m->patchRam.switch2 & 0x18) >> 3); + break; + case paramVCFFreq: + return m->patchRam.vcfFreq; + break; + case paramVCFReso: + return m->patchRam.vcfReso; + break; + case paramVCFEnv: + return m->patchRam.vcfEnv; + break; + case paramVCFLFO: + return m->patchRam.vcfLfo; + break; + case paramVCFKey: + return m->patchRam.vcfKey; + break; + case paramVCFMode: + return (m->patchRam.switch2 & 0x02) != 0; + break; + + case paramAttack: + return m->patchRam.env_a; + break; + case paramDecay: + return m->patchRam.env_d; + break; + case paramSustain: + return m->patchRam.env_s; + break; + case paramRelease: + return m->patchRam.env_r; + break; + + case paramEnvGate: + return (m->patchRam.switch2 & 0x04) != 0; + + case paramVCALevel: + return m->patchRam.vca; + break; + } + return 0; +} diff --git a/plugin/peacock.hpp b/plugin/peacock.hpp index c7e6458..d75fd88 100644 --- a/plugin/peacock.hpp +++ b/plugin/peacock.hpp @@ -29,6 +29,40 @@ class Peacock : public Plugin { Peacock(); ~Peacock(); + enum Parameters { + paramLFORate, + paramLFODelay, + + paramVCORange, + paramVCOLFO, + paramPWMLFO, + paramPWMMode, + paramSaw, + paramSqr, + paramSub, + paramNoise, + + paramHPF, + + paramVCFFreq, + paramVCFReso, + paramVCFMode, + paramVCFEnv, + paramVCFLFO, + paramVCFKey, + + paramEnvGate, + paramVCALevel, + + paramAttack, + paramDecay, + paramSustain, + paramRelease, + + paramModWheel, + + parameterCount + }; protected: const char* getLabel() const override { return "peacock-8"; } @@ -43,6 +77,11 @@ class Peacock : public Plugin { // Initialisation void initAudioPort(bool input, uint32_t index, AudioPort& port) override; + void initParameter(uint32_t index, Parameter ¶meter) override; + void setParameterValue(uint32_t index, float value) override; + float getParameterValue(uint32_t index) const override; + + void run(const float**, float** outputs, uint32_t frames, const MidiEvent* midiEvents, uint32_t midiEventCount) override; void runMidi(const MidiEvent* ev, uint32_t ev_count, uint32_t timeLimit); @@ -51,7 +90,6 @@ class Peacock : public Plugin { private: Assigner* ic1; Module* m; - uint32_t sampleRate; @@ -59,8 +97,6 @@ class Peacock : public Plugin { uint32_t lastEvent = 0; // event number of last MIDI event processed in a chunk uint32_t framesLeft = 0, blockLeft = 0; - - DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(Peacock); };