commit b5a727fd6778e607527d041cb3ac37a41633b3da
parent a02b81b9d1df8b2741321ed8da482f6fc3f2b2e2
Author: Matt Demanett <matt@demanett.net>
Date: Sun, 29 Dec 2019 16:46:31 -0600
PULSE: compact square wave oscillator.
Diffstat:
8 files changed, 375 insertions(+), 11 deletions(-)
diff --git a/plugin.json b/plugin.json
@@ -40,6 +40,15 @@
]
},
{
+ "slug": "Bogaudio-Pulse",
+ "name": "PULSE",
+ "description": "Compact square/pulse oscillator with PWM",
+ "tags": [
+ "Oscillator",
+ "Polyphonic"
+ ]
+ },
+ {
"slug": "Bogaudio-XCO",
"name": "XCO",
"description": "Oscillator with wave mixer, wave mods, FM, hard sync",
@@ -532,6 +541,15 @@
]
},
{
+ "slug": "Bogaudio-PolyCon",
+ "name": "POLYCON",
+ "description": "Polyphonic per-channel constant voltages",
+ "tags": [
+ "Utility",
+ "Polyphonic"
+ ]
+ },
+ {
"slug": "Bogaudio-PolyMult",
"name": "POLYMULT",
"description": "Mono-to-poly multiple",
@@ -645,15 +663,6 @@
"tags": [
"Blank"
]
- },
- {
- "slug": "Bogaudio-PolyCon",
- "name": "POLYCON",
- "description": "Polyphonic per-channel constant voltages",
- "tags": [
- "Utility",
- "Polyphonic"
- ]
}
]
}
diff --git a/res-src/Pulse-src.svg b/res-src/Pulse-src.svg
@@ -0,0 +1,192 @@
+<svg
+ version="1.1"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ width="45"
+ height="380"
+ viewBox="0 0 45 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="knob-medium" viewBox="0 0 26px 26px">
+ <g transform="translate(13 13)">
+ <polyline points="-3,0 3,0" stroke-width="1" stroke="#00f" />
+ <polyline points="0,-3 0,3" stroke-width="1" stroke="#00f" />
+ <circle cx="0" cy="0" r="12.5" stroke-width="1" stroke="#00f" fill="none" />
+ </g>
+ </symbol>
+
+ <symbol id="knob-smallest" viewBox="0 0 16px 16px">
+ <g transform="translate(8 8)">
+ <polyline points="-3,0 3,0" stroke-width="1" stroke="#00f" />
+ <polyline points="0,-3 0,3" stroke-width="1" stroke="#00f" />
+ <circle r="7.5" stroke-width="1" stroke="#00f" fill="none" />
+ </g>
+ </symbol>
+
+ <symbol id="knobguide-frequency-sine" viewBox="0 0 45px 45px">
+ <g transform="translate(22.5 22.5)">
+ <polyline points="0,0 3.5,0" stroke-width="0.7" stroke="#333" transform="rotate(-240) translate(15 0)" />
+ <polyline points="0,0 2.5,0" stroke-width="0.3" stroke="#333" transform="rotate(-206.67) translate(15 0)" />
+ <polyline points="0,0 2.5,0" stroke-width="0.3" stroke="#333" transform="rotate(-173.33) translate(15 0)" />
+
+ <g transform="rotate(-140) translate(14 0)">
+ <polyline points="0,0 2.5,0" stroke-width="1.0" stroke="#333" transform="translate(0 0)" />
+ <text font-size="5.0pt" transform="translate(5 0) rotate(140) translate(-5.5 0)">OV</text>
+ </g>
+
+ <polyline points="0,0 2.5,0" stroke-width="0.3" stroke="#333" transform="rotate(-106.67) translate(15 0)" />
+ <polyline points="0,0 2.5,0" stroke-width="0.3" stroke="#333" transform="rotate(-73.33) translate(15 0)" />
+ <polyline points="0,0 2.5,0" stroke-width="0.3" stroke="#333" transform="rotate(-40) translate(15 0)" />
+ <polyline points="0,0 2.5,0" stroke-width="0.3" stroke="#333" transform="rotate(-6.67) translate(15 0)" />
+ <polyline points="0,0 2.5,0" stroke-width="0.3" stroke="#333" transform="rotate(26.67) translate(15 0)" />
+ <polyline points="0,0 3.5,0" stroke-width="0.7" stroke="#333" transform="rotate(60) translate(15 0)" />
+ </g>
+ </symbol>
+
+ <symbol id="knobguide-pw" viewBox="0 0 45px 45px">
+ <g transform="translate(22.5 22.5)">
+ <g transform="rotate(-240) translate(15 0)">
+ <text font-size="9.0pt" transform="translate(3 0) rotate(240) translate(-2.2 3.6)">-</text>
+ </g>
+ <g transform="rotate(-210) translate(15 0)">
+ <polyline points="0,0 2.5,0" stroke-width="0.3" stroke="#333" />
+ </g>
+ <g transform="rotate(-180) translate(15 0)">
+ <polyline points="0,0 2.5,0" stroke-width="0.3" stroke="#333" />
+ </g>
+ <g transform="rotate(-150) translate(15 0)">
+ <polyline points="0,0 2.5,0" stroke-width="0.3" stroke="#333" />
+ </g>
+ <g transform="rotate(-120) translate(15 0)">
+ <polyline points="0,0 2.5,0" stroke-width="0.3" stroke="#333" />
+ </g>
+ <g transform="rotate(-90) translate(15 0)">
+ <text font-size="5.0pt" transform="translate(2 0) rotate(90) translate(-2 2)">0</text>
+ </g>
+ <g transform="rotate(-60) translate(15 0)">
+ <polyline points="0,0 2.5,0" stroke-width="0.3" stroke="#333" />
+ </g>
+ <g transform="rotate(-30) translate(15 0)">
+ <polyline points="0,0 2.5,0" stroke-width="0.3" stroke="#333" />
+ </g>
+ <g transform="rotate(0) translate(15 0)">
+ <polyline points="0,0 2.5,0" stroke-width="0.3" stroke="#333" />
+ </g>
+ <g transform="rotate(30) translate(15 0)">
+ <polyline points="0,0 2.5,0" stroke-width="0.3" stroke="#333" />
+ </g>
+ <g transform="rotate(60) translate(15 0)">
+ <text font-size="5.0pt" transform="translate(3 0) rotate(-60) translate(-2 2)">+</text>
+ </g>
+ </g>
+ </symbol>
+
+ <symbol id="knobguide-centertick" viewBox="0 0 40px 40px">
+ <g transform="translate(20 20)">
+ <g transform="rotate(-90) translate(10 0)">
+ <polyline points="0,0 4,0" stroke-width="1" stroke="#333" />
+ </g>
+ <path d="M 0 -12.5 A 12.5 12.5 0 0 1 12.5 0" stroke="#333" stroke-width="0.5" stroke-linecap="round" fill="none" transform="rotate(20)" />
+ <path d="M 0 -12.5 A 12.5 12.5 0 0 1 12.5 0" stroke="#333" stroke-width="0.5" stroke-linecap="round" fill="none" transform="rotate(43)" />
+ <path d="M 0 -12.5 A 12.5 12.5 0 0 0 -12.5 0" stroke="#333" stroke-width="0.5" stroke-linecap="round" fill="none" transform="rotate(-20)" />
+ <path d="M 0 -12.5 A 12.5 12.5 0 0 0 -12.5 0" stroke="#333" stroke-width="0.5" stroke-linecap="round" fill="none" transform="rotate(-43)" />
+ </g>
+ </symbol>
+
+ <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>
+
+ <symbol id="button-small" viewBox="0 0 9px 9px">
+ <g transform="translate(4.5 4.5)">
+ <circle r="4" stroke-width="1" stroke="#00f" fill="#f00" />
+ </g>
+ </symbol>
+ </defs>
+
+ <rect width="100%" height="100%" fill="#ddd" />
+ <polyline points="1,1 44,1 44,379 1,379 1,1" stroke="#e4e4e4" stroke-width="0.5" fill="none" />
+ <polyline points="0.5,0.5 44.5,0.5 44.5,379.5 0.5,379.5 0.5,0.5" stroke="#ebebeb" stroke-width="0.8" fill="none" />
+ <polyline points="0,0 45,0 45,380 0,380 0,0" stroke="#f2f2f2" stroke-width="1" fill="none" />
+
+ <g transform="rotate(-90) translate(-376 13)">
+ <text class="title" font-size="7pt" letter-spacing="2.5px">PULSE</text>
+ <g transform="translate(0 12)">
+ <text class="brand" font-size="7pt" letter-spacing="2px">BGA</text>
+ <rect width="3.0" height="3" fill="#ddd" transform="translate(11.5 -5)" />
+ </g>
+ </g>
+
+ <!-- <polyline points="0,0 0,380" stroke="#0f0" stroke-width="1" fill="none" transform="translate(22.5 0)" /> -->
+
+ <g transform="translate(0 25)">
+ <!-- <polyline points="0,0 45,0" stroke="#0f0" stroke-width="1" fill="none" transform="translate(0 0)" /> -->
+ <use id="FREQUENCY_PARAM" xlink:href="#knob-medium" transform="translate(9.5 2)" />
+ <use xlink:href="#knobguide-frequency-sine" transform="translate(0 -7.5)" />
+ </g>
+
+ <g transform="translate(0 63)">
+ <text font-size="6pt" letter-spacing="1px" transform="translate(4 6.1)">SLOW</text>
+ <use id="SLOW_PARAM" xlink:href="#button-small" transform="translate(31 -1)" />
+ </g>
+
+ <g transform="translate(0 89)">
+ <!-- <rect width="45" height="10" fill="#f0f" transform="translate(0 -17)" /> -->
+ <text font-size="6pt" letter-spacing="2px" transform="translate(15.5 -1)">PW</text>
+ <use id="PW_PARAM" xlink:href="#knob-medium" transform="translate(9.5 9.5)" />
+ <use xlink:href="#knobguide-pw" transform="translate(0 0)" />
+ </g>
+
+ <g transform="translate(0 145)">
+ <!-- <rect width="45" height="10" fill="#f0f" transform="translate(0 -16)" /> -->
+ <text font-size="6pt" letter-spacing="2px" transform="translate(10.5 0)">PWM</text>
+ <use id="PWM_PARAM" xlink:href="#knob-smallest" transform="translate(14.5 9.5)" />
+ <use xlink:href="#knobguide-centertick" transform="translate(2.2 -2.5)" />
+ </g>
+
+ <g transform="translate(0 182)">
+ <!-- <rect width="45" height="10" fill="#f0f" transform="translate(0 -10)" /> -->
+ <g transform="translate(5.5 0)">
+ <rect width="34" height="105" rx="5" fill="#fafafa" />
+ <rect width="34" height="10" fill="#fafafa" transform="translate(0 98)" />
+ <use id="PITCH_INPUT" xlink:href="#input" transform="translate(5 3)" />
+ <text font-size="5pt" letter-spacing="2px" transform="translate(3.5 35)">V/OCT</text>
+ <use id="PWM_INPUT" xlink:href="#input" transform="translate(5 38)" />
+ <text font-size="5pt" letter-spacing="2px" transform="translate(7 70)">PWM</text>
+ <use id="SYNC_INPUT" xlink:href="#input" transform="translate(5 73)" />
+ <text font-size="5pt" letter-spacing="2px" transform="translate(5 105)">SYNC</text>
+ </g>
+ <g transform="translate(5.5 111)">
+ <rect width="34" height="10" fill="#bbb" transform="translate(0 -3)" />
+ <rect width="34" height="35" rx="5" fill="#bbb" />
+ <use id="OUT_OUTPUT" xlink:href="#output" transform="translate(5 0)" />
+ <text font-size="5pt" letter-spacing="2px" transform="translate(8.3 32)">OUT</text>
+ </g>
+ </g>
+</svg>
diff --git a/res/Pulse.svg b/res/Pulse.svg
Binary files differ.
diff --git a/src/Pulse.cpp b/src/Pulse.cpp
@@ -0,0 +1,106 @@
+
+#include "Pulse.hpp"
+
+#define LINEAR_MODE "linear_mode"
+
+json_t* Pulse::dataToJson() {
+ json_t* root = VCOBase::dataToJson();
+ json_object_set_new(root, LINEAR_MODE, json_boolean(_linearMode));
+ return root;
+}
+
+void Pulse::dataFromJson(json_t* root) {
+ VCOBase::dataFromJson(root);
+ json_t* l = json_object_get(root, LINEAR_MODE);
+ if (l) {
+ _linearMode = json_is_true(l);
+ }
+}
+
+bool Pulse::active() {
+ return outputs[OUT_OUTPUT].isConnected();
+}
+
+void Pulse::addChannel(int c) {
+ VCOBase::addChannel(c);
+ _engines[c]->squareActive = true;
+}
+
+void Pulse::modulate() {
+ _slowMode = params[SLOW_PARAM].getValue() > 0.5f;
+}
+
+void Pulse::modulateChannel(int c) {
+ VCOBase::modulateChannel(c);
+ Engine& e = *_engines[c];
+
+ float pw = params[PW_PARAM].getValue();
+ if (inputs[PWM_INPUT].isConnected()) {
+ float pwm = clamp(inputs[PWM_INPUT].getPolyVoltage(c) / 5.0f, -1.0f, 1.0f);
+ pwm *= clamp(params[PWM_PARAM].getValue(), -1.0f, 1.0f);
+ pw = clamp(pw + pwm, -1.0f, 1.0f);
+ }
+ pw *= 1.0f - 2.0f * e.square.minPulseWidth;
+ pw *= 0.5f;
+ pw += 0.5f;
+ e.square.setPulseWidth(e.squarePulseWidthSL.next(pw));
+}
+
+void Pulse::processChannel(const ProcessArgs& args, int c) {
+ VCOBase::processChannel(args, c);
+
+ outputs[OUT_OUTPUT].setChannels(_channels);
+ outputs[OUT_OUTPUT].setVoltage(_engines[c]->squareOut, c);
+}
+
+struct PulseWidget : ModuleWidget {
+ static constexpr int hp = 3;
+
+ PulseWidget(Pulse* 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/Pulse.svg")));
+ addChild(panel);
+ }
+
+ addChild(createWidget<ScrewSilver>(Vec(0, 0)));
+ addChild(createWidget<ScrewSilver>(Vec(box.size.x - 15, 365)));
+
+ // generated by svg_widgets.rb
+ auto frequencyParamPosition = Vec(9.5, 27.0);
+ auto slowParamPosition = Vec(31.0, 62.0);
+ auto pwParamPosition = Vec(9.5, 98.5);
+ auto pwmParamPosition = Vec(14.5, 154.5);
+
+ auto pitchInputPosition = Vec(10.5, 185.0);
+ auto pwmInputPosition = Vec(10.5, 220.0);
+ auto syncInputPosition = Vec(10.5, 255.0);
+
+ auto outOutputPosition = Vec(10.5, 293.0);
+ // end generated by svg_widgets.rb
+
+ addParam(createParam<Knob26>(frequencyParamPosition, module, Pulse::FREQUENCY_PARAM));
+ addParam(createParam<IndicatorButtonGreen9>(slowParamPosition, module, Pulse::SLOW_PARAM));
+ addParam(createParam<Knob26>(pwParamPosition, module, Pulse::PW_PARAM));
+ addParam(createParam<Knob16>(pwmParamPosition, module, Pulse::PWM_PARAM));
+
+ addInput(createInput<Port24>(pitchInputPosition, module, Pulse::PITCH_INPUT));
+ addInput(createInput<Port24>(pwmInputPosition, module, Pulse::PWM_INPUT));
+ addInput(createInput<Port24>(syncInputPosition, module, Pulse::SYNC_INPUT));
+
+ addOutput(createOutput<Port24>(outOutputPosition, module, Pulse::OUT_OUTPUT));
+ }
+
+ void appendContextMenu(Menu* menu) override {
+ auto m = dynamic_cast<Pulse*>(module);
+ assert(m);
+ menu->addChild(new MenuLabel());
+ menu->addChild(new BoolOptionMenuItem("Lineary frequency mode", [m]() { return &m->_linearMode; }));
+ }
+};
+
+Model* modelPulse = createModel<Pulse, PulseWidget>("Bogaudio-Pulse", "PULSE", "Compact square/pulse oscillator with PWM", "Oscillator", "Polyphonic");
diff --git a/src/Pulse.hpp b/src/Pulse.hpp
@@ -0,0 +1,56 @@
+#pragma once
+
+#include "bogaudio.hpp"
+#include "vco_base.hpp"
+
+extern Model* modelPulse;
+
+namespace bogaudio {
+
+struct Pulse : VCOBase {
+ enum ParamsIds {
+ FREQUENCY_PARAM,
+ SLOW_PARAM,
+ PW_PARAM,
+ PWM_PARAM,
+ NUM_PARAMS
+ };
+
+ enum InputsIds {
+ PITCH_INPUT,
+ PWM_INPUT,
+ SYNC_INPUT,
+ NUM_INPUTS
+ };
+
+ enum OutputsIds {
+ OUT_OUTPUT,
+ NUM_OUTPUTS
+ };
+
+ Pulse()
+ : VCOBase(
+ FREQUENCY_PARAM,
+ -1,
+ PITCH_INPUT,
+ SYNC_INPUT,
+ -1
+ )
+ {
+ config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS);
+ configParam<VCOFrequencyParamQuantity>(FREQUENCY_PARAM, -3.0f, 6.0f, 0.0f, "Frequency", " Hz");
+ configParam(SLOW_PARAM, 0.0f, 1.0f, 0.0f, "Slow mode");
+ configParam(PW_PARAM, -1.0f, 1.0f, 0.0f, "Pulse width", "%", 0.0f, 100.0f*0.5f*(1.0f - 2.0f * SquareOscillator::minPulseWidth), 50.0f);
+ configParam(PWM_PARAM, -1.0f, 1.0f, 0.0f, "Pulse width CV amount");
+ }
+
+ json_t* dataToJson() override;
+ void dataFromJson(json_t* root) override;
+ bool active() override;
+ void addChannel(int c) override;
+ void modulate() override;
+ void modulateChannel(int c) override;
+ void processChannel(const ProcessArgs& args, int c) override;
+};
+
+} // namespace bogaudio
diff --git a/src/Sine.hpp b/src/Sine.hpp
@@ -56,7 +56,6 @@ struct Sine : VCOBase {
configParam(SLOW_PARAM, 0.0f, 1.0f, 0.0f, "Slow mode");
configParam(FM_DEPTH_PARAM, 0.0f, 1.0f, 0.0f, "FM depth", "%", 0.0f, 100.0f);
configParam(PHASE_PARAM, -1.0f, 1.0f, 0.0f, "Phase offset", "ยบ", 0.0f, 180.0f);
-
}
json_t* dataToJson() override;
diff --git a/src/bogaudio.cpp b/src/bogaudio.cpp
@@ -50,6 +50,7 @@
#include "PolyCon.hpp"
#include "PolyMult.hpp"
#include "Pressor.hpp"
+#include "Pulse.hpp"
#include "Reftone.hpp"
#include "SampleHold.hpp"
#include "Shaper.hpp"
@@ -88,6 +89,7 @@ void init(rack::Plugin *p) {
p->addModel(modelVCO);
p->addModel(modelLVCO);
p->addModel(modelSine);
+ p->addModel(modelPulse);
p->addModel(modelXCO);
p->addModel(modelAdditator);
p->addModel(modelFMOp);
diff --git a/src/vco_base.cpp b/src/vco_base.cpp
@@ -141,7 +141,7 @@ void VCOBase::processChannel(const ProcessArgs& args, int c) {
float frequency = e.baseHz;
Phasor::phase_delta_t phaseOffset = 0;
- if (inputs[_fmInputID].isConnected() && _fmDepth > 0.01f) {
+ if (_fmInputID >= 0 && inputs[_fmInputID].isConnected() && _fmDepth > 0.01f) {
float fm = inputs[_fmInputID].getPolyVoltage(c) * _fmDepth;
if (_fmLinearMode) {
phaseOffset = Phasor::radiansToPhase(2.0f * fm);