commit 18552e82e450ddd6699214de3468a8f7537c8927
parent 6f04039e200f0c6f752541bc6040e2f4900066e2
Author: Matt Demanett <matt@demanett.net>
Date: Tue, 30 Jun 2020 00:30:24 -0400
Crude proof-of-concepts for PEQ14 vocoder and resynthesizer expanders; also changed how expanders check they're next to the correct modules.
Diffstat:
29 files changed, 605 insertions(+), 63 deletions(-)
diff --git a/plugin.json b/plugin.json
@@ -197,10 +197,11 @@
{
"slug": "Bogaudio-PEQ14",
"name": "PEQ14",
- "description": "6-channel parametric equalizer / filter bank",
+ "description": "14-channel parametric equalizer / filter bank",
"manualUrl": "https://github.com/bogaudio/BogaudioModules/blob/master/README.md#peq14",
"tags": [
"Filter",
+ "Vocoder",
"Polyphonic"
]
},
@@ -216,6 +217,30 @@
]
},
{
+ "slug": "Bogaudio-PEQ14XV",
+ "name": "PEQ14XV",
+ "description": "PEQ14 vocoder expander",
+ "manualUrl": "https://github.com/bogaudio/BogaudioModules/blob/master/README.md#peq14xv",
+ "tags": [
+ "Filter",
+ "Vocoder",
+ "Expander",
+ "Polyphonic"
+ ]
+ },
+ {
+ "slug": "Bogaudio-PEQ14XR",
+ "name": "PEQ14XR",
+ "description": "PEQ14 resynthesizer expander",
+ "manualUrl": "https://github.com/bogaudio/BogaudioModules/blob/master/README.md#peq14xr",
+ "tags": [
+ "Filter",
+ "Vocoder",
+ "Expander",
+ "Polyphonic"
+ ]
+ },
+ {
"slug": "Bogaudio-DADSRH",
"name": "DADSR(H)",
"description": "Advanced envelope generator",
diff --git a/res-src/PEQ14XR-src.svg b/res-src/PEQ14XR-src.svg
@@ -0,0 +1,58 @@
+<svg
+ version="1.1"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ width="75"
+ height="380"
+ viewBox="0 0 75 380"
+>
+ <style>
+ text {
+ fill: #333;
+ font-family: 'Roboto', sans-serif;
+ font-weight: bold;
+ }
+ text.title {
+ font-family: 'Comfortaa', sans-serif;
+ font-weight: normal;
+ }
+ text.brand {
+ font-family: 'Audiowide', sans-serif;
+ font-weight: bold;
+ }
+ </style>
+
+ <defs>
+ <symbol id="input" viewBox="0 0 24px 24px">
+ <g transform="translate(12 12)">
+ <circle cx="0" cy="0" r="5" stroke-width="1" stroke="#0f0" fill="#0f0" />
+ <circle cx="0" cy="0" r="10.5" stroke-width="3" stroke="#0f0" fill="none" />
+ </g>
+ </symbol>
+
+ <symbol id="output" viewBox="0 0 24px 24px">
+ <g transform="translate(12 12)">
+ <circle cx="0" cy="0" r="5" stroke-width="1" stroke="#f00" fill="#f00" />
+ <circle cx="0" cy="0" r="10.5" stroke-width="3" stroke="#f00" fill="none" />
+ </g>
+ </symbol>
+ </defs>
+
+ <rect width="100%" height="100%" fill="#ddd" />
+ <polyline points="1,1 74,1 74,379 1,379 1,1" stroke="#e4e4e4" stroke-width="0.5" fill="none" />
+ <polyline points="0.5,0.5 74.5,0.5 74.5,379.5 0.5,379.5 0.5,0.5" stroke="#ebebeb" stroke-width="0.8" fill="none" />
+ <polyline points="0,0 75,0 75,380 0,380 0,0" stroke="#f2f2f2" stroke-width="1" fill="none" />
+
+ <text class="title" x="20" y="14" font-size="7pt" letter-spacing="1px">PEQ14XR</text>
+ <!-- <text class="title" x="15" y="17" font-size="9pt" letter-spacing="3px" transform="translate(75 -8) rotate(90)">PEQ14-XO</text> -->
+ <g transform="translate(25 374)">
+ <text class="brand" font-size="6.5pt" letter-spacing="2px">BGA</text>
+ <rect width="2" height="2" fill="#ddd" transform="translate(11.5 -4)" />
+ </g>
+
+ <g transform="translate(20.5 318)">
+ <rect width="34" height="42" rx="5" fill="#bbb" />
+ <use id="OUT_OUTPUT" xlink:href="#output" transform="translate(5 4)" />
+ <text font-size="5pt" letter-spacing="2px" transform="translate(8.5 37)">OUT</text>
+ </g>
+</svg>
diff --git a/res-src/PEQ14XV-src.svg b/res-src/PEQ14XV-src.svg
@@ -0,0 +1,62 @@
+<svg
+ version="1.1"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ width="75"
+ height="380"
+ viewBox="0 0 75 380"
+>
+ <style>
+ text {
+ fill: #333;
+ font-family: 'Roboto', sans-serif;
+ font-weight: bold;
+ }
+ text.title {
+ font-family: 'Comfortaa', sans-serif;
+ font-weight: normal;
+ }
+ text.brand {
+ font-family: 'Audiowide', sans-serif;
+ font-weight: bold;
+ }
+ </style>
+
+ <defs>
+ <symbol id="input" viewBox="0 0 24px 24px">
+ <g transform="translate(12 12)">
+ <circle cx="0" cy="0" r="5" stroke-width="1" stroke="#0f0" fill="#0f0" />
+ <circle cx="0" cy="0" r="10.5" stroke-width="3" stroke="#0f0" fill="none" />
+ </g>
+ </symbol>
+
+ <symbol id="output" viewBox="0 0 24px 24px">
+ <g transform="translate(12 12)">
+ <circle cx="0" cy="0" r="5" stroke-width="1" stroke="#f00" fill="#f00" />
+ <circle cx="0" cy="0" r="10.5" stroke-width="3" stroke="#f00" fill="none" />
+ </g>
+ </symbol>
+ </defs>
+
+ <rect width="100%" height="100%" fill="#ddd" />
+ <polyline points="1,1 74,1 74,379 1,379 1,1" stroke="#e4e4e4" stroke-width="0.5" fill="none" />
+ <polyline points="0.5,0.5 74.5,0.5 74.5,379.5 0.5,379.5 0.5,0.5" stroke="#ebebeb" stroke-width="0.8" fill="none" />
+ <polyline points="0,0 75,0 75,380 0,380 0,0" stroke="#f2f2f2" stroke-width="1" fill="none" />
+
+ <text class="title" x="20" y="14" font-size="7pt" letter-spacing="1px">PEQ14XV</text>
+ <!-- <text class="title" x="15" y="17" font-size="9pt" letter-spacing="3px" transform="translate(75 -8) rotate(90)">PEQ14-XO</text> -->
+ <g transform="translate(25 374)">
+ <text class="brand" font-size="6.5pt" letter-spacing="2px">BGA</text>
+ <rect width="2" height="2" fill="#ddd" transform="translate(11.5 -4)" />
+ </g>
+
+ <g transform="translate(5.5 318)">
+ <rect width="64" height="40" rx="5" fill="#fafafa" />
+ <rect width="32" height="40" rx="5" fill="#bbb" transform="translate(32)" />
+ <rect width="10" height="40" fill="#bbb" transform="translate(32)" />
+ <use id="IN_INPUT" xlink:href="#input" transform="translate(3.5 4)" />
+ <use id="OUT_OUTPUT" xlink:href="#output" transform="translate(35.5 4)" />
+ <text font-size="6pt" letter-spacing="1px" transform="translate(11 36)">IN</text>
+ <text font-size="6pt" letter-spacing="2px" transform="translate(37 36)">OUT</text>
+ </g>
+</svg>
diff --git a/res/PEQ14XR.svg b/res/PEQ14XR.svg
Binary files differ.
diff --git a/res/PEQ14XV.svg b/res/PEQ14XV.svg
Binary files differ.
diff --git a/src/Mix4.hpp b/src/Mix4.hpp
@@ -86,7 +86,7 @@ struct Mix4 : ExpandableModule<Mix4ExpanderMessage, BGModule> {
sampleRateChange();
_rms.setSensitivity(0.05f);
- setExpanderModel(modelMix4x);
+ setExpanderModelPredicate([](Model* m) { return m == modelMix4x; });
}
virtual ~Mix4() {
for (int i = 0; i < 4; ++i) {
diff --git a/src/Mix4x.hpp b/src/Mix4x.hpp
@@ -108,7 +108,7 @@ struct Mix4x : ExpanderModule<Mix4ExpanderMessage, BGModule> {
_channels[2] = new MixerExpanderChannel(params[LOW3_PARAM], params[MID3_PARAM], params[HIGH3_PARAM], params[A3_PARAM], params[B3_PARAM], params[PRE_A3_PARAM], params[PRE_B3_PARAM], inputs[A3_INPUT], inputs[B3_INPUT]);
_channels[3] = new MixerExpanderChannel(params[LOW4_PARAM], params[MID4_PARAM], params[HIGH4_PARAM], params[A4_PARAM], params[B4_PARAM], params[PRE_A4_PARAM], params[PRE_B4_PARAM], inputs[A4_INPUT], inputs[B4_INPUT]);
- setBaseModel(modelMix4);
+ setBaseModelPredicate([](Model* m) { return m == modelMix4; });
}
virtual ~Mix4x() {
for (int i = 0; i < 4; ++i) {
diff --git a/src/Mix8.hpp b/src/Mix8.hpp
@@ -126,7 +126,7 @@ struct Mix8 : ExpandableModule<Mix8ExpanderMessage, BGModule> {
sampleRateChange();
_rms.setSensitivity(0.05f);
- setExpanderModel(modelMix8x);
+ setExpanderModelPredicate([](Model* m) { return m == modelMix8x; });
}
virtual ~Mix8() {
for (int i = 0; i < 8; ++i) {
diff --git a/src/Mix8x.hpp b/src/Mix8x.hpp
@@ -175,7 +175,7 @@ struct Mix8x : ExpanderModule<Mix8ExpanderMessage, BGModule> {
_channels[6] = new MixerExpanderChannel(params[LOW7_PARAM], params[MID7_PARAM], params[HIGH7_PARAM], params[A7_PARAM], params[B7_PARAM], params[PRE_A7_PARAM], params[PRE_B7_PARAM], inputs[A7_INPUT], inputs[B7_INPUT]);
_channels[7] = new MixerExpanderChannel(params[LOW8_PARAM], params[MID8_PARAM], params[HIGH8_PARAM], params[A8_PARAM], params[B8_PARAM], params[PRE_A8_PARAM], params[PRE_B8_PARAM], inputs[A8_INPUT], inputs[B8_INPUT]);
- setBaseModel(modelMix8);
+ setBaseModelPredicate([](Model* m) { return m == modelMix8; });
}
virtual ~Mix8x() {
for (int i = 0; i < 8; ++i) {
diff --git a/src/PEQ.cpp b/src/PEQ.cpp
@@ -59,8 +59,7 @@ void PEQ::processAlways(const ProcessArgs& args) {
}
void PEQ::processChannel(const ProcessArgs& args, int c) {
- float outs[6];
- outputs[OUT_OUTPUT].setVoltage(_engines[c]->next(inputs[IN_INPUT].getVoltage(c), outs, _rmsSums), c);
+ outputs[OUT_OUTPUT].setVoltage(_engines[c]->next(inputs[IN_INPUT].getVoltage(c), _rmsSums), c);
}
void PEQ::postProcessAlways(const ProcessArgs& args) {
diff --git a/src/PEQ14.cpp b/src/PEQ14.cpp
@@ -87,22 +87,41 @@ void PEQ14::processAlways(const ProcessArgs& args) {
}
void PEQ14::processChannel(const ProcessArgs& args, int c) {
- float outs[14] {};
- float out = _engines[c]->next(inputs[IN_INPUT].getVoltage(c), outs, _rmsSums);
+ PEQEngine& e = *_engines[c];
+ float out = e.next(inputs[IN_INPUT].getVoltage(c), _rmsSums);
outputs[OUT_OUTPUT].setVoltage(out, c);
- if (expanderConnected()) {
- std::copy(outs, outs + 14, toExpander()->outs[c]);
- }
+ float levels[14];
float oddOut = 0.0f;
float evenOut = 0.0f;
for (int i = 0; i < 14; ++i) {
- oddOut += outs[i] * (float)(i % 2 == 0 || (i == 13 && _highMode == MultimodeFilter::HIGHPASS_MODE));
- evenOut += outs[i] * (float)(i % 2 == 1 || (i == 0 && _lowMode == MultimodeFilter::LOWPASS_MODE));
- outputs[EF1_OUTPUT + i].setVoltage(2.0f * _efs[c][i].next(outs[i]), c);
+ oddOut += e.outs[i] * (float)(i % 2 == 0 || (i == 13 && _highMode == MultimodeFilter::HIGHPASS_MODE));
+ evenOut += e.outs[i] * (float)(i % 2 == 1 || (i == 0 && _lowMode == MultimodeFilter::LOWPASS_MODE));
+ levels[i] = scaleEF(_efs[c][i].next(e.outs[i]), e.frequencies[i]);
+ outputs[EF1_OUTPUT + i].setVoltage(levels[i], c);
}
outputs[ODDS_OUTPUT].setVoltage(oddOut, c);
outputs[EVENS_OUTPUT].setVoltage(evenOut, c);
+
+ if (expanderConnected()) {
+ auto m = toExpander();
+ m->valid = true;
+ std::copy(e.outs, e.outs + 14, m->outs[c]);
+ std::copy(e.frequencies, e.frequencies + 14, m->frequencies[c]);
+ std::copy(levels, levels + 14, m->levels[c]);
+ m->bandwidths[c] = e.bandwidth;
+ m->lowLP = _lowMode == MultimodeFilter::LOWPASS_MODE;
+ m->highHP = _highMode == MultimodeFilter::HIGHPASS_MODE;
+ }
+}
+
+float PEQ14::scaleEF(float ef, float frequency) {
+ float bandwidth = 2.0 * params[BANDWIDTH_PARAM].getValue();
+ float minf = std::max(0.0f, powf(2.0f, -bandwidth) * frequency);
+ float maxf = std::min(PEQChannel::maxFrequency, powf(2.0f, bandwidth) * frequency);
+ float scale = (maxf - minf) / PEQChannel::maxFrequency;
+ scale = 1.0f / scale;
+ return scale * ef;
}
void PEQ14::postProcessAlways(const ProcessArgs& args) {
@@ -351,4 +370,4 @@ struct PEQ14Widget : ModuleWidget {
}
};
-Model* modelPEQ14 = createModel<PEQ14, PEQ14Widget>("Bogaudio-PEQ14", "PEQ14", "6-channel parametric equalizer / filter bank", "Filter", "Polyphonic");
+Model* modelPEQ14 = createModel<PEQ14, PEQ14Widget>("Bogaudio-PEQ14", "PEQ14", "14-channel parametric equalizer / filter bank", "Filter", "Vocoder", "Polyphonic");
diff --git a/src/PEQ14.hpp b/src/PEQ14.hpp
@@ -181,7 +181,7 @@ struct PEQ14 : ExpandableModule<PEQ14ExpanderMessage, BGModule> {
configParam<ScaledSquaringParamQuantity<(int)PEQChannel::maxFrequency>>(FREQUENCY14_PARAM, 0.0f, 1.0f, 0.5873670f, "Channel 6 frequency", " HZ");
configParam(FREQUENCY_CV14_PARAM, -1.0f, 1.0f, 1.0f, "Channel 6 frequency CV attenuation", "%", 0.0f, 100.0f);
- setExpanderModel(modelPEQ14XO);
+ setExpanderModelPredicate([](Model* m) { return m == modelPEQ14XO || m == modelPEQ14XR || m == modelPEQ14XV; });
}
void sampleRateChange() override;
@@ -192,6 +192,7 @@ struct PEQ14 : ExpandableModule<PEQ14ExpanderMessage, BGModule> {
void modulate() override;
void processAlways(const ProcessArgs& args) override;
void processChannel(const ProcessArgs& args, int c) override;
+ float scaleEF(float ef, float frequency);
void postProcessAlways(const ProcessArgs& args) override;
};
diff --git a/src/PEQ14XO.cpp b/src/PEQ14XO.cpp
@@ -5,6 +5,16 @@ void PEQ14XO::processAll(const ProcessArgs& args) {
for (int i = 0; i < 14; ++i) {
outputs[BAND1_OUTPUT + i].setChannels(_channels);
}
+
+ if (expanderConnected()) {
+ if (baseConnected()) {
+ // *toExpander() = *fromBase();
+ fromBase()->copyTo(toExpander());
+ }
+ else {
+ toExpander()->reset();
+ }
+ }
}
void PEQ14XO::processChannel(const ProcessArgs& args, int c) {
diff --git a/src/PEQ14XO.hpp b/src/PEQ14XO.hpp
@@ -4,7 +4,7 @@
namespace bogaudio {
-struct PEQ14XO : ExpanderModule<PEQ14ExpanderMessage, BGModule> {
+struct PEQ14XO : ExpanderModule<PEQ14ExpanderMessage, ExpandableModule<PEQ14ExpanderMessage, BGModule>> {
enum ParamsIds {
NUM_PARAMS
};
@@ -33,7 +33,8 @@ struct PEQ14XO : ExpanderModule<PEQ14ExpanderMessage, BGModule> {
PEQ14XO() {
config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS);
- setBaseModel(modelPEQ14);
+ setBaseModelPredicate([](Model* m) { return m == modelPEQ14 || m == modelPEQ14XO || m == modelPEQ14XR || m == modelPEQ14XV; });
+ setExpanderModelPredicate([](Model* m) { return m == modelPEQ14XO || m == modelPEQ14XR || m == modelPEQ14XV; });
}
void processAll(const ProcessArgs& args) override;
diff --git a/src/PEQ14XR.cpp b/src/PEQ14XR.cpp
@@ -0,0 +1,94 @@
+
+#include "PEQ14XR.hpp"
+
+void PEQ14XR::Engine::setSampleRate(float sr) {
+ for (int i = 0; i < 14; ++i) {
+ oscillators[i].setSampleRate(sr);
+ }
+}
+
+void PEQ14XR::sampleRateChange() {
+ float sr = APP->engine->getSampleRate();
+ for (int c = 0; c < _channels; ++c) {
+ _engines[c]->setSampleRate(sr);
+ }
+}
+
+void PEQ14XR::addChannel(int c) {
+ _engines[c] = new Engine();
+ _engines[c]->setSampleRate(APP->engine->getSampleRate());
+}
+
+void PEQ14XR::removeChannel(int c) {
+ delete _engines[c];
+ _engines[c] = NULL;
+}
+
+void PEQ14XR::processAlways(const ProcessArgs& args) {
+ outputs[OUT_OUTPUT].setChannels(_channels);
+
+ _baseMessage = NULL;
+ if (baseConnected()) {
+ _baseMessage = fromBase();
+ }
+
+ if (expanderConnected()) {
+ if (_baseMessage) {
+ // *toExpander() = *_baseMessage;
+ _baseMessage->copyTo(toExpander());
+ }
+ else {
+ toExpander()->reset();
+ }
+ }
+}
+
+void PEQ14XR::processChannel(const ProcessArgs& args, int c) {
+ if (_baseMessage && _baseMessage->valid) {
+ Engine& e = *_engines[c];
+ float out = 0.0f;
+ for (int i = 0; i < 14; ++i) {
+ e.oscillators[i].setFrequency(_baseMessage->frequencies[c][i]);
+
+ float db = _baseMessage->levels[c][i];
+ db *= 0.2f;
+ db = std::max(0.0f, std::min(1.0f, db));
+ db = 1.0f - db;
+ db *= Amplifier::minDecibels;
+ e.amplifiers[i].setLevel(db);
+
+ out += e.amplifiers[i].next(e.oscillators[i].next());
+ }
+ outputs[OUT_OUTPUT].setVoltage(_saturator.next(out), c);
+ }
+ else {
+ outputs[OUT_OUTPUT].setVoltage(0.0f, c);
+ }
+}
+
+struct PEQ14XRWidget : ModuleWidget {
+ static constexpr int hp = 5;
+
+ PEQ14XRWidget(PEQ14XR* module) {
+ setModule(module);
+ box.size = Vec(RACK_GRID_WIDTH * hp, RACK_GRID_HEIGHT);
+
+ {
+ SvgPanel *panel = new SvgPanel();
+ panel->box.size = box.size;
+ panel->setBackground(APP->window->loadSvg(asset::plugin(pluginInstance, "res/PEQ14XR.svg")));
+ addChild(panel);
+ }
+
+ addChild(createWidget<ScrewSilver>(Vec(0, 0)));
+ addChild(createWidget<ScrewSilver>(Vec(box.size.x - 15, 365)));
+
+ // generated by svg_widgets.rb
+ auto outOutputPosition = Vec(25.5, 322.0);
+ // end generated by svg_widgets.rb
+
+ addOutput(createOutput<Port24>(outOutputPosition, module, PEQ14XR::OUT_OUTPUT));
+ }
+};
+
+Model* modelPEQ14XR = createModel<PEQ14XR, PEQ14XRWidget>("Bogaudio-PEQ14XR", "PEQ14XR", "PEQ14 resynthesizer expander", "Filter", "Vocoder", "Expander", "Polyphonic");
diff --git a/src/PEQ14XR.hpp b/src/PEQ14XR.hpp
@@ -0,0 +1,61 @@
+#pragma once
+
+#include "PEQ14_shared.hpp"
+#include "dsp/oscillator.hpp"
+
+namespace bogaudio {
+
+struct PEQ14XR : ExpanderModule<PEQ14ExpanderMessage, ExpandableModule<PEQ14ExpanderMessage, BGModule>> {
+ enum ParamsIds {
+ NUM_PARAMS
+ };
+
+ enum InputsIds {
+ NUM_INPUTS
+ };
+
+ enum OutputsIds {
+ OUT_OUTPUT,
+ NUM_OUTPUTS
+ };
+
+ struct Engine {
+ struct BandOscillator {
+ Phasor _phasor;
+ TriangleOscillator _oscillator;
+ // SineTableOscillator _oscillator;
+ // BandLimitedSawOscillator _oscillator;
+
+ inline void setSampleRate(float sr) { _phasor.setSampleRate(sr); }
+ inline void setFrequency(float f) { _phasor.setFrequency(f); }
+ inline float next() {
+ _phasor.advancePhase();
+ return _oscillator.nextFromPhasor(_phasor);
+ }
+ };
+
+ BandOscillator oscillators[14];
+ Amplifier amplifiers[14];
+
+ void setSampleRate(float sr);
+ };
+
+ Engine* _engines[maxChannels] {};
+ Saturator _saturator;
+ PEQ14ExpanderMessage* _baseMessage = NULL;
+
+ PEQ14XR() {
+ config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS);
+
+ setBaseModelPredicate([](Model* m) { return m == modelPEQ14 || m == modelPEQ14XO || m == modelPEQ14XR || m == modelPEQ14XV; });
+ setExpanderModelPredicate([](Model* m) { return m == modelPEQ14XO || m == modelPEQ14XR || m == modelPEQ14XV; });
+ }
+
+ void sampleRateChange() override;
+ void addChannel(int c) override;
+ void removeChannel(int c) override;
+ void processAlways(const ProcessArgs& args) override;
+ void processChannel(const ProcessArgs& args, int c) override;
+};
+
+} // namespace bogaudio
diff --git a/src/PEQ14XV.cpp b/src/PEQ14XV.cpp
@@ -0,0 +1,124 @@
+
+#include "PEQ14XV.hpp"
+
+PEQ14XV::Engine::Engine() {
+ filters[0] = new MultimodeFilter8();
+ for (int i = 1; i < 13; ++i) {
+ filters[i] = new MultimodeFilter4();
+ }
+ filters[13] = new MultimodeFilter8();
+}
+
+PEQ14XV::Engine::~Engine() {
+ for (int i = 0; i < 14; ++i) {
+ delete filters[i];
+ }
+}
+
+void PEQ14XV::sampleRateChange() {
+ _sampleRate = APP->engine->getSampleRate();
+}
+
+void PEQ14XV::addChannel(int c) {
+ _engines[c] = new Engine();
+}
+
+void PEQ14XV::removeChannel(int c) {
+ delete _engines[c];
+ _engines[c] = NULL;
+}
+
+void PEQ14XV::processAlways(const ProcessArgs& args) {
+ outputs[OUT_OUTPUT].setChannels(_channels);
+
+ _baseMessage = NULL;
+ if (baseConnected()) {
+ _baseMessage = fromBase();
+ }
+
+ if (expanderConnected()) {
+ if (_baseMessage) {
+ // *toExpander() = *_baseMessage;
+ _baseMessage->copyTo(toExpander());
+ }
+ else {
+ toExpander()->reset();
+ }
+ }
+}
+
+void PEQ14XV::processChannel(const ProcessArgs& args, int c) {
+ if (_baseMessage && _baseMessage->valid) {
+ Engine& e = *_engines[c];
+ float in = inputs[IN_INPUT].getPolyVoltage(c);
+ float out = 0.0f;
+ for (int i = 0; i < 14; ++i) {
+ auto mode = MultimodeFilter::BANDPASS_MODE;
+ int poles = 4;
+ float bandwidth = _baseMessage->bandwidths[c];
+ if (i == 0 && _baseMessage->lowLP) {
+ mode = MultimodeFilter::LOWPASS_MODE;
+ poles = 12;
+ bandwidth = MultimodeFilter::minQbw;
+ }
+ if (i == 13 && _baseMessage->highHP) {
+ mode = MultimodeFilter::HIGHPASS_MODE;
+ poles = 12;
+ bandwidth = MultimodeFilter::minQbw;
+ }
+ e.filters[i]->setParams(
+ _sampleRate,
+ MultimodeFilter::BUTTERWORTH_TYPE,
+ poles,
+ mode,
+ _baseMessage->frequencies[c][i],
+ bandwidth,
+ MultimodeFilter::PITCH_BANDWIDTH_MODE
+ );
+
+ float db = _baseMessage->levels[c][i];
+ db *= 0.2f;
+ db = std::max(0.0f, std::min(1.0f, db));
+ db = 1.0f - db;
+ db *= Amplifier::minDecibels;
+ e.amplifiers[i].setLevel(db);
+
+ out += e.amplifiers[i].next(e.filters[i]->next(in));
+ }
+ outputs[OUT_OUTPUT].setVoltage(_saturator.next(out), c);
+ }
+ else {
+ outputs[OUT_OUTPUT].setVoltage(0.0f, c);
+ }
+}
+
+struct PEQ14XVWidget : ModuleWidget {
+ static constexpr int hp = 5;
+
+ PEQ14XVWidget(PEQ14XV* module) {
+ setModule(module);
+ box.size = Vec(RACK_GRID_WIDTH * hp, RACK_GRID_HEIGHT);
+
+ {
+ SvgPanel *panel = new SvgPanel();
+ panel->box.size = box.size;
+ panel->setBackground(APP->window->loadSvg(asset::plugin(pluginInstance, "res/PEQ14XV.svg")));
+ addChild(panel);
+ }
+
+ addChild(createWidget<ScrewSilver>(Vec(0, 0)));
+ addChild(createWidget<ScrewSilver>(Vec(box.size.x - 15, 365)));
+
+ // generated by svg_widgets.rb
+ auto inInputPosition = Vec(9.0, 322.0);
+
+ auto outOutputPosition = Vec(41.0, 322.0);
+ // end generated by svg_widgets.rb
+
+ addInput(createInput<Port24>(inInputPosition, module, PEQ14XV::IN_INPUT));
+
+ addOutput(createOutput<Port24>(outOutputPosition, module, PEQ14XV::OUT_OUTPUT));
+ }
+};
+
+Model* modelPEQ14XV = createModel<PEQ14XV, PEQ14XVWidget>("Bogaudio-PEQ14XV", "PEQ14XV", "PEQ14 vocoder expander", "Filter", "Vocoder", "Expander", "Polyphonic");
diff --git a/src/PEQ14XV.hpp b/src/PEQ14XV.hpp
@@ -0,0 +1,50 @@
+#pragma once
+
+#include "PEQ14_shared.hpp"
+#include "dsp/filters/multimode.hpp"
+
+namespace bogaudio {
+
+struct PEQ14XV : ExpanderModule<PEQ14ExpanderMessage, ExpandableModule<PEQ14ExpanderMessage, BGModule>> {
+ enum ParamsIds {
+ NUM_PARAMS
+ };
+
+ enum InputsIds {
+ IN_INPUT,
+ NUM_INPUTS
+ };
+
+ enum OutputsIds {
+ OUT_OUTPUT,
+ NUM_OUTPUTS
+ };
+
+ struct Engine {
+ MultimodeFilter* filters[14] {};
+ Amplifier amplifiers[14];
+
+ Engine();
+ ~Engine();
+ };
+
+ float _sampleRate = 1000.0f;
+ Engine* _engines[maxChannels] {};
+ Saturator _saturator;
+ PEQ14ExpanderMessage* _baseMessage = NULL;
+
+ PEQ14XV() {
+ config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS);
+
+ setBaseModelPredicate([](Model* m) { return m == modelPEQ14 || m == modelPEQ14XO || m == modelPEQ14XR || m == modelPEQ14XV; });
+ setExpanderModelPredicate([](Model* m) { return m == modelPEQ14XO || m == modelPEQ14XR || m == modelPEQ14XV; });
+ }
+
+ void sampleRateChange() override;
+ void addChannel(int c) override;
+ void removeChannel(int c) override;
+ void processAlways(const ProcessArgs& args) override;
+ void processChannel(const ProcessArgs& args, int c) override;
+};
+
+} // namespace bogaudio
diff --git a/src/PEQ14_shared.hpp b/src/PEQ14_shared.hpp
@@ -6,16 +6,44 @@
extern Model* modelPEQ14;
extern Model* modelPEQ14XO;
+extern Model* modelPEQ14XR;
+extern Model* modelPEQ14XV;
namespace bogaudio {
struct PEQ14ExpanderMessage : ExpanderMessage {
+ bool valid = false;
float outs[BGModule::maxChannels][14];
+ float frequencies[BGModule::maxChannels][14];
+ float levels[BGModule::maxChannels][14];
+ float bandwidths[BGModule::maxChannels];
+ bool lowLP = false;
+ bool highHP = false;
PEQ14ExpanderMessage() {
- for (int c = 0; c < BGModule::maxChannels; ++c) {
- std::fill(outs[c], outs[c] + 14, 0.0f);
- }
+ reset();
+ }
+
+ void reset() {
+ valid = false;
+ const int n = BGModule::maxChannels * 14;
+ std::fill((float*)outs, (float*)outs + n, 0.0f);
+ std::fill((float*)frequencies, (float*)frequencies + n, 0.0f);
+ std::fill((float*)levels, (float*)levels + n, 0.0f);
+ std::fill((float*)bandwidths, (float*)bandwidths + BGModule::maxChannels, 0.0f);
+ lowLP = false;
+ highHP = false;
+ }
+
+ void copyTo(PEQ14ExpanderMessage* o) {
+ o->valid = valid;
+ const int n = BGModule::maxChannels * 14;
+ std::copy((float*)outs, (float*)outs + n, (float*)o->outs);
+ std::copy((float*)frequencies, (float*)frequencies + n, (float*)o->frequencies);
+ std::copy((float*)levels, (float*)levels + n, (float*)o->levels);
+ std::copy((float*)bandwidths, (float*)bandwidths + BGModule::maxChannels, (float*)o->bandwidths);
+ o->lowLP = lowLP;
+ o->highHP = highHP;
}
};
diff --git a/src/PEQ6.cpp b/src/PEQ6.cpp
@@ -81,15 +81,15 @@ void PEQ6::processAlways(const ProcessArgs& args) {
}
void PEQ6::processChannel(const ProcessArgs& args, int c) {
- float outs[6] {};
- float out = _engines[c]->next(inputs[IN_INPUT].getVoltage(c), outs, _rmsSums);
+ PEQEngine& e = *_engines[c];
+ float out = e.next(inputs[IN_INPUT].getVoltage(c), _rmsSums);
outputs[OUT_OUTPUT].setVoltage(out, c);
if (expanderConnected()) {
- std::copy(outs, outs + 6, toExpander()->outs[c]);
+ std::copy(e.outs, e.outs + 6, toExpander()->outs[c]);
}
for (int i = 0; i < 6; ++i) {
- outputs[EF1_OUTPUT + i].setVoltage(2.0f * _efs[c][i].next(outs[i]), c);
+ outputs[EF1_OUTPUT + i].setVoltage(2.0f * _efs[c][i].next(e.outs[i]), c);
}
}
diff --git a/src/PEQ6.hpp b/src/PEQ6.hpp
@@ -104,7 +104,7 @@ struct PEQ6 : ExpandableModule<PEQ6ExpanderMessage, BGModule> {
configParam<ScaledSquaringParamQuantity<(int)PEQChannel::maxFrequency>>(FREQUENCY6_PARAM, 0.0f, 1.0f, 0.3535534f, "Channel 6 frequency", " HZ");
configParam(FREQUENCY_CV6_PARAM, -1.0f, 1.0f, 1.0f, "Channel 6 frequency CV attenuation", "%", 0.0f, 100.0f);
- setExpanderModel(modelPEQ6XO);
+ setExpanderModelPredicate([](Model* m) { return m == modelPEQ6XO; });
}
void sampleRateChange() override;
diff --git a/src/PEQ6XO.hpp b/src/PEQ6XO.hpp
@@ -25,7 +25,7 @@ struct PEQ6XO : ExpanderModule<PEQ6ExpanderMessage, BGModule> {
PEQ6XO() {
config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS);
- setBaseModel(modelPEQ6);
+ setBaseModelPredicate([](Model* m) { return m == modelPEQ6; });
}
void processAll(const ProcessArgs& args) override;
diff --git a/src/Pgmr.hpp b/src/Pgmr.hpp
@@ -101,7 +101,7 @@ struct Pgmr : ExpandableModule<PgmrExpanderMessage, OutputRangeAddressableSequen
_localSteps[2] = new PgmrStep(params[CVA3_PARAM], params[CVB3_PARAM], params[CVC3_PARAM], params[CVD3_PARAM], lights[SELECT3_LIGHT], params[SELECT3_PARAM], inputs[SELECT3_INPUT], outputs[SELECT3_OUTPUT]);
_localSteps[3] = new PgmrStep(params[CVA4_PARAM], params[CVB4_PARAM], params[CVC4_PARAM], params[CVD4_PARAM], lights[SELECT4_LIGHT], params[SELECT4_PARAM], inputs[SELECT4_INPUT], outputs[SELECT4_OUTPUT]);
- setExpanderModel(modelPgmrX);
+ setExpanderModelPredicate([](Model* m) { return m == modelPgmrX; });
_id = PgmrRegistry::registry().registerBase(*this);
}
virtual ~Pgmr() {
diff --git a/src/PgmrX.hpp b/src/PgmrX.hpp
@@ -85,9 +85,8 @@ struct PgmrX : ExpanderModule<PgmrExpanderMessage, ExpandableModule<PgmrExpander
_localSteps[2] = new PgmrStep(params[CVA3_PARAM], params[CVB3_PARAM], params[CVC3_PARAM], params[CVD3_PARAM], lights[SELECT3_LIGHT], params[SELECT3_PARAM], inputs[SELECT3_INPUT], outputs[SELECT3_OUTPUT]);
_localSteps[3] = new PgmrStep(params[CVA4_PARAM], params[CVB4_PARAM], params[CVC4_PARAM], params[CVD4_PARAM], lights[SELECT4_LIGHT], params[SELECT4_PARAM], inputs[SELECT4_INPUT], outputs[SELECT4_OUTPUT]);
- setBaseModel(modelPgmr);
- setChainableModel(modelPgmrX);
- setExpanderModel(modelPgmrX);
+ setBaseModelPredicate([](Model* m) { return m == modelPgmr || m == modelPgmrX; });
+ setExpanderModelPredicate([](Model* m) { return m == modelPgmrX; });
}
virtual ~PgmrX() {
PgmrRegistry::registry().deregisterExpander(_baseID, _position);
diff --git a/src/TestExpander.hpp b/src/TestExpander.hpp
@@ -36,7 +36,7 @@ struct TestExpanderBase : ExpandableModule<TestExpanderMessage, BGModule> {
TestExpanderBase() {
config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
- setExpanderModel(modelTestExpanderExtension);
+ setExpanderModelPredicate([](Model* m) { return m == modelTestExpanderExtension; });
}
int channels() override;
@@ -65,7 +65,7 @@ struct TestExpanderExtension : ExpanderModule<TestExpanderMessage, BGModule> {
TestExpanderExtension() {
config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
- setBaseModel(modelTestExpanderBase);
+ setBaseModelPredicate([](Model* m) { return m == modelTestExpanderBase; });
}
void processAll(const ProcessArgs& args) override;
diff --git a/src/bogaudio.cpp b/src/bogaudio.cpp
@@ -61,6 +61,8 @@
#include "PEQ6XO.hpp"
#include "PEQ14.hpp"
#include "PEQ14XO.hpp"
+#include "PEQ14XR.hpp"
+#include "PEQ14XV.hpp"
#include "Pgmr.hpp"
#include "PgmrX.hpp"
#include "PolyCon8.hpp"
@@ -130,6 +132,8 @@ void init(rack::Plugin *p) {
p->addModel(modelPEQ6XO);
p->addModel(modelPEQ14);
p->addModel(modelPEQ14XO);
+ p->addModel(modelPEQ14XV);
+ p->addModel(modelPEQ14XR);
p->addModel(modelDADSRH);
p->addModel(modelDADSRHPlus);
diff --git a/src/expanders.hpp b/src/expanders.hpp
@@ -17,7 +17,7 @@ struct ExpanderMessage {
template<class MSG, class BASE>
struct ExpandableModule : BASE {
- Model* _expanderModel = NULL;
+ std::function<bool(Model*)> _expanderModel;
MSG _messages[2] {};
bool _wasConnected = false;
@@ -29,12 +29,12 @@ struct ExpandableModule : BASE {
BGModule::rightExpander.consumerMessage = &_messages[1];
}
- void setExpanderModel(Model* m) {
- _expanderModel = m;
+ void setExpanderModelPredicate(std::function<bool(Model*)> p) {
+ _expanderModel = p;
}
bool expanderConnected() {
- bool connected = BGModule::rightExpander.module && _expanderModel && BGModule::rightExpander.module->model == _expanderModel;
+ bool connected = BGModule::rightExpander.module && _expanderModel && _expanderModel(BGModule::rightExpander.module->model);
if (!connected && _wasConnected) {
_messages[1] = _messages[0] = MSG();
}
@@ -60,8 +60,7 @@ struct ExpandableModule : BASE {
// An expander must be to the right of the expanded module to work.
template<class MSG, class BASE>
struct ExpanderModule : BASE {
- Model* _baseModel = NULL;
- Model* _chainableModel = NULL;
+ std::function<bool(Model*)> _baseModel;
MSG _messages[2] {};
bool _wasConnected = false;
@@ -73,16 +72,12 @@ struct ExpanderModule : BASE {
BGModule::leftExpander.consumerMessage = &_messages[1];
}
- void setBaseModel(Model* m) {
- _baseModel = m;
- }
-
- void setChainableModel(Model* m) {
- _chainableModel = m;
+ void setBaseModelPredicate(std::function<bool(Model*)> p) {
+ _baseModel = p;
}
bool baseConnected() {
- bool connected = BGModule::leftExpander.module && ((_baseModel && BGModule::leftExpander.module->model == _baseModel) || (_chainableModel && BGModule::leftExpander.module->model == _chainableModel));
+ bool connected = BGModule::leftExpander.module && _baseModel && _baseModel(BGModule::leftExpander.module->model);
if (!connected && _wasConnected) {
_messages[1] = _messages[0] = MSG();
}
diff --git a/src/parametric_eq.cpp b/src/parametric_eq.cpp
@@ -50,30 +50,30 @@ void PEQChannel::modulate() {
fcv *= 12.0f;
}
- float f = _frequencyParam.getValue();
- f *= f;
- f *= maxFrequency;
- f = clamp(f, minFrequency, maxFrequency);
- f = frequencyToSemitone(f);
- f += fcv;
- f = clamp(f, minFrequencySemitone, maxFrequencySemitone);
- f = semitoneToFrequency(_frequencySL.next(f));
+ frequency = _frequencyParam.getValue();
+ frequency *= frequency;
+ frequency *= maxFrequency;
+ frequency = clamp(frequency, minFrequency, maxFrequency);
+ frequency = frequencyToSemitone(frequency);
+ frequency += fcv;
+ frequency = clamp(frequency, minFrequencySemitone, maxFrequencySemitone);
+ frequency = semitoneToFrequency(_frequencySL.next(frequency));
- float bw = MultimodeFilter::minQbw;
+ bandwidth = MultimodeFilter::minQbw;
if (_mode == MultimodeFilter::BANDPASS_MODE) {
- bw = clamp(_bandwidthParam.getValue(), 0.0f, 1.0f);
+ bandwidth = clamp(_bandwidthParam.getValue(), 0.0f, 1.0f);
if (_bandwidthInput && _bandwidthInput->isConnected()) {
- bw *= clamp(_bandwidthInput->getPolyVoltage(_c) / 10.0f, 0.0f, 1.0f);
+ bandwidth *= clamp(_bandwidthInput->getPolyVoltage(_c) / 10.0f, 0.0f, 1.0f);
}
- bw = MultimodeFilter::minQbw + bw * (MultimodeFilter::maxQbw - MultimodeFilter::minQbw);
+ bandwidth = MultimodeFilter::minQbw + bandwidth * (MultimodeFilter::maxQbw - MultimodeFilter::minQbw);
}
_filter->setParams(
_sampleRate,
MultimodeFilter::BUTTERWORTH_TYPE,
_poles,
_mode,
- f,
- bw,
+ frequency,
+ bandwidth,
MultimodeFilter::PITCH_BANDWIDTH_MODE
);
}
@@ -102,12 +102,14 @@ void PEQEngine::modulate() {
}
}
-float PEQEngine::next(float sample, float* outs, float* rmsSums) {
+float PEQEngine::next(float sample, float* rmsSums) {
+ bandwidth = _channels[1]->bandwidth; // take from any bandpass-only channel.
float out = 0.0f;
for (int i = 0; i < _n; ++i) {
PEQChannel& c = *_channels[i];
c.next(sample);
out += outs[i] = c.out;
+ frequencies[i] = c.frequency;
rmsSums[i] += c.rms;
}
return _saturator.next(out);
diff --git a/src/parametric_eq.hpp b/src/parametric_eq.hpp
@@ -40,6 +40,8 @@ struct PEQChannel {
float out = 0.0f;
float rms = 0.0f;
+ float frequency = 0.0f;
+ float bandwidth = 0.0f;
PEQChannel(
MultimodeFilter* filter,
@@ -87,14 +89,22 @@ struct PEQEngine {
PEQChannel** _channels;
Saturator _saturator;
+ float* outs = NULL;
+ float* frequencies = NULL;
+ float bandwidth = 0.0f;
+
PEQEngine(int channels) : _n(channels) {
_channels = new PEQChannel*[_n] {};
+ outs = new float[_n] {};
+ frequencies = new float[_n] {};
}
~PEQEngine() {
for (int i = 0; i < _n; ++i) {
delete _channels[i];
}
delete[] _channels;
+ delete[] outs;
+ delete[] frequencies;
}
inline void configChannel(
@@ -129,7 +139,7 @@ struct PEQEngine {
void setFrequencyMode(bool full);
void setSampleRate(float sr);
void modulate();
- float next(float sample, float* outs, float* rmsSums);
+ float next(float sample, float* rmsSums);
};
} // namespace bogaudio