commit a44057ecb3821dd895c141499c4f42dded8a33a3
parent 05d0f3ffc5e9e063e5bba9f80050230eaa18cce7
Author: Matt Demanett <matt@demanett.net>
Date: Tue, 20 Feb 2018 01:27:10 -0500
Work-in-progress additive oscillator.
Diffstat:
8 files changed, 365 insertions(+), 28 deletions(-)
diff --git a/res-src/Additator-src.svg b/res-src/Additator-src.svg
@@ -0,0 +1,134 @@
+<svg
+ version="1.1"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ width="300"
+ height="380"
+ viewBox="0 0 300 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" viewBox="0 0 38px 38px">
+ <g transform="translate(19 19)">
+ <polyline points="-5,0 5,0" stroke-width="1" stroke="#00f" />
+ <polyline points="0,-5 0,5" stroke-width="1" stroke="#00f" />
+ <circle cx="0" cy="0" r="18" stroke-width="1" stroke="#00f" fill="none" />
+ </g>
+ </symbol>
+
+ <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" stroke-width="1" stroke="#00f" fill="none" />
+ </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>
+ </defs>
+
+ <rect width="100%" height="100%" fill="#ddd" />
+ <polyline points="1,1 299,1 299,379 1,379 1,1" stroke="#e4e4e4" stroke-width="0.5" fill="none" />
+ <polyline points="0.5,0.5 299.5,0.5 299.5,379.5 0.5,379.5 0.5,0.5" stroke="#ebebeb" stroke-width="0.8" fill="none" />
+ <polyline points="0,0 300,0 300,380 0,380 0,0" stroke="#f2f2f2" stroke-width="1" fill="none" />
+
+ <!-- <rect width="80" height="20" fill="#0f0" transform="translate(0 0)" /> -->
+ <!-- <rect width="80" height="20" fill="#0f0" transform="translate(220 0)" /> -->
+
+ <text class="title" x="86" y="19" font-size="12pt" letter-spacing="4px">ADDITATOR</text>
+ <g transform="translate(110 374)">
+ <text class="brand" font-size="8pt" letter-spacing="2px">BOGAUDIO</text>
+ <rect width="3.0" height="3" fill="#ddd" transform="translate(24 -5)" />
+ </g>
+
+ <g transform="translate(10 50)">
+ <g transform="translate(10 0)">
+ <!-- <polyline points="0,19 70,19" stroke="#0f0" stroke-width="1" fill="none" /> -->
+ <text font-size="8pt" letter-spacing="2px" transform="translate(8 57) rotate(270)">FREQUENCY</text>
+ <use id="FREQUENCY_PARAM" xlink:href="#knob" transform="translate(20 0)" />
+ </g>
+ <g transform="translate(90 0)">
+ <!-- <polyline points="0,19 68,19" stroke="#0f0" stroke-width="1" fill="none" /> -->
+ <text font-size="8pt" letter-spacing="2px" transform="translate(8 50) rotate(270)">PARTIALS</text>
+ <use id="PARTIALS_PARAM" xlink:href="#knob" transform="translate(20 0)" />
+ </g>
+ <g transform="translate(170 0)">
+ <!-- <polyline points="0,19 68,19" stroke="#0f0" stroke-width="1" fill="none" /> -->
+ <text font-size="8pt" letter-spacing="2px" transform="translate(8 39) rotate(270)">WIDTH</text>
+ <use id="WIDTH_PARAM" xlink:href="#knob" transform="translate(20 0)" />
+ </g>
+ <g transform="translate(220 0)">
+ <!-- <polyline points="0,19 68,19" stroke="#0f0" stroke-width="1" fill="none" /> -->
+ <!-- <text font-size="8pt" letter-spacing="2px" transform="translate(8 39) rotate(270)">WIDTH</text> -->
+ <use id="EVEN_WIDTH_PARAM" xlink:href="#knob-medium" transform="translate(20 6)" />
+ </g>
+ </g>
+
+ <g transform="translate(10 130)">
+ <g transform="translate(10 0)">
+ <!-- <polyline points="0,19 70,19" stroke="#0f0" stroke-width="1" fill="none" /> -->
+ <text font-size="8pt" letter-spacing="2px" transform="translate(8 40) rotate(270)">DECAY</text>
+ <use id="DECAY_PARAM" xlink:href="#knob" transform="translate(20 0)" />
+ </g>
+ <g transform="translate(90 0)">
+ <!-- <polyline points="0,19 68,19" stroke="#0f0" stroke-width="1" fill="none" /> -->
+ <text font-size="8pt" letter-spacing="2px" transform="translate(8 36) rotate(270)">ODDS</text>
+ <use id="ODD_SCALE_PARAM" xlink:href="#knob-medium" transform="translate(20 6)" />
+ </g>
+ <g transform="translate(150 0)">
+ <!-- <polyline points="0,19 68,19" stroke="#0f0" stroke-width="1" fill="none" /> -->
+ <text font-size="8pt" letter-spacing="2px" transform="translate(8 38) rotate(270)">EVENS</text>
+ <use id="EVEN_SCALE_PARAM" xlink:href="#knob-medium" transform="translate(20 6)" />
+ </g>
+ </g>
+
+ <g transform="translate(0 320)">
+ <g transform="translate(10 0)">
+ <rect width="30" height="40" rx="5" fill="#fafafa" />
+ <use id="PITCH_INPUT" xlink:href="#input" transform="translate(3 3)" />
+ <text font-size="6pt" letter-spacing="1px" transform="translate(1 36)">V/OCT</text>
+ </g>
+ <g transform="translate(50 0)">
+ <rect width="30" height="40" rx="5" fill="#fafafa" />
+ <use id="SYNC_INPUT" xlink:href="#input" transform="translate(3 3)" />
+ <text font-size="6pt" letter-spacing="2px" transform="translate(1 36)">SYNC</text>
+ </g>
+ <g transform="translate(150 0)">
+ <!-- <polyline points="0,19 68,19" stroke="#0f0" stroke-width="1" fill="none" /> -->
+ <text font-size="8pt" letter-spacing="2px" transform="translate(8 40) rotate(270)">PHASE</text>
+ <use id="PHASE_PARAM" xlink:href="#knob-medium" transform="translate(20 6)" />
+ </g>
+ <g transform="translate(260 0)">
+ <rect width="30" height="40" rx="5" fill="#bbb" />
+ <use id="AUDIO_OUTPUT" xlink:href="#output" transform="translate(3 3)" />
+ <text font-size="6pt" letter-spacing="2px" transform="translate(5 36)">OUT</text>
+ </g>
+ </g>
+</svg>
diff --git a/res/Additator.svg b/res/Additator.svg
Binary files differ.
diff --git a/src/Additator.cpp b/src/Additator.cpp
@@ -0,0 +1,130 @@
+
+#include "Additator.hpp"
+
+void Additator::onReset() {
+ _syncTrigger.reset();
+ _phase = -10.0f; // trigger a phase sync.
+}
+
+void Additator::onSampleRateChange() {
+ _oscillator.setSampleRate(engineGetSampleRate());
+ _phase = -10.0f; // trigger a phase sync.
+}
+
+void Additator::step() {
+ float width = params[WIDTH_PARAM].value;
+ float evenWidth = params[EVEN_WIDTH_PARAM].value;
+ if (_width != width || evenWidth != _evenWidth) {
+ _width = width;
+ _evenWidth = evenWidth;
+
+ float multiple = 1.0f;
+ _oscillator.setPartialFrequencyRatio(1, multiple);
+ for (int i = 2, n = _oscillator.partialCount(); i <= n; ++i) {
+ float ii = i;
+ if (i % 2 == 0) {
+ ii += _evenWidth;
+ }
+ _oscillator.setPartialFrequencyRatio(i, powf(ii, _width));
+ }
+ }
+
+ int partials = roundf(params[PARTIALS_PARAM].value);
+ float decay = params[DECAY_PARAM].value;
+ float oddScale = params[ODD_SCALE_PARAM].value;
+ float evenScale = params[EVEN_SCALE_PARAM].value;
+ if (_partials != partials || _decay != decay || _oddScale != oddScale || _evenScale != evenScale) {
+ _partials = partials;
+ _decay = decay;
+ _oddScale = oddScale;
+ _evenScale = evenScale;
+
+ float as[maxPartials + 1];
+ float total = as[1] = 1.0f;
+ for (int i = 2, n = _oscillator.partialCount(); i <= n; ++i) {
+ as[i] = 0.0f;
+ if (i <= _partials) {
+ as[i] = 1.0f / powf(i, _decay);
+ as[i] *= i % 2 == 1 ? _oddScale : _evenScale;
+ total += as[i];
+ }
+ }
+ total /= 3.0f; // pretty arbitrary, this.
+ for (int i = 1, n = _oscillator.partialCount(); i <= n; ++i) {
+ as[i] /= total;
+ _oscillator.setPartialAmplitude(i, as[i]);
+ }
+ }
+
+ float frequency = params[FREQUENCY_PARAM].value;
+ if (inputs[PITCH_INPUT].active) {
+ frequency += inputs[PITCH_INPUT].value;
+ }
+ frequency = clamp(cvToFrequency(frequency), 50.0f, 10000.0f);
+ _oscillator.setFrequency(frequency);
+
+ if (_syncTrigger.process(inputs[SYNC_INPUT].value)) {
+ _oscillator.syncToPhase(_phase * M_PI / 2.0f);
+ }
+ float phase = params[PHASE_PARAM].value;
+ if (_phase != phase) {
+ _phase = phase;
+ _oscillator.syncToPhase(phase * M_PI / 2.0f);
+ }
+
+ outputs[AUDIO_OUTPUT].value = _oscillator.next() * 5.0;
+}
+
+struct AdditatorWidget : ModuleWidget {
+ AdditatorWidget(Additator* module) : ModuleWidget(module) {
+ box.size = Vec(RACK_GRID_WIDTH * 20, RACK_GRID_HEIGHT);
+
+ {
+ SVGPanel *panel = new SVGPanel();
+ panel->box.size = box.size;
+ panel->setBackground(SVG::load(assetPlugin(plugin, "res/Additator.svg")));
+ addChild(panel);
+ }
+
+ addChild(Widget::create<ScrewSilver>(Vec(15, 0)));
+ addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 30, 0)));
+ addChild(Widget::create<ScrewSilver>(Vec(15, 365)));
+ addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 30, 365)));
+
+ // generated by svg_widgets.rb
+ auto frequencyParamPosition = Vec(40.5, 50.5);
+ auto partialsParamPosition = Vec(120.5, 50.5);
+ auto widthParamPosition = Vec(200.5, 50.5);
+ auto evenWidthParamPosition = Vec(250.5, 56.5);
+ auto decayParamPosition = Vec(40.5, 130.5);
+ auto oddScaleParamPosition = Vec(120.5, 136.5);
+ auto evenScaleParamPosition = Vec(180.5, 136.5);
+ auto phaseParamPosition = Vec(170.5, 326.5);
+
+ auto pitchInputPosition = Vec(13.0, 323.0);
+ auto syncInputPosition = Vec(53.0, 323.0);
+
+ auto audioOutputPosition = Vec(263.0, 323.0);
+ // end generated by svg_widgets.rb
+
+ addParam(ParamWidget::create<Knob38>(frequencyParamPosition, module, Additator::FREQUENCY_PARAM, -5.0, 5.0, 0.0));
+ addParam(ParamWidget::create<Knob38>(partialsParamPosition, module, Additator::PARTIALS_PARAM, 1.0, module->maxPartials, module->maxPartials / 2.0f));
+ addParam(ParamWidget::create<Knob38>(widthParamPosition, module, Additator::WIDTH_PARAM, 0.0, 2.0, 1.0));
+ addParam(ParamWidget::create<Knob26>(evenWidthParamPosition, module, Additator::EVEN_WIDTH_PARAM, -0.99, 0.99, 0.0));
+ addParam(ParamWidget::create<Knob38>(decayParamPosition, module, Additator::DECAY_PARAM, -1.0, 3.0, 1.0));
+ addParam(ParamWidget::create<Knob26>(oddScaleParamPosition, module, Additator::ODD_SCALE_PARAM, 0.0, 1.0, 1.0));
+ addParam(ParamWidget::create<Knob26>(evenScaleParamPosition, module, Additator::EVEN_SCALE_PARAM, 0.0, 1.0, 1.0));
+ {
+ auto w = ParamWidget::create<Knob26>(phaseParamPosition, module, Additator::PHASE_PARAM, 0.0, 3.0, 0.0);
+ dynamic_cast<Knob*>(w)->snap = true;
+ addParam(w);
+ }
+
+ addInput(Port::create<Port24>(pitchInputPosition, Port::INPUT, module, Additator::PITCH_INPUT));
+ addInput(Port::create<Port24>(syncInputPosition, Port::INPUT, module, Additator::SYNC_INPUT));
+
+ addOutput(Port::create<Port24>(audioOutputPosition, Port::OUTPUT, module, Additator::AUDIO_OUTPUT));
+ }
+};
+
+Model* modelAdditator = Model::create<Additator, AdditatorWidget>("Bogaudio", "Bogaudio-Additator", "Additator");
diff --git a/src/Additator.hpp b/src/Additator.hpp
@@ -0,0 +1,64 @@
+#pragma once
+
+#include "bogaudio.hpp"
+#include "dsp/oscillator.hpp"
+#include "dsp/pitch.hpp"
+
+using namespace bogaudio::dsp;
+
+extern Model* modelAdditator;
+
+namespace bogaudio {
+
+struct Additator : Module {
+ enum ParamsIds {
+ FREQUENCY_PARAM,
+ PARTIALS_PARAM,
+ WIDTH_PARAM,
+ EVEN_WIDTH_PARAM,
+ DECAY_PARAM,
+ ODD_SCALE_PARAM,
+ EVEN_SCALE_PARAM,
+ PHASE_PARAM,
+ NUM_PARAMS
+ };
+
+ enum InputsIds {
+ PITCH_INPUT,
+ SYNC_INPUT,
+ NUM_INPUTS
+ };
+
+ enum OutputsIds {
+ AUDIO_OUTPUT,
+ NUM_OUTPUTS
+ };
+
+ enum LightsIds {
+ NUM_LIGHTS
+ };
+
+ const int maxPartials = 100;
+ int _partials = 0;
+ float _width = 0.0f;
+ float _evenWidth = 0.0f;
+ float _decay = 0.0f;
+ float _oddScale = 0.0f;
+ float _evenScale = 0.0f;
+ float _phase = 0.0f;
+ SineBankOscillator _oscillator;
+ SchmittTrigger _syncTrigger;
+
+ Additator()
+ : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS)
+ , _oscillator(engineGetSampleRate(), 1000.0, maxPartials)
+ {
+ onReset();
+ }
+
+ virtual void onReset() override;
+ virtual void onSampleRateChange() override;
+ virtual void step() override;
+};
+
+} // namespace bogaudio
diff --git a/src/Test.hpp b/src/Test.hpp
@@ -94,10 +94,10 @@ struct Test : Module {
switch (5) {
case 1: {
// saw
- float phase = M_PI;
for (int i = 1, n = _sineBank.partialCount(); i <= n; ++i) {
- _sineBank.setPartial(i, i, baseAmplitude / (float)i, &phase);
+ _sineBank.setPartial(i, i, baseAmplitude / (float)i);
}
+ _sineBank.syncToPhase(M_PI);
break;
}
@@ -112,17 +112,16 @@ struct Test : Module {
case 3: {
// triangle
if (false) {
- float phase = M_PI / 2.0;
for (int i = 1, n = _sineBank.partialCount(); i <= n; ++i) {
- _sineBank.setPartial(i, i, i % 2 == 1 ? baseAmplitude / (float)(i * i) : 0.0, &phase);
+ _sineBank.setPartial(i, i, i % 2 == 1 ? baseAmplitude / (float)(i * i) : 0.0);
}
+ _sineBank.syncToPhase(M_PI / 2.0);
}
else {
- float phase = 0.0f;
- _sineBank.setPartial(1, 1.0f, baseAmplitude, &phase);
+ _sineBank.setPartial(1, 1.0f, baseAmplitude);
for (int i = 2, n = _sineBank.partialCount(); i < n; ++i) {
float k = 2*i - 1;
- _sineBank.setPartial(i, k, powf(-1.0f, k) * baseAmplitude/(k * k), &phase);
+ _sineBank.setPartial(i, k, powf(-1.0f, k) * baseAmplitude/(k * k));
}
}
break;
@@ -138,12 +137,11 @@ struct Test : Module {
case 5: {
// ?
- float phase = 0.0;
float factor = 0.717;
float factor2 = factor;
float multiple = 1.0;
for (int i = 1, n = _sineBank.partialCount(); i <= n; ++i) {
- _sineBank.setPartial(i, multiple, baseAmplitude / multiple, &phase);
+ _sineBank.setPartial(i, multiple, baseAmplitude / multiple);
multiple += i % 2 == 1 ? factor : factor2;
}
break;
diff --git a/src/bogaudio.cpp b/src/bogaudio.cpp
@@ -1,6 +1,8 @@
#include "bogaudio.hpp"
+#include "Additator.hpp"
+
#include "Shaper.hpp"
#include "ShaperPlus.hpp"
#include "DADSRH.hpp"
@@ -33,6 +35,8 @@ void init(rack::Plugin *p) {
p->website = "https://github.com/bogaudio/BogaudioModules";
p->manual = "https://github.com/bogaudio/BogaudioModules/blob/master/README.md";
+ p->addModel(modelAdditator);
+
p->addModel(modelShaper);
p->addModel(modelShaperPlus);
p->addModel(modelDADSRH);
diff --git a/src/dsp/oscillator.cpp b/src/dsp/oscillator.cpp
@@ -94,24 +94,29 @@ float TriangleOscillator::_next() {
}
-void SineBankOscillator::setPartial(int i, float frequencyRatio, float amplitude, float* phase) {
- if (i > (int)_partials.size()) {
- return;
- }
+void SineBankOscillator::setPartial(int i, float frequencyRatio, float amplitude) {
+ setPartialFrequencyRatio(i, frequencyRatio);
+ setPartialAmplitude(i, amplitude);
+}
- Partial& p = _partials[i - 1];
- if (amplitude > 0.01f || amplitude < -0.01f) {
- p.enabled = true;
+void SineBankOscillator::setPartialFrequencyRatio(int i, float frequencyRatio) {
+ if (i <= (int)_partials.size()) {
+ Partial& p = _partials[i - 1];
p.frequencyRatio = frequencyRatio;
p.frequency = _frequency * frequencyRatio;
p.sine.setFrequency((double)_frequency * (double)frequencyRatio);
- p.amplitude = amplitude;
- if (phase) {
- p.sine.setPhase(*phase);
- }
}
- else {
- p.enabled = false;
+}
+
+void SineBankOscillator::setPartialAmplitude(int i, float amplitude) {
+ if (i <= (int)_partials.size()) {
+ _partials[i - 1].amplitude = amplitude;
+ }
+}
+
+void SineBankOscillator::syncToPhase(float phase) {
+ for (Partial& p : _partials) {
+ p.sine.setPhase(phase);
}
}
@@ -132,7 +137,7 @@ void SineBankOscillator::_frequencyChanged() {
float SineBankOscillator::_next() {
float next = 0.0;
for (Partial& p : _partials) {
- if (p.enabled && p.frequency < _maxPartialFrequency) {
+ if (p.frequency < _maxPartialFrequency && (p.amplitude > 0.001 || p.amplitude < -0.001)) {
next += p.sine.next() * p.amplitude;
}
else {
diff --git a/src/dsp/oscillator.hpp b/src/dsp/oscillator.hpp
@@ -158,15 +158,13 @@ struct TriangleOscillator : Phasor {
struct SineBankOscillator : OscillatorGenerator {
struct Partial {
- bool enabled;
float frequency;
float frequencyRatio;
float amplitude;
SineOscillator sine;
Partial()
- : enabled(false)
- , frequency(0.0)
+ : frequency(0.0)
, frequencyRatio(0.0)
, amplitude(0.0)
, sine(0.0, 0.0, 1.0)
@@ -193,8 +191,12 @@ struct SineBankOscillator : OscillatorGenerator {
return _partials.size();
}
- // one-based index.
- void setPartial(int i, float frequencyRatio, float amplitude, float* phase = NULL);
+ // one-based indexes.
+ void setPartial(int i, float frequencyRatio, float amplitude);
+ void setPartialFrequencyRatio(int i, float frequencyRatio);
+ void setPartialAmplitude(int i, float amplitude);
+
+ void syncToPhase(float phase);
virtual void _sampleRateChanged() override;
virtual void _frequencyChanged() override;