diff --git a/res/LFO-1.svg b/res/LFO-1.svg new file mode 100644 index 0000000..7509fc5 --- /dev/null +++ b/res/LFO-1.svg @@ -0,0 +1,482 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/res/LFO-2.svg b/res/LFO-2.svg new file mode 100644 index 0000000..a8b4329 --- /dev/null +++ b/res/LFO-2.svg @@ -0,0 +1,328 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/res/SEQ3.svg b/res/SEQ3.svg index 5b2c7cb..025d557 100644 --- a/res/SEQ3.svg +++ b/res/SEQ3.svg @@ -9,15 +9,15 @@ xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - width="87.444527mm" - height="100.54166mm" - viewBox="0 0 87.444527 100.54168" + width="330.49908" + height="380" + viewBox="0 0 87.444549 100.54166" version="1.1" - id="svg4541" - sodipodi:docname="SEQ3.svg" - inkscape:version="0.92.2 5c3e80d, 2017-08-06"> + id="svg12553" + inkscape:version="0.92.2 5c3e80d, 2017-08-06" + sodipodi:docname="SEQ3.svg"> + id="defs12547" /> + inkscape:window-maximized="0"> + + + id="metadata12550"> @@ -56,461 +63,481 @@ inkscape:label="Layer 1" inkscape:groupmode="layer" id="layer1" - transform="translate(36.687896,-107.67836)"> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + transform="translate(-26.543497,-161.30655)"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/res/VCO-1.svg b/res/VCO-1.svg new file mode 100644 index 0000000..bdba7dc --- /dev/null +++ b/res/VCO-1.svg @@ -0,0 +1,466 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/res/VCO-2.svg b/res/VCO-2.svg new file mode 100644 index 0000000..c3e2536 --- /dev/null +++ b/res/VCO-2.svg @@ -0,0 +1,327 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/res/VCO.svg b/res/VCO.svg deleted file mode 100644 index 6846d25..0000000 --- a/res/VCO.svg +++ /dev/null @@ -1,452 +0,0 @@ - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/ADSR.cpp b/src/ADSR.cpp index b42a804..4b69b58 100644 --- a/src/ADSR.cpp +++ b/src/ADSR.cpp @@ -111,10 +111,10 @@ ADSRWidget::ADSRWidget() { addChild(createScrew(Vec(15, 365))); addChild(createScrew(Vec(box.size.x-30, 365))); - addParam(createParam(Vec(62, 57), module, ADSR::ATTACK_PARAM, 0.0, 1.0, 0.5)); - addParam(createParam(Vec(62, 124), module, ADSR::DECAY_PARAM, 0.0, 1.0, 0.5)); - addParam(createParam(Vec(62, 191), module, ADSR::SUSTAIN_PARAM, 0.0, 1.0, 0.5)); - addParam(createParam(Vec(62, 257), module, ADSR::RELEASE_PARAM, 0.0, 1.0, 0.5)); + addParam(createParam(Vec(62, 57), module, ADSR::ATTACK_PARAM, 0.0, 1.0, 0.5)); + addParam(createParam(Vec(62, 124), module, ADSR::DECAY_PARAM, 0.0, 1.0, 0.5)); + addParam(createParam(Vec(62, 191), module, ADSR::SUSTAIN_PARAM, 0.0, 1.0, 0.5)); + addParam(createParam(Vec(62, 257), module, ADSR::RELEASE_PARAM, 0.0, 1.0, 0.5)); addInput(createInput(Vec(9, 63), module, ADSR::ATTACK_INPUT)); addInput(createInput(Vec(9, 129), module, ADSR::DECAY_INPUT)); diff --git a/src/Delay.cpp b/src/Delay.cpp index 57398e1..2eb48e2 100644 --- a/src/Delay.cpp +++ b/src/Delay.cpp @@ -122,10 +122,10 @@ DelayWidget::DelayWidget() { addChild(createScrew(Vec(15, 365))); addChild(createScrew(Vec(box.size.x-30, 365))); - addParam(createParam(Vec(67, 57), module, Delay::TIME_PARAM, 0.0, 1.0, 0.5)); - addParam(createParam(Vec(67, 123), module, Delay::FEEDBACK_PARAM, 0.0, 1.0, 0.5)); - addParam(createParam(Vec(67, 190), module, Delay::COLOR_PARAM, 0.0, 1.0, 0.5)); - addParam(createParam(Vec(67, 257), module, Delay::MIX_PARAM, 0.0, 1.0, 0.5)); + addParam(createParam(Vec(67, 57), module, Delay::TIME_PARAM, 0.0, 1.0, 0.5)); + addParam(createParam(Vec(67, 123), module, Delay::FEEDBACK_PARAM, 0.0, 1.0, 0.5)); + addParam(createParam(Vec(67, 190), module, Delay::COLOR_PARAM, 0.0, 1.0, 0.5)); + addParam(createParam(Vec(67, 257), module, Delay::MIX_PARAM, 0.0, 1.0, 0.5)); addInput(createInput(Vec(14, 63), module, Delay::TIME_INPUT)); addInput(createInput(Vec(14, 129), module, Delay::FEEDBACK_INPUT)); diff --git a/src/Fundamental.cpp b/src/Fundamental.cpp index 7e2dce2..2a3ea76 100644 --- a/src/Fundamental.cpp +++ b/src/Fundamental.cpp @@ -8,9 +8,10 @@ void init(rack::Plugin *p) { plugin->slug = "Fundamental"; plugin->name = "Fundamental"; plugin->homepageUrl = "https://github.com/VCVRack/Fundamental"; - createModel(plugin, "VCO", "VCO"); + createModel(plugin, "VCO", "VCO-1"); createModel(plugin, "VCF", "VCF"); createModel(plugin, "VCA", "VCA"); + createModel(plugin, "LFO", "LFO-1"); createModel(plugin, "Delay", "Delay"); createModel(plugin, "ADSR", "ADSR"); createModel(plugin, "VCMixer", "VC Mixer"); diff --git a/src/Fundamental.hpp b/src/Fundamental.hpp index 08c89c5..476cc0c 100644 --- a/src/Fundamental.hpp +++ b/src/Fundamental.hpp @@ -22,6 +22,10 @@ struct VCAWidget : ModuleWidget { VCAWidget(); }; +struct LFOWidget : ModuleWidget { + LFOWidget(); +}; + struct DelayWidget : ModuleWidget { DelayWidget(); }; diff --git a/src/LFO.cpp b/src/LFO.cpp new file mode 100644 index 0000000..d5647cf --- /dev/null +++ b/src/LFO.cpp @@ -0,0 +1,126 @@ +#include "Fundamental.hpp" + + +struct LFO : Module { + enum ParamIds { + OFFSET_PARAM, + INVERT_PARAM, + FREQ_PARAM, + FM1_PARAM, + FM2_PARAM, + PW_PARAM, + PWM_PARAM, + NUM_PARAMS + }; + enum InputIds { + FM1_INPUT, + FM2_INPUT, + RESET_INPUT, + PW_INPUT, + NUM_INPUTS + }; + enum OutputIds { + SIN_OUTPUT, + TRI_OUTPUT, + SAW_OUTPUT, + SQR_OUTPUT, + NUM_OUTPUTS + }; + + float phase = 0.0; + + float lights[1] = {}; + + LFO() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS) {} + void step(); +}; + + +void LFO::step() { + // Compute frequency + float pitch = params[FREQ_PARAM].value; + pitch += params[FM1_PARAM].value * inputs[FM1_INPUT].value; + pitch += params[FM2_PARAM].value * inputs[FM2_INPUT].value; + pitch = fminf(pitch, 8.0); + float freq = powf(2.0, pitch); + + // Pulse width + const float pwMin = 0.01; + float pw = clampf(params[PW_PARAM].value + params[PWM_PARAM].value * inputs[PW_INPUT].value / 10.0, pwMin, 1.0 - pwMin); + + // Advance phase + float deltaPhase = fminf(freq / gSampleRate, 0.5); + phase += deltaPhase; + if (phase >= 1.0) + phase -= 1.0; + + float offset = params[OFFSET_PARAM].value > 0.0 ? 5.0 : 0.0; + float factor = params[INVERT_PARAM].value > 0.0 ? 5.0 : -5.0; + + // Outputs + float sin = sinf(2*M_PI * phase); + + float tri; + if (phase < 0.25) + tri = 4.0*phase; + else if (phase < 0.75) + tri = 2.0 - 4.0*phase; + else + tri = -4.0 + 4.0*phase; + + float saw; + if (phase < 0.5) + saw = 2.0*phase; + else + saw = -2.0 + 2.0*phase; + + float sqr = phase < pw ? 1.0 : -1.0; + + outputs[SIN_OUTPUT].value = factor * sin + offset; + outputs[TRI_OUTPUT].value = factor * tri + offset; + outputs[SAW_OUTPUT].value = factor * saw + offset; + outputs[SQR_OUTPUT].value = factor * sqr + offset; + + // Lights + lights[0] = -1.0 + 2.0*phase; +} + + +LFOWidget::LFOWidget() { + LFO *module = new LFO(); + setModule(module); + box.size = Vec(15*10, 380); + + { + SVGPanel *panel = new SVGPanel(); + panel->box.size = box.size; + panel->setBackground(SVG::load(assetPlugin(plugin, "res/LFO-1.svg"))); + addChild(panel); + } + + addChild(createScrew(Vec(15, 0))); + addChild(createScrew(Vec(box.size.x-30, 0))); + addChild(createScrew(Vec(15, 365))); + addChild(createScrew(Vec(box.size.x-30, 365))); + + addParam(createParam(Vec(15, 77), module, LFO::OFFSET_PARAM, 0.0, 1.0, 1.0)); + addParam(createParam(Vec(119, 77), module, LFO::INVERT_PARAM, 0.0, 1.0, 1.0)); + + addParam(createParam(Vec(47, 61), module, LFO::FREQ_PARAM, -8.0, 6.0, -1.0)); + addParam(createParam(Vec(23, 143), module, LFO::FM1_PARAM, 0.0, 1.0, 0.0)); + addParam(createParam(Vec(91, 143), module, LFO::PW_PARAM, 0.0, 1.0, 0.5)); + addParam(createParam(Vec(23, 208), module, LFO::FM2_PARAM, 0.0, 1.0, 0.0)); + addParam(createParam(Vec(91, 208), module, LFO::PWM_PARAM, 0.0, 1.0, 0.0)); + + addInput(createInput(Vec(11, 276), module, LFO::FM1_INPUT)); + addInput(createInput(Vec(45, 276), module, LFO::FM2_INPUT)); + addInput(createInput(Vec(80, 276), module, LFO::RESET_INPUT)); + addInput(createInput(Vec(114, 276), module, LFO::PW_INPUT)); + + addOutput(createOutput(Vec(11, 320), module, LFO::SIN_OUTPUT)); + addOutput(createOutput(Vec(45, 320), module, LFO::TRI_OUTPUT)); + addOutput(createOutput(Vec(80, 320), module, LFO::SAW_OUTPUT)); + addOutput(createOutput(Vec(114, 320), module, LFO::SQR_OUTPUT)); + + addChild(createValueLight>(Vec(99, 41), &module->lights[0])); +} diff --git a/src/SEQ3.cpp b/src/SEQ3.cpp index 1aa1615..05d8e29 100644 --- a/src/SEQ3.cpp +++ b/src/SEQ3.cpp @@ -224,18 +224,18 @@ SEQ3Widget::SEQ3Widget() { addChild(createScrew(Vec(15, 365))); addChild(createScrew(Vec(box.size.x-30, 365))); - addParam(createParam(Vec(17, 56), module, SEQ3::CLOCK_PARAM, -2.0, 6.0, 2.0)); + addParam(createParam(Vec(18, 56), module, SEQ3::CLOCK_PARAM, -2.0, 6.0, 2.0)); addParam(createParam(Vec(60, 61-1), module, SEQ3::RUN_PARAM, 0.0, 1.0, 0.0)); addChild(createValueLight>(Vec(60+5, 61+4), &module->runningLight)); - addParam(createParam(Vec(98, 61-1), module, SEQ3::RESET_PARAM, 0.0, 1.0, 0.0)); + addParam(createParam(Vec(99, 61-1), module, SEQ3::RESET_PARAM, 0.0, 1.0, 0.0)); addChild(createValueLight>(Vec(98+5, 61+4), &module->resetLight)); addParam(createParam(Vec(132, 56), module, SEQ3::STEPS_PARAM, 1.0, 8.0, 8.0)); - addChild(createValueLight>(Vec(180.5, 65), &module->gatesLight)); - addChild(createValueLight>(Vec(218.5, 65), &module->rowLights[0])); + addChild(createValueLight>(Vec(180, 65), &module->gatesLight)); + addChild(createValueLight>(Vec(219, 65), &module->rowLights[0])); addChild(createValueLight>(Vec(257, 65), &module->rowLights[1])); - addChild(createValueLight>(Vec(295.5, 65), &module->rowLights[2])); + addChild(createValueLight>(Vec(296, 65), &module->rowLights[2])); - static const float portX[8] = {19, 57, 96, 134, 173, 211, 250, 288}; + static const float portX[8] = {20, 58, 96, 135, 173, 212, 250, 289}; addInput(createInput(Vec(portX[0]-1, 99-1), module, SEQ3::CLOCK_INPUT)); addInput(createInput(Vec(portX[1]-1, 99-1), module, SEQ3::EXT_CLOCK_INPUT)); addInput(createInput(Vec(portX[2]-1, 99-1), module, SEQ3::RESET_INPUT)); diff --git a/src/VCA.cpp b/src/VCA.cpp index f9bc912..484272d 100644 --- a/src/VCA.cpp +++ b/src/VCA.cpp @@ -60,8 +60,8 @@ VCAWidget::VCAWidget() { addChild(createScrew(Vec(15, 365))); addChild(createScrew(Vec(box.size.x-30, 365))); - addParam(createParam(Vec(27, 57), module, VCA::LEVEL1_PARAM, 0.0, 1.0, 0.5)); - addParam(createParam(Vec(27, 222), module, VCA::LEVEL2_PARAM, 0.0, 1.0, 0.5)); + addParam(createParam(Vec(27, 57), module, VCA::LEVEL1_PARAM, 0.0, 1.0, 0.5)); + addParam(createParam(Vec(27, 222), module, VCA::LEVEL2_PARAM, 0.0, 1.0, 0.5)); addInput(createInput(Vec(11, 113), module, VCA::EXP1_INPUT)); addInput(createInput(Vec(54, 113), module, VCA::LIN1_INPUT)); diff --git a/src/VCF.cpp b/src/VCF.cpp index 9318237..559864f 100644 --- a/src/VCF.cpp +++ b/src/VCF.cpp @@ -153,11 +153,11 @@ VCFWidget::VCFWidget() { addChild(createScrew(Vec(15, 365))); addChild(createScrew(Vec(box.size.x-30, 365))); - addParam(createParam(Vec(33, 61), module, VCF::FREQ_PARAM, 0.0, 1.0, 0.5)); - addParam(createParam(Vec(12, 143), module, VCF::FINE_PARAM, 0.0, 1.0, 0.5)); - addParam(createParam(Vec(71, 143), module, VCF::RES_PARAM, 0.0, 1.0, 0.0)); - addParam(createParam(Vec(12, 208), module, VCF::FREQ_CV_PARAM, -1.0, 1.0, 0.0)); - addParam(createParam(Vec(71, 208), module, VCF::DRIVE_PARAM, 0.0, 1.0, 0.0)); + addParam(createParam(Vec(33, 61), module, VCF::FREQ_PARAM, 0.0, 1.0, 0.5)); + addParam(createParam(Vec(12, 143), module, VCF::FINE_PARAM, 0.0, 1.0, 0.5)); + addParam(createParam(Vec(71, 143), module, VCF::RES_PARAM, 0.0, 1.0, 0.0)); + addParam(createParam(Vec(12, 208), module, VCF::FREQ_CV_PARAM, -1.0, 1.0, 0.0)); + addParam(createParam(Vec(71, 208), module, VCF::DRIVE_PARAM, 0.0, 1.0, 0.0)); addInput(createInput(Vec(10, 276), module, VCF::FREQ_INPUT)); addInput(createInput(Vec(48, 276), module, VCF::RES_INPUT)); diff --git a/src/VCMixer.cpp b/src/VCMixer.cpp index 53ffb65..a0a40d1 100644 --- a/src/VCMixer.cpp +++ b/src/VCMixer.cpp @@ -63,10 +63,10 @@ VCMixerWidget::VCMixerWidget() { addChild(createScrew(Vec(15, 365))); addChild(createScrew(Vec(box.size.x-30, 365))); - addParam(createParam(Vec(48, 55), module, VCMixer::MIX_PARAM, 0.0, 1.0, 0.5)); - addParam(createParam(Vec(57, 139), module, VCMixer::CH1_PARAM, 0.0, 1.0, 0.0)); - addParam(createParam(Vec(57, 219), module, VCMixer::CH2_PARAM, 0.0, 1.0, 0.0)); - addParam(createParam(Vec(57, 300), module, VCMixer::CH3_PARAM, 0.0, 1.0, 0.0)); + addParam(createParam(Vec(52, 58), module, VCMixer::MIX_PARAM, 0.0, 1.0, 0.5)); + addParam(createParam(Vec(57, 139), module, VCMixer::CH1_PARAM, 0.0, 1.0, 0.0)); + addParam(createParam(Vec(57, 219), module, VCMixer::CH2_PARAM, 0.0, 1.0, 0.0)); + addParam(createParam(Vec(57, 300), module, VCMixer::CH3_PARAM, 0.0, 1.0, 0.0)); addInput(createInput(Vec(16, 69), module, VCMixer::MIX_CV_INPUT)); addInput(createInput(Vec(22, 129), module, VCMixer::CH1_INPUT)); diff --git a/src/VCO.cpp b/src/VCO.cpp index e75c1fe..56f7d97 100644 --- a/src/VCO.cpp +++ b/src/VCO.cpp @@ -17,14 +17,14 @@ struct VCO : Module { FINE_PARAM, FM_PARAM, PW_PARAM, - PW_CV_PARAM, + PWM_PARAM, NUM_PARAMS }; enum InputIds { PITCH_INPUT, FM_INPUT, - PW_INPUT, SYNC_INPUT, + PW_INPUT, NUM_INPUTS }; enum OutputIds { @@ -90,7 +90,7 @@ void VCO::step() { // Pulse width const float pwMin = 0.01; - float pw = clampf(params[PW_PARAM].value + params[PW_CV_PARAM].value * inputs[PW_INPUT].value / 10.0, pwMin, 1.0 - pwMin); + float pw = clampf(params[PW_PARAM].value + params[PWM_PARAM].value * inputs[PW_INPUT].value / 10.0, pwMin, 1.0 - pwMin); // Advance phase float deltaPhase = clampf(freq / gSampleRate, 1e-6, 0.5); @@ -205,7 +205,7 @@ VCOWidget::VCOWidget() { { SVGPanel *panel = new SVGPanel(); panel->box.size = box.size; - panel->setBackground(SVG::load(assetPlugin(plugin, "res/VCO.svg"))); + panel->setBackground(SVG::load(assetPlugin(plugin, "res/VCO-1.svg"))); addChild(panel); } @@ -215,13 +215,13 @@ VCOWidget::VCOWidget() { addChild(createScrew(Vec(box.size.x-30, 365))); addParam(createParam(Vec(15, 77), module, VCO::MODE_PARAM, 0.0, 1.0, 1.0)); - addParam(createParam(Vec(120, 77), module, VCO::SYNC_PARAM, 0.0, 1.0, 1.0)); + addParam(createParam(Vec(119, 77), module, VCO::SYNC_PARAM, 0.0, 1.0, 1.0)); - addParam(createParam(Vec(48, 61), module, VCO::FREQ_PARAM, -54.0, 54.0, 0.0)); - addParam(createParam(Vec(23, 143), module, VCO::FINE_PARAM, -1.0, 1.0, 0.0)); - addParam(createParam(Vec(91, 143), module, VCO::PW_PARAM, 0.0, 1.0, 0.5)); - addParam(createParam(Vec(23, 208), module, VCO::FM_PARAM, 0.0, 1.0, 0.0)); - addParam(createParam(Vec(91, 208), module, VCO::PW_CV_PARAM, 0.0, 1.0, 0.0)); + addParam(createParam(Vec(47, 61), module, VCO::FREQ_PARAM, -54.0, 54.0, 0.0)); + addParam(createParam(Vec(23, 143), module, VCO::FINE_PARAM, -1.0, 1.0, 0.0)); + addParam(createParam(Vec(91, 143), module, VCO::PW_PARAM, 0.0, 1.0, 0.5)); + addParam(createParam(Vec(23, 208), module, VCO::FM_PARAM, 0.0, 1.0, 0.0)); + addParam(createParam(Vec(91, 208), module, VCO::PWM_PARAM, 0.0, 1.0, 0.0)); addInput(createInput(Vec(11, 276), module, VCO::PITCH_INPUT)); addInput(createInput(Vec(45, 276), module, VCO::FM_INPUT));