commit 69c0033c128595e60d5cd81206ce9d41ebec2183
parent e39adffd1d38ab488d34d2b68346008517762758
Author: Matt Demanett <matt@demanett.net>
Date: Thu, 22 Feb 2018 01:14:29 -0500
Additator: more work.
Diffstat:
4 files changed, 243 insertions(+), 57 deletions(-)
diff --git a/res-src/Additator-src.svg b/res-src/Additator-src.svg
@@ -52,6 +52,16 @@
<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>
+
+ <symbol id="light" viewBox="0 0 1.1px 1.1px">
+ <rect width="3.2" height="3.2" fill="#0f0" />
+ </symbol>
</defs>
<rect width="100%" height="100%" fill="#ddd" />
@@ -84,31 +94,88 @@
<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)">
+ <g transform="translate(220 -20)">
<!-- <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)" />
+ <use id="ODD_SKEW_PARAM" xlink:href="#knob-medium" transform="translate(20 6)" />
+ </g>
+ <g transform="translate(220 20)">
+ <!-- <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_SKEW_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 36) rotate(270)">GAIN</text>
+ <use id="GAIN_PARAM" xlink:href="#knob" transform="translate(20 0)" />
+ </g>
+ <g transform="translate(90 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)">
+ <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 36) rotate(270)">ODDS</text>
- <use id="ODD_SCALE_PARAM" xlink:href="#knob-medium" transform="translate(20 6)" />
+ <text font-size="8pt" letter-spacing="2px" transform="translate(8 50) rotate(270)">BALANCE</text>
+ <use id="BALANCE_PARAM" xlink:href="#knob" transform="translate(20 0)" />
</g>
- <g transform="translate(150 0)">
+ <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 38) rotate(270)">EVENS</text>
- <use id="EVEN_SCALE_PARAM" xlink:href="#knob-medium" transform="translate(20 6)" />
+ <!-- <text font-size="8pt" letter-spacing="2px" transform="translate(8 50) rotate(270)">BALANCE</text> -->
+ <use id="FILTER_PARAM" xlink:href="#knob" transform="translate(20 0)" />
+ </g>
+ </g>
+
+ <g transform="translate(0 200)">
+ <g transform="translate(10 0)">
+ <rect width="30" height="40" rx="5" fill="#fafafa" />
+ <use id="PARTIALS_INPUT" xlink:href="#input" transform="translate(3 3)" />
+ <text font-size="6pt" letter-spacing="1px" transform="translate(9 36)">PS</text>
+ </g>
+ <g transform="translate(50 0)">
+ <rect width="30" height="40" rx="5" fill="#fafafa" />
+ <use id="WIDTH_INPUT" xlink:href="#input" transform="translate(3 3)" />
+ <text font-size="6pt" letter-spacing="2px" transform="translate(11.5 36)">W</text>
+ </g>
+ <g transform="translate(90 0)">
+ <rect width="30" height="40" rx="5" fill="#fafafa" />
+ <use id="ODD_SKEW_INPUT" xlink:href="#input" transform="translate(3 3)" />
+ <text font-size="6pt" letter-spacing="2px" transform="translate(9 36)">OS</text>
+ </g>
+ <g transform="translate(130 0)">
+ <rect width="30" height="40" rx="5" fill="#fafafa" />
+ <use id="EVEN_SKEW_INPUT" xlink:href="#input" transform="translate(3 3)" />
+ <text font-size="6pt" letter-spacing="2px" transform="translate(9 36)">ES</text>
+ </g>
+ </g>
+
+ <g transform="translate(0 260)">
+ <g transform="translate(10 0)">
+ <rect width="30" height="40" rx="5" fill="#fafafa" />
+ <use id="GAIN_INPUT" xlink:href="#input" transform="translate(3 3)" />
+ <text font-size="6pt" letter-spacing="1px" transform="translate(11.5 36)">G</text>
+ </g>
+ <g transform="translate(50 0)">
+ <rect width="30" height="40" rx="5" fill="#fafafa" />
+ <use id="DECAY_INPUT" xlink:href="#input" transform="translate(3 3)" />
+ <text font-size="6pt" letter-spacing="2px" transform="translate(11.5 36)">D</text>
+ </g>
+ <g transform="translate(90 0)">
+ <rect width="30" height="40" rx="5" fill="#fafafa" />
+ <use id="BALANCE_INPUT" xlink:href="#input" transform="translate(3 3)" />
+ <text font-size="6pt" letter-spacing="2px" transform="translate(11.5 36)">B</text>
+ </g>
+ <g transform="translate(130 0)">
+ <rect width="30" height="40" rx="5" fill="#fafafa" />
+ <use id="FILTER_INPUT" xlink:href="#input" transform="translate(3 3)" />
+ <text font-size="6pt" letter-spacing="2px" transform="translate(11.5 36)">F</text>
</g>
</g>
+ <!-- <polyline points="0,0 300,0" stroke="#0f0" stroke-width="1" fill="none" transform="translate(0 339)" /> -->
<g transform="translate(0 320)">
<g transform="translate(10 0)">
<rect width="30" height="40" rx="5" fill="#fafafa" />
@@ -120,10 +187,16 @@
<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 transform="translate(111 0)">
+ <g transform="translate(0 17.5)">
+ <use id="SINE_LIGHT" xlink:href="#light" transform="translate(0 0)" />
+ <text font-size="7pt" letter-spacing="2px" transform="translate(7 5)">SIN</text>
+ </g>
+ <g transform="translate(32 17.5)">
+ <use id="COSINE_LIGHT" xlink:href="#light" transform="translate(0 0)" />
+ <text font-size="7pt" letter-spacing="2px" transform="translate(7 5)">COS</text>
+ </g>
+ <use id="PHASE_PARAM" xlink:href="#button-small" transform="translate(68 14.5)" />
</g>
<g transform="translate(260 0)">
<rect width="30" height="40" rx="5" fill="#bbb" />
diff --git a/res/Additator.svg b/res/Additator.svg
Binary files differ.
diff --git a/src/Additator.cpp b/src/Additator.cpp
@@ -3,53 +3,93 @@
void Additator::onReset() {
_syncTrigger.reset();
- _phase = -10.0f; // trigger a phase sync.
+ _phase = PHASE_RESET;
}
void Additator::onSampleRateChange() {
_oscillator.setSampleRate(engineGetSampleRate());
- _phase = -10.0f; // trigger a phase sync.
+ _phase = PHASE_RESET;
+}
+
+float Additator::cvValue(Input& cv) {
+ if (!cv.active) {
+ return 0.0f;
+ }
+ return clamp(cv.value / 5.0f, -1.0f, 1.0f);
}
void Additator::step() {
- float width = params[WIDTH_PARAM].value;
- float evenWidth = params[EVEN_WIDTH_PARAM].value;
- if (_width != width || evenWidth != _evenWidth) {
+ lights[SINE_LIGHT].value = _phase == PHASE_SINE;
+ lights[COSINE_LIGHT].value = _phase == PHASE_COSINE;
+ if (!outputs[AUDIO_OUTPUT].active) {
+ return;
+ }
+
+ float width = clamp(params[WIDTH_PARAM].value + (maxWidth / 2.0f) * cvValue(inputs[WIDTH_INPUT]), 0.0f, maxWidth);
+ float oddSkew = clamp(params[ODD_SKEW_PARAM].value + cvValue(inputs[ODD_SKEW_INPUT]), -maxSkew, maxSkew);
+ float evenSkew = clamp(params[EVEN_SKEW_PARAM].value + cvValue(inputs[EVEN_SKEW_INPUT]), -maxSkew, maxSkew);
+ if (
+ _width != width ||
+ _oddSkew != oddSkew ||
+ _evenSkew != evenSkew
+ ) {
_width = width;
- _evenWidth = evenWidth;
+ _oddSkew = oddSkew;
+ _evenSkew = evenSkew;
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;
+ ii += _evenSkew;
+ }
+ else {
+ ii += _oddSkew;
}
_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) {
+ int partials = clamp((int)roundf(params[PARTIALS_PARAM].value + (maxPartials / 2.0f) * cvValue(inputs[PARTIALS_INPUT])), 0, maxPartials);
+ float amplitudeNormalization = clamp(params[GAIN_PARAM].value + ((maxAmplitudeNormalization - minAmplitudeNormalization) / 2.0f) * cvValue(inputs[GAIN_INPUT]), minAmplitudeNormalization, maxAmplitudeNormalization);
+ float decay = clamp(params[DECAY_PARAM].value + ((maxDecay - minDecay) / 2.0f) * cvValue(inputs[DECAY_INPUT]), minDecay, maxDecay);
+ float balance = clamp(params[BALANCE_PARAM].value + cvValue(inputs[BALANCE_INPUT]), -1.0f, 1.0f);
+ float filter = clamp(params[FILTER_PARAM].value + cvValue(inputs[FILTER_INPUT]), minFilter, maxFilter);
+ if (
+ _partials != partials ||
+ _amplitudeNormalization != amplitudeNormalization ||
+ _decay != decay ||
+ _balance != balance ||
+ _filter != filter
+ ) {
_partials = partials;
+ _amplitudeNormalization = amplitudeNormalization;
_decay = decay;
- _oddScale = oddScale;
- _evenScale = evenScale;
+ _balance = balance;
+ _filter = filter;
float as[maxPartials + 1];
float total = as[1] = 1.0f;
+ filter = log10f(_filter) + 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;
+ as[i] = powf(i, -_decay) * powf(_filter, i);
+ if (i % 2 == 0) {
+ if (_balance > 0.0f) {
+ as[i] *= 1.0f - _balance;
+ }
+ }
+ else {
+ if (_balance < 0.0f) {
+ as[i] *= 1.0f + _balance;
+ }
+ }
total += as[i];
}
}
- total /= 3.0f; // pretty arbitrary, this.
+ total /= _amplitudeNormalization;
for (int i = 1, n = _oscillator.partialCount(); i <= n; ++i) {
as[i] /= total;
_oscillator.setPartialAmplitude(i, as[i]);
@@ -64,12 +104,12 @@ void Additator::step() {
_oscillator.setFrequency(frequency);
if (_syncTrigger.process(inputs[SYNC_INPUT].value)) {
- _oscillator.syncToPhase(_phase * M_PI / 2.0f);
+ _oscillator.syncToPhase(_phase == PHASE_SINE ? 0.0f : M_PI / 2.0f);
}
- float phase = params[PHASE_PARAM].value;
+ Phase phase = ((int)params[PHASE_PARAM].value) == 2 ? PHASE_COSINE : PHASE_SINE;
if (_phase != phase) {
_phase = phase;
- _oscillator.syncToPhase(phase * M_PI / 2.0f);
+ _oscillator.syncToPhase(_phase == PHASE_SINE ? 0.0f : M_PI / 2.0f);
}
outputs[AUDIO_OUTPUT].value = _oscillator.next() * 5.0;
@@ -95,35 +135,78 @@ struct AdditatorWidget : ModuleWidget {
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 oddSkewParamPosition = Vec(250.5, 36.5);
+ auto evenSkewParamPosition = Vec(250.5, 76.5);
+ auto gainParamPosition = Vec(40.5, 130.5);
+ auto decayParamPosition = Vec(120.5, 130.5);
+ auto balanceParamPosition = Vec(200.5, 130.5);
+ auto filterParamPosition = Vec(250.5, 130.5);
+ auto phaseParamPosition = Vec(179.0, 334.5);
+
+ auto partialsInputPosition = Vec(13.0, 203.0);
+ auto widthInputPosition = Vec(53.0, 203.0);
+ auto oddSkewInputPosition = Vec(93.0, 203.0);
+ auto evenSkewInputPosition = Vec(133.0, 203.0);
+ auto gainInputPosition = Vec(13.0, 263.0);
+ auto decayInputPosition = Vec(53.0, 263.0);
+ auto balanceInputPosition = Vec(93.0, 263.0);
+ auto filterInputPosition = Vec(133.0, 263.0);
auto pitchInputPosition = Vec(13.0, 323.0);
auto syncInputPosition = Vec(53.0, 323.0);
auto audioOutputPosition = Vec(263.0, 323.0);
+
+ auto sineLightPosition = Vec(111.0, 337.5);
+ auto cosineLightPosition = Vec(143.0, 337.5);
// 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);
- }
-
+ addParam(ParamWidget::create<Knob38>(partialsParamPosition, module, Additator::PARTIALS_PARAM, 1.0, module->maxPartials, module->maxPartials / 10.0f));
+ addParam(ParamWidget::create<Knob38>(widthParamPosition, module, Additator::WIDTH_PARAM, 0.0, module->maxWidth, module->maxWidth / 2.0f));
+ addParam(ParamWidget::create<Knob26>(oddSkewParamPosition, module, Additator::ODD_SKEW_PARAM, -module->maxSkew, module->maxSkew, 0.0));
+ addParam(ParamWidget::create<Knob26>(evenSkewParamPosition, module, Additator::EVEN_SKEW_PARAM, -module->maxSkew, module->maxSkew, 0.0));
+ addParam(ParamWidget::create<Knob38>(
+ gainParamPosition,
+ module,
+ Additator::GAIN_PARAM,
+ module->minAmplitudeNormalization,
+ module->maxAmplitudeNormalization,
+ (module->maxAmplitudeNormalization - module->minAmplitudeNormalization) / 2.0f + module->minAmplitudeNormalization
+ ));
+ addParam(ParamWidget::create<Knob38>(
+ decayParamPosition,
+ module,
+ Additator::DECAY_PARAM,
+ module->minDecay,
+ module->maxDecay,
+ (module->maxDecay - module->minDecay) / 2.0f + module->minDecay
+ ));
+ addParam(ParamWidget::create<Knob26>(balanceParamPosition, module, Additator::BALANCE_PARAM, -1.0, 1.0, 0.0));
+ addParam(ParamWidget::create<Knob26>(
+ filterParamPosition,
+ module,
+ Additator::FILTER_PARAM,
+ module->minFilter,
+ module->maxFilter,
+ (module->maxFilter - module->minFilter) / 2.0f + module->minFilter
+ ));
+ addParam(ParamWidget::create<StatefulButton9>(phaseParamPosition, module, Additator::PHASE_PARAM, 1.0, 2.0, 1.0));
+
+ addInput(Port::create<Port24>(partialsInputPosition, Port::INPUT, module, Additator::PARTIALS_INPUT));
+ addInput(Port::create<Port24>(widthInputPosition, Port::INPUT, module, Additator::WIDTH_INPUT));
+ addInput(Port::create<Port24>(oddSkewInputPosition, Port::INPUT, module, Additator::ODD_SKEW_INPUT));
+ addInput(Port::create<Port24>(evenSkewInputPosition, Port::INPUT, module, Additator::EVEN_SKEW_INPUT));
+ addInput(Port::create<Port24>(gainInputPosition, Port::INPUT, module, Additator::GAIN_INPUT));
+ addInput(Port::create<Port24>(decayInputPosition, Port::INPUT, module, Additator::DECAY_INPUT));
+ addInput(Port::create<Port24>(balanceInputPosition, Port::INPUT, module, Additator::BALANCE_INPUT));
+ addInput(Port::create<Port24>(filterInputPosition, Port::INPUT, module, Additator::FILTER_INPUT));
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));
+
+ addChild(ModuleLightWidget::create<TinyLight<GreenLight>>(sineLightPosition, module, Additator::SINE_LIGHT));
+ addChild(ModuleLightWidget::create<TinyLight<GreenLight>>(cosineLightPosition, module, Additator::COSINE_LIGHT));
}
};
diff --git a/src/Additator.hpp b/src/Additator.hpp
@@ -15,10 +15,12 @@ struct Additator : Module {
FREQUENCY_PARAM,
PARTIALS_PARAM,
WIDTH_PARAM,
- EVEN_WIDTH_PARAM,
+ ODD_SKEW_PARAM,
+ EVEN_SKEW_PARAM,
+ GAIN_PARAM,
DECAY_PARAM,
- ODD_SCALE_PARAM,
- EVEN_SCALE_PARAM,
+ BALANCE_PARAM,
+ FILTER_PARAM,
PHASE_PARAM,
NUM_PARAMS
};
@@ -26,6 +28,14 @@ struct Additator : Module {
enum InputsIds {
PITCH_INPUT,
SYNC_INPUT,
+ PARTIALS_INPUT,
+ WIDTH_INPUT,
+ ODD_SKEW_INPUT,
+ EVEN_SKEW_INPUT,
+ GAIN_INPUT,
+ DECAY_INPUT,
+ BALANCE_INPUT,
+ FILTER_INPUT,
NUM_INPUTS
};
@@ -35,17 +45,36 @@ struct Additator : Module {
};
enum LightsIds {
+ SINE_LIGHT,
+ COSINE_LIGHT,
NUM_LIGHTS
};
+ enum Phase {
+ PHASE_RESET,
+ PHASE_SINE,
+ PHASE_COSINE
+ };
+
const int maxPartials = 100;
+ const float maxWidth = 2.0f;
+ const float maxSkew = 0.99f;
+ const float minAmplitudeNormalization = 1.0f;
+ const float maxAmplitudeNormalization = 5.0f;
+ const float minDecay = -1.0f;
+ const float maxDecay = 3.0f;
+ const float minFilter = 0.1;
+ const float maxFilter = 1.9;
+
int _partials = 0;
float _width = 0.0f;
- float _evenWidth = 0.0f;
+ float _oddSkew = 0.0f;
+ float _evenSkew = 0.0f;
+ float _amplitudeNormalization = 0.0f;
float _decay = 0.0f;
- float _oddScale = 0.0f;
- float _evenScale = 0.0f;
- float _phase = 0.0f;
+ float _balance = 0.0f;
+ float _filter = 0.0f;
+ Phase _phase = PHASE_RESET;
SineBankOscillator _oscillator;
SchmittTrigger _syncTrigger;
@@ -58,6 +87,7 @@ struct Additator : Module {
virtual void onReset() override;
virtual void onSampleRateChange() override;
+ float cvValue(Input& cv);
virtual void step() override;
};