diff --git a/plugin.json b/plugin.json index d22bc90..001182b 100644 --- a/plugin.json +++ b/plugin.json @@ -75,8 +75,8 @@ }, { "slug": "LFO2", - "name": "LFO-2", - "description": "Low-frequency oscillator", + "name": "Wavetable LFO", + "description": "Low-frequency wavetable oscillator", "manualUrl": "https://vcvrack.com/Fundamental#LFO", "tags": [ "LFO", diff --git a/res/ADSR.svg b/res/ADSR.svg index 5f9feba..795dd28 100644 --- a/res/ADSR.svg +++ b/res/ADSR.svg @@ -1,297 +1,825 @@ - - - - + id="svg256" + sodipodi:docname="ADSR.svg" + inkscape:version="0.92.3 (2405546, 2018-03-11)"> + id="metadata260"> image/svg+xml - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + id="bb72ae90-dc3a-411d-92ac-8ad769c6f9d9" + data-name="components" + style="display:none" + inkscape:groupmode="layer"> + + + + + + + + + + + + + + + + + diff --git a/res/LFO-1.svg b/res/LFO-1.svg deleted file mode 100644 index 7509fc5..0000000 --- a/res/LFO-1.svg +++ /dev/null @@ -1,482 +0,0 @@ - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/res/LFO-2.svg b/res/LFO-2.svg deleted file mode 100644 index 7885fda..0000000 --- a/res/LFO-2.svg +++ /dev/null @@ -1,328 +0,0 @@ - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/res/LFO.svg b/res/LFO.svg new file mode 100644 index 0000000..95cbf36 --- /dev/null +++ b/res/LFO.svg @@ -0,0 +1,655 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/res/VCF.svg b/res/VCF.svg index eca2e87..fb69c93 100644 --- a/res/VCF.svg +++ b/res/VCF.svg @@ -1,6 +1,4 @@ - - - - + inkscape:version="0.92.3 (2405546, 2018-03-11)"> + id="metadata187"> image/svg+xml - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + id="baec97a9-4811-4dbc-ac5b-52231b9f4c1d" + data-name="FND TXT" + style="display:inline"> + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + id="g125"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/res/VCO-1.svg b/res/VCO-1.svg deleted file mode 100644 index fbcbe6c..0000000 --- a/res/VCO-1.svg +++ /dev/null @@ -1,467 +0,0 @@ - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/res/VCO-2.svg b/res/VCO-2.svg deleted file mode 100644 index c3e2536..0000000 --- a/res/VCO-2.svg +++ /dev/null @@ -1,327 +0,0 @@ - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/res/VCO.svg b/res/VCO.svg new file mode 100644 index 0000000..b55bf70 --- /dev/null +++ b/res/VCO.svg @@ -0,0 +1,631 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/res/WTLFO.svg b/res/WTLFO.svg new file mode 100644 index 0000000..c321881 --- /dev/null +++ b/res/WTLFO.svg @@ -0,0 +1,505 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/res/WTVCO.svg b/res/WTVCO.svg new file mode 100644 index 0000000..369a2e6 --- /dev/null +++ b/res/WTVCO.svg @@ -0,0 +1,535 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/ADSR.cpp b/src/ADSR.cpp index 99d9ce8..724bb6a 100644 --- a/src/ADSR.cpp +++ b/src/ADSR.cpp @@ -160,29 +160,32 @@ struct ADSRWidget : ModuleWidget { setModule(module); setPanel(createPanel(asset::plugin(pluginInstance, "res/ADSR.svg"))); - addChild(createWidget(Vec(15, 0))); - addChild(createWidget(Vec(box.size.x - 30, 0))); - addChild(createWidget(Vec(15, 365))); - addChild(createWidget(Vec(box.size.x - 30, 365))); - - addParam(createParam(Vec(62, 57), module, ADSR::ATTACK_PARAM)); - addParam(createParam(Vec(62, 124), module, ADSR::DECAY_PARAM)); - addParam(createParam(Vec(62, 191), module, ADSR::SUSTAIN_PARAM)); - addParam(createParam(Vec(62, 257), module, ADSR::RELEASE_PARAM)); - - addInput(createInput(Vec(9, 63), module, ADSR::ATTACK_INPUT)); - addInput(createInput(Vec(9, 129), module, ADSR::DECAY_INPUT)); - addInput(createInput(Vec(9, 196), module, ADSR::SUSTAIN_INPUT)); - addInput(createInput(Vec(9, 263), module, ADSR::RELEASE_INPUT)); - - addInput(createInput(Vec(9, 320), module, ADSR::GATE_INPUT)); - addInput(createInput(Vec(48, 320), module, ADSR::TRIG_INPUT)); - addOutput(createOutput(Vec(87, 320), module, ADSR::ENVELOPE_OUTPUT)); - - addChild(createLight>(Vec(94, 41), module, ADSR::ATTACK_LIGHT)); - addChild(createLight>(Vec(94, 109), module, ADSR::DECAY_LIGHT)); - addChild(createLight>(Vec(94, 175), module, ADSR::SUSTAIN_LIGHT)); - addChild(createLight>(Vec(94, 242), module, ADSR::RELEASE_LIGHT)); + addChild(createWidget(Vec(RACK_GRID_WIDTH, 0))); + addChild(createWidget(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0))); + addChild(createWidget(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); + addChild(createWidget(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); + + addParam(createParamCentered(mm2px(Vec(6.604, 55.454)), module, ADSR::ATTACK_PARAM)); + addParam(createParamCentered(mm2px(Vec(17.441, 55.454)), module, ADSR::DECAY_PARAM)); + addParam(createParamCentered(mm2px(Vec(28.279, 55.454)), module, ADSR::SUSTAIN_PARAM)); + addParam(createParamCentered(mm2px(Vec(39.116, 55.454)), module, ADSR::RELEASE_PARAM)); + // addParam(createParamCentered(mm2px(Vec(6.604, 80.603)), module, ADSR::ATTCV_PARAM)); + // addParam(createParamCentered(mm2px(Vec(17.441, 80.63)), module, ADSR::DECCV_PARAM)); + // addParam(createParamCentered(mm2px(Vec(28.279, 80.603)), module, ADSR::SUSCV_PARAM)); + // addParam(createParamCentered(mm2px(Vec(39.119, 80.603)), module, ADSR::RELCV_PARAM)); + // addParam(createParamCentered(mm2px(Vec(6.604, 113.115)), module, ADSR::PUSH_PARAM)); + + addInput(createInputCentered(mm2px(Vec(6.604, 96.882)), module, ADSR::ATTACK_INPUT)); + addInput(createInputCentered(mm2px(Vec(17.441, 96.859)), module, ADSR::DECAY_INPUT)); + addInput(createInputCentered(mm2px(Vec(28.279, 96.886)), module, ADSR::SUSTAIN_INPUT)); + addInput(createInputCentered(mm2px(Vec(39.119, 96.89)), module, ADSR::RELEASE_INPUT)); + addInput(createInputCentered(mm2px(Vec(17.441, 113.115)), module, ADSR::GATE_INPUT)); + addInput(createInputCentered(mm2px(Vec(28.279, 113.115)), module, ADSR::TRIG_INPUT)); + + addOutput(createOutputCentered(mm2px(Vec(39.119, 113.115)), module, ADSR::ENVELOPE_OUTPUT)); + + // mm2px(Vec(45.72, 21.219)) + // addChild(createWidget(mm2px(Vec(0.0, 13.039)))); } }; diff --git a/src/LFO.cpp b/src/LFO.cpp index fe49273..b591485 100644 --- a/src/LFO.cpp +++ b/src/LFO.cpp @@ -88,14 +88,14 @@ struct LFO : Module { INVERT_PARAM, FREQ_PARAM, FM1_PARAM, - FM2_PARAM, + FM2_PARAM, // removed PW_PARAM, PWM_PARAM, NUM_PARAMS }; enum InputIds { FM1_INPUT, - FM2_INPUT, + FM2_INPUT, // removed RESET_INPUT, PW_INPUT, NUM_INPUTS @@ -200,37 +200,34 @@ struct LFO : Module { }; - struct LFOWidget : ModuleWidget { LFOWidget(LFO* module) { setModule(module); - setPanel(createPanel(asset::plugin(pluginInstance, "res/LFO-1.svg"))); - - addChild(createWidget(Vec(15, 0))); - addChild(createWidget(Vec(box.size.x - 30, 0))); - addChild(createWidget(Vec(15, 365))); - addChild(createWidget(Vec(box.size.x - 30, 365))); - - addParam(createParam(Vec(15, 77), module, LFO::OFFSET_PARAM)); - addParam(createParam(Vec(119, 77), module, LFO::INVERT_PARAM)); - - addParam(createParam(Vec(47, 61), module, LFO::FREQ_PARAM)); - addParam(createParam(Vec(23, 143), module, LFO::FM1_PARAM)); - addParam(createParam(Vec(91, 143), module, LFO::PW_PARAM)); - addParam(createParam(Vec(23, 208), module, LFO::FM2_PARAM)); - addParam(createParam(Vec(91, 208), module, LFO::PWM_PARAM)); - - 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(createLight>(Vec(99, 42.5f), module, LFO::PHASE_LIGHT)); + setPanel(createPanel(asset::plugin(pluginInstance, "res/LFO.svg"))); + + addChild(createWidget(Vec(RACK_GRID_WIDTH, 0))); + addChild(createWidget(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0))); + addChild(createWidget(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); + addChild(createWidget(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); + + addParam(createParamCentered(mm2px(Vec(22.902, 29.803)), module, LFO::FREQ_PARAM)); + addParam(createParamCentered(mm2px(Vec(22.861, 56.388)), module, LFO::PW_PARAM)); + addParam(createParamCentered(mm2px(Vec(6.604, 80.603)), module, LFO::FM1_PARAM)); + // addParam(createParamCentered(mm2px(Vec(17.441, 80.603)), module, LFO::INV_PARAM)); + // addParam(createParamCentered(mm2px(Vec(28.279, 80.603)), module, LFO::OFST_PARAM)); + addParam(createParamCentered(mm2px(Vec(39.116, 80.603)), module, LFO::PWM_PARAM)); + + addInput(createInputCentered(mm2px(Vec(6.604, 96.859)), module, LFO::FM1_INPUT)); + // addInput(createInputCentered(mm2px(Vec(17.441, 96.859)), module, LFO::CLK_INPUT)); + addInput(createInputCentered(mm2px(Vec(28.279, 96.819)), module, LFO::RESET_INPUT)); + addInput(createInputCentered(mm2px(Vec(39.116, 96.819)), module, LFO::PW_INPUT)); + + addOutput(createOutputCentered(mm2px(Vec(6.604, 113.115)), module, LFO::SIN_OUTPUT)); + addOutput(createOutputCentered(mm2px(Vec(17.441, 113.115)), module, LFO::TRI_OUTPUT)); + addOutput(createOutputCentered(mm2px(Vec(28.279, 113.115)), module, LFO::SAW_OUTPUT)); + addOutput(createOutputCentered(mm2px(Vec(39.116, 113.115)), module, LFO::SQR_OUTPUT)); + + addChild(createLightCentered>(mm2px(Vec(31.085, 16.428)), module, LFO::PHASE_LIGHT)); } }; @@ -238,6 +235,7 @@ struct LFOWidget : ModuleWidget { Model* modelLFO = createModel("LFO"); +#if 0 struct LFO2 : Module { enum ParamIds { OFFSET_PARAM, @@ -335,7 +333,7 @@ struct LFO2 : Module { struct LFO2Widget : ModuleWidget { LFO2Widget(LFO2* module) { setModule(module); - setPanel(createPanel(asset::plugin(pluginInstance, "res/LFO-2.svg"))); + setPanel(createPanel(asset::plugin(pluginInstance, "res/WTLFO.svg"))); addChild(createWidget(Vec(15, 0))); addChild(createWidget(Vec(box.size.x - 30, 0))); @@ -361,3 +359,4 @@ struct LFO2Widget : ModuleWidget { Model* modelLFO2 = createModel("LFO2"); +#endif \ No newline at end of file diff --git a/src/VCF.cpp b/src/VCF.cpp index 40f546e..de1a0a9 100644 --- a/src/VCF.cpp +++ b/src/VCF.cpp @@ -68,7 +68,7 @@ static const int UPSAMPLE = 2; struct VCF : Module { enum ParamIds { FREQ_PARAM, - FINE_PARAM, + FINE_PARAM, // removed RES_PARAM, FREQ_CV_PARAM, DRIVE_PARAM, @@ -199,24 +199,25 @@ struct VCFWidget : ModuleWidget { setModule(module); setPanel(createPanel(asset::plugin(pluginInstance, "res/VCF.svg"))); - addChild(createWidget(Vec(15, 0))); - addChild(createWidget(Vec(box.size.x - 30, 0))); - addChild(createWidget(Vec(15, 365))); - addChild(createWidget(Vec(box.size.x - 30, 365))); - - addParam(createParam(Vec(33, 61), module, VCF::FREQ_PARAM)); - addParam(createParam(Vec(12, 143), module, VCF::FINE_PARAM)); - addParam(createParam(Vec(71, 143), module, VCF::RES_PARAM)); - addParam(createParam(Vec(12, 208), module, VCF::FREQ_CV_PARAM)); - addParam(createParam(Vec(71, 208), module, VCF::DRIVE_PARAM)); - - addInput(createInput(Vec(10, 276), module, VCF::FREQ_INPUT)); - addInput(createInput(Vec(48, 276), module, VCF::RES_INPUT)); - addInput(createInput(Vec(85, 276), module, VCF::DRIVE_INPUT)); - addInput(createInput(Vec(10, 320), module, VCF::IN_INPUT)); - - addOutput(createOutput(Vec(48, 320), module, VCF::LPF_OUTPUT)); - addOutput(createOutput(Vec(85, 320), module, VCF::HPF_OUTPUT)); + addChild(createWidget(Vec(RACK_GRID_WIDTH, 0))); + addChild(createWidget(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0))); + addChild(createWidget(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); + addChild(createWidget(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); + + addParam(createParamCentered(mm2px(Vec(17.587, 29.808)), module, VCF::FREQ_PARAM)); + addParam(createParamCentered(mm2px(Vec(8.895, 56.388)), module, VCF::RES_PARAM)); + addParam(createParamCentered(mm2px(Vec(26.665, 56.388)), module, VCF::DRIVE_PARAM)); + addParam(createParamCentered(mm2px(Vec(6.996, 80.603)), module, VCF::FREQ_CV_PARAM)); + // addParam(createParamCentered(mm2px(Vec(17.833, 80.603)), module, VCF::RESCV_PARAM)); + // addParam(createParamCentered(mm2px(Vec(28.67, 80.603)), module, VCF::DRIVE_CV_PARAM)); + + addInput(createInputCentered(mm2px(Vec(6.996, 96.813)), module, VCF::FREQ_INPUT)); + addInput(createInputCentered(mm2px(Vec(17.833, 96.813)), module, VCF::RES_INPUT)); + addInput(createInputCentered(mm2px(Vec(28.67, 96.813)), module, VCF::DRIVE_INPUT)); + addInput(createInputCentered(mm2px(Vec(6.996, 113.115)), module, VCF::IN_INPUT)); + + addOutput(createOutputCentered(mm2px(Vec(17.833, 113.115)), module, VCF::LPF_OUTPUT)); + addOutput(createOutputCentered(mm2px(Vec(28.67, 113.115)), module, VCF::HPF_OUTPUT)); } }; diff --git a/src/VCO.cpp b/src/VCO.cpp index b1ddc9f..b075933 100644 --- a/src/VCO.cpp +++ b/src/VCO.cpp @@ -250,10 +250,11 @@ struct VCO : Module { MODE_PARAM, SYNC_PARAM, FREQ_PARAM, - FINE_PARAM, + FINE_PARAM, // removed FM_PARAM, PW_PARAM, PWM_PARAM, + LINEAR_PARAM, NUM_PARAMS }; enum InputIds { @@ -360,40 +361,38 @@ struct VCO : Module { struct VCOWidget : ModuleWidget { VCOWidget(VCO* module) { setModule(module); - setPanel(createPanel(asset::plugin(pluginInstance, "res/VCO-1.svg"))); - - addChild(createWidget(Vec(15, 0))); - addChild(createWidget(Vec(box.size.x - 30, 0))); - addChild(createWidget(Vec(15, 365))); - addChild(createWidget(Vec(box.size.x - 30, 365))); - - addParam(createParam(Vec(15, 77), module, VCO::MODE_PARAM)); - addParam(createParam(Vec(119, 77), module, VCO::SYNC_PARAM)); - - addParam(createParam(Vec(47, 61), module, VCO::FREQ_PARAM)); - addParam(createParam(Vec(23, 143), module, VCO::FINE_PARAM)); - addParam(createParam(Vec(91, 143), module, VCO::PW_PARAM)); - addParam(createParam(Vec(23, 208), module, VCO::FM_PARAM)); - addParam(createParam(Vec(91, 208), module, VCO::PWM_PARAM)); - - addInput(createInput(Vec(11, 276), module, VCO::PITCH_INPUT)); - addInput(createInput(Vec(45, 276), module, VCO::FM_INPUT)); - addInput(createInput(Vec(80, 276), module, VCO::SYNC_INPUT)); - addInput(createInput(Vec(114, 276), module, VCO::PW_INPUT)); - - addOutput(createOutput(Vec(11, 320), module, VCO::SIN_OUTPUT)); - addOutput(createOutput(Vec(45, 320), module, VCO::TRI_OUTPUT)); - addOutput(createOutput(Vec(80, 320), module, VCO::SAW_OUTPUT)); - addOutput(createOutput(Vec(114, 320), module, VCO::SQR_OUTPUT)); - - addChild(createLight>(Vec(99, 42.5f), module, VCO::PHASE_LIGHT)); + setPanel(createPanel(asset::plugin(pluginInstance, "res/VCO.svg"))); + + addChild(createWidget(Vec(RACK_GRID_WIDTH, 0))); + addChild(createWidget(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0))); + addChild(createWidget(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); + addChild(createWidget(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); + + addParam(createParamCentered(mm2px(Vec(22.905, 29.808)), module, VCO::FREQ_PARAM)); + addParam(createParamCentered(mm2px(Vec(22.862, 56.388)), module, VCO::PW_PARAM)); + addParam(createParamCentered(mm2px(Vec(6.607, 80.603)), module, VCO::FM_PARAM)); + addParam(createParamCentered(mm2px(Vec(17.444, 80.603)), module, VCO::LINEAR_PARAM)); + addParam(createParamCentered(mm2px(Vec(28.282, 80.603)), module, VCO::SYNC_PARAM)); + addParam(createParamCentered(mm2px(Vec(39.118, 80.603)), module, VCO::PWM_PARAM)); + + addInput(createInputCentered(mm2px(Vec(6.607, 96.859)), module, VCO::FM_INPUT)); + addInput(createInputCentered(mm2px(Vec(17.444, 96.859)), module, VCO::PITCH_INPUT)); + addInput(createInputCentered(mm2px(Vec(28.282, 96.859)), module, VCO::SYNC_INPUT)); + addInput(createInputCentered(mm2px(Vec(39.15, 96.859)), module, VCO::PW_INPUT)); + + addOutput(createOutputCentered(mm2px(Vec(6.607, 113.115)), module, VCO::SIN_OUTPUT)); + addOutput(createOutputCentered(mm2px(Vec(17.444, 113.115)), module, VCO::TRI_OUTPUT)); + addOutput(createOutputCentered(mm2px(Vec(28.282, 113.115)), module, VCO::SAW_OUTPUT)); + addOutput(createOutputCentered(mm2px(Vec(39.119, 113.115)), module, VCO::SQR_OUTPUT)); + + addChild(createLightCentered>(mm2px(Vec(31.089, 16.428)), module, VCO::PHASE_LIGHT)); } }; Model* modelVCO = createModel("VCO"); - +#if 0 struct VCO2 : Module { enum ParamIds { MODE_PARAM, @@ -493,7 +492,7 @@ struct VCO2 : Module { struct VCO2Widget : ModuleWidget { VCO2Widget(VCO2* module) { setModule(module); - setPanel(createPanel(asset::plugin(pluginInstance, "res/VCO-2.svg"))); + setPanel(createPanel(asset::plugin(pluginInstance, "res/WTVCO.svg"))); addChild(createWidget(Vec(15, 0))); addChild(createWidget(Vec(box.size.x - 30, 0))); @@ -519,3 +518,4 @@ struct VCO2Widget : ModuleWidget { Model* modelVCO2 = createModel("VCO2"); +#endif \ No newline at end of file diff --git a/src/WTLFO.cpp b/src/WTLFO.cpp new file mode 100644 index 0000000..0779b69 --- /dev/null +++ b/src/WTLFO.cpp @@ -0,0 +1,235 @@ +#include "plugin.hpp" + + +using simd::float_4; + + +struct WTLFO : Module { + enum ParamId { + OFFSET_PARAM, + INVERT_PARAM, + FREQ_PARAM, + POS_PARAM, + FM_PARAM, + // added in 2.0 + POS_CV_PARAM, + NUM_PARAMS + }; + enum InputId { + FM_INPUT, + RESET_INPUT, + POS_INPUT, + // added in 2.0 + CLOCK_INPUT, + NUM_INPUTS + }; + enum OutputId { + WAVE_OUTPUT, + NUM_OUTPUTS + }; + enum LightId { + ENUMS(PHASE_LIGHT, 3), + OFFSET_LIGHT, + INVERT_LIGHT, + NUM_LIGHTS + }; + + dsp::ClockDivider lightDivider; + // All waves concatenated + std::vector wavetable; + // Number of points in each wave + size_t waveLen = 0; + bool offset = false; + bool invert = false; + + float_4 phases[4] = {}; + dsp::BooleanTrigger offsetTrigger; + dsp::BooleanTrigger invertTrigger; + + WTLFO() { + config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS); + // TODO Change to momentary with backward compatibility in fromJson() + configButton(OFFSET_PARAM, "Offset 0-10V"); + configButton(INVERT_PARAM, "Invert wave"); + configParam(FREQ_PARAM, -8.f, 10.f, 1.f, "Frequency", " Hz", 2, 1); + configParam(POS_PARAM, 0.f, 1.f, 0.f, "Wavetable position", "%", 0.f, 100.f); + configParam(FM_PARAM, -1.f, 1.f, 0.f, "Frequency modulation", "%", 0.f, 100.f); + configParam(POS_CV_PARAM, -1.f, 1.f, 0.f, "Wavetable position CV", "%", 0.f, 100.f); + configInput(FM_INPUT, "Frequency modulation"); + configInput(RESET_INPUT, "Reset"); + configInput(POS_INPUT, "Wavetable position"); + configInput(CLOCK_INPUT, "Clock"); + configOutput(WAVE_OUTPUT, "Wavetable"); + configLight(PHASE_LIGHT, "Phase"); + + lightDivider.setDivision(16); + onReset(ResetEvent()); + } + + void onReset(const ResetEvent& e) override { + Module::onReset(e); + + // Build geometric waveforms + wavetable.clear(); + waveLen = 1024; + wavetable.resize(waveLen * 4); + + for (size_t i = 0; i < waveLen; i++) { + float p = float(i) / waveLen; + float sin = std::sin(2 * float(M_PI) * p); + wavetable[i + 0 * waveLen] = sin; + float tri = (p < 0.25f) ? 4*p : (p < 0.75f) ? 2 - 4*p : 4*p - 4; + wavetable[i + 1 * waveLen] = tri; + float saw = (p < 0.5f) ? 2*p : 2*p - 2; + wavetable[i + 2 * waveLen] = saw; + float sqr = (p < 0.5f) ? 1 : -1; + wavetable[i + 3 * waveLen] = sqr; + } + + // Reset state + for (int c = 0; c < 16; c += 4) { + phases[c / 4] = 0.f; + } + } + + void clearOutput() { + outputs[WAVE_OUTPUT].setVoltage(0.f); + outputs[WAVE_OUTPUT].setChannels(1); + lights[PHASE_LIGHT + 0].setBrightness(0.f); + lights[PHASE_LIGHT + 1].setBrightness(0.f); + lights[PHASE_LIGHT + 2].setBrightness(0.f); + } + + void process(const ProcessArgs& args) override { + float freqParam = params[FREQ_PARAM].getValue(); + float fmParam = params[FM_PARAM].getValue(); + float posParam = params[POS_PARAM].getValue(); + float posCvParam = params[POS_CV_PARAM].getValue(); + + if (offsetTrigger.process(params[OFFSET_PARAM].getValue() > 0.f)) + offset ^= true; + if (invertTrigger.process(params[INVERT_PARAM].getValue() > 0.f)) + invert ^= true; + + int channels = std::max(1, inputs[FM_INPUT].getChannels()); + + // Check valid wave and wavetable size + if (waveLen < 2) { + clearOutput(); + return; + } + int wavetableNum = wavetable.size() / waveLen; + if (wavetableNum < 1) { + clearOutput(); + return; + } + + for (int c = 0; c < channels; c += 4) { + // Calculate frequency in Hz + float_4 pitch = freqParam + inputs[FM_INPUT].getVoltageSimd(c) * fmParam; + float_4 freq = simd::pow(2.f, pitch); + + // Accumulate phase + float_4 phase = phases[c / 4]; + phase += freq * args.sampleTime; + // Wrap phase + phase -= simd::trunc(phase); + phases[c / 4] = phase; + // Scale phase from 0 to waveLen + phase *= waveLen; + + // Get wavetable position, scaled from 0 to (wavetableNum - 1) + float_4 pos = posParam + inputs[POS_INPUT].getPolyVoltageSimd(c) * posCvParam / 10.f; + pos = simd::clamp(pos); + pos *= (wavetableNum - 1); + + // Get wavetable points + float_4 out = 0.f; + for (int i = 0; i < 4 && c + i < channels; i++) { + // Get wave indexes + float phaseF = phase[i] - std::trunc(phase[i]); + size_t i0 = std::trunc(phase[i]); + size_t i1 = (i0 + 1) % waveLen; + // Get pos indexes + float posF = pos[0] - std::trunc(pos[0]); + size_t pos0 = std::trunc(pos[0]); + size_t pos1 = pos0 + 1; + // Get waves + float out0 = crossfade(wavetable[i0 + pos0 * waveLen], wavetable[i1 + pos0 * waveLen], phaseF); + if (posF > 0.f) { + float out1 = crossfade(wavetable[i0 + pos1 * waveLen], wavetable[i1 + pos1 * waveLen], phaseF); + out[i] = crossfade(out0, out1, posF); + } + else { + out[i] = out0; + } + } + + // Invert and offset + if (invert) + out *= -1.f; + if (offset) + out += 1.f; + + outputs[WAVE_OUTPUT].setVoltageSimd(out * 5.f, c); + } + + outputs[WAVE_OUTPUT].setChannels(channels); + + // Light + if (lightDivider.process()) { + if (channels == 1) { + float b = 1.f - phases[0][0]; + lights[PHASE_LIGHT + 0].setSmoothBrightness(b, args.sampleTime * lightDivider.getDivision()); + lights[PHASE_LIGHT + 1].setBrightness(0.f); + lights[PHASE_LIGHT + 2].setBrightness(0.f); + } + else { + lights[PHASE_LIGHT + 0].setBrightness(0.f); + lights[PHASE_LIGHT + 1].setBrightness(0.f); + lights[PHASE_LIGHT + 2].setBrightness(1.f); + } + } + } +}; + + +struct WTLFODisplay : LedDisplay { + WTLFODisplay() { + box.size = mm2px(Vec(35.56, 29.224)); + } +}; + + +struct WTLFOWidget : ModuleWidget { + WTLFOWidget(WTLFO* module) { + setModule(module); + setPanel(createPanel(asset::plugin(pluginInstance, "res/WTLFO.svg"))); + + addChild(createWidget(Vec(RACK_GRID_WIDTH, 0))); + addChild(createWidget(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0))); + addChild(createWidget(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); + addChild(createWidget(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); + + addParam(createParamCentered(mm2px(Vec(8.913, 56.388)), module, WTLFO::FREQ_PARAM)); + addParam(createParamCentered(mm2px(Vec(26.647, 56.388)), module, WTLFO::POS_PARAM)); + addParam(createParamCentered(mm2px(Vec(6.987, 80.603)), module, WTLFO::FM_PARAM)); + addParam(createLightParamCentered>(mm2px(Vec(17.824, 80.517)), module, WTLFO::INVERT_PARAM, WTLFO::INVERT_LIGHT)); + addParam(createParamCentered(mm2px(Vec(28.662, 80.536)), module, WTLFO::POS_CV_PARAM)); + addParam(createLightParamCentered>(mm2px(Vec(17.824, 96.859)), module, WTLFO::OFFSET_PARAM, WTLFO::OFFSET_LIGHT)); + + addInput(createInputCentered(mm2px(Vec(6.987, 96.859)), module, WTLFO::FM_INPUT)); + addInput(createInputCentered(mm2px(Vec(28.662, 96.859)), module, WTLFO::POS_INPUT)); + addInput(createInputCentered(mm2px(Vec(6.987, 113.115)), module, WTLFO::CLOCK_INPUT)); + addInput(createInputCentered(mm2px(Vec(17.824, 113.115)), module, WTLFO::RESET_INPUT)); + + addOutput(createOutputCentered(mm2px(Vec(28.662, 113.115)), module, WTLFO::WAVE_OUTPUT)); + + addChild(createLightCentered>(mm2px(Vec(17.731, 49.409)), module, WTLFO::PHASE_LIGHT)); + + addChild(createWidget(mm2px(Vec(0.004, 13.04)))); + } +}; + + +Model* modelLFO2 = createModel("LFO2"); \ No newline at end of file diff --git a/src/WTVCO.cpp b/src/WTVCO.cpp new file mode 100644 index 0000000..4a5b75a --- /dev/null +++ b/src/WTVCO.cpp @@ -0,0 +1,136 @@ +#include "plugin.hpp" + + +struct WTVCO : Module { + enum ParamIds { + MODE_PARAM, // removed + SYNC_PARAM, + FREQ_PARAM, + WAVE_PARAM, + FM_PARAM, + NUM_PARAMS + }; + enum InputIds { + FM_INPUT, + SYNC_INPUT, + WAVE_INPUT, + NUM_INPUTS + }; + enum OutputIds { + OUT_OUTPUT, + NUM_OUTPUTS + }; + enum LightIds { + ENUMS(PHASE_LIGHT, 3), + NUM_LIGHTS + }; + + // VoltageControlledOscillator<8, 8, float_4> oscillators[4]; + dsp::ClockDivider lightDivider; + + WTVCO() { + config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS); + configSwitch(MODE_PARAM, 0.f, 1.f, 1.f, "Engine mode", {"Digital", "Analog"}); + configSwitch(SYNC_PARAM, 0.f, 1.f, 1.f, "Sync mode", {"Soft", "Hard"}); + configParam(FREQ_PARAM, -54.f, 54.f, 0.f, "Frequency", " Hz", dsp::FREQ_SEMITONE, dsp::FREQ_C4); + configParam(WAVE_PARAM, 0.f, 3.f, 1.5f, "Wave"); + configParam(FM_PARAM, 0.f, 1.f, 1.f, "Frequency modulation", "%", 0.f, 100.f); + configInput(FM_INPUT, "Frequency modulation"); + configInput(SYNC_INPUT, "Sync"); + configInput(WAVE_INPUT, "Wave type"); + configOutput(OUT_OUTPUT, "Audio"); + + lightDivider.setDivision(16); + } + + void process(const ProcessArgs& args) override { + // float freqParam = params[FREQ_PARAM].getValue() / 12.f; + // float fmParam = dsp::quadraticBipolar(params[FM_PARAM].getValue()); + // float waveParam = params[WAVE_PARAM].getValue(); + + // int channels = std::max(inputs[FM_INPUT].getChannels(), 1); + + // for (int c = 0; c < channels; c += 4) { + // auto* oscillator = &oscillators[c / 4]; + // oscillator->channels = std::min(channels - c, 4); + // oscillator->analog = (params[MODE_PARAM].getValue() > 0.f); + // oscillator->soft = (params[SYNC_PARAM].getValue() <= 0.f); + + // float_4 pitch = freqParam; + // pitch += fmParam * inputs[FM_INPUT].getVoltageSimd(c); + // oscillator->setPitch(pitch); + + // oscillator->syncEnabled = inputs[SYNC_INPUT].isConnected(); + // oscillator->process(args.sampleTime, inputs[SYNC_INPUT].getPolyVoltageSimd(c)); + + // // Outputs + // if (outputs[OUT_OUTPUT].isConnected()) { + // float_4 wave = simd::clamp(waveParam + inputs[WAVE_INPUT].getPolyVoltageSimd(c) / 10.f * 3.f, 0.f, 3.f); + // float_4 v = 0.f; + // v += oscillator->sin() * simd::fmax(0.f, 1.f - simd::fabs(wave - 0.f)); + // v += oscillator->tri() * simd::fmax(0.f, 1.f - simd::fabs(wave - 1.f)); + // v += oscillator->saw() * simd::fmax(0.f, 1.f - simd::fabs(wave - 2.f)); + // v += oscillator->sqr() * simd::fmax(0.f, 1.f - simd::fabs(wave - 3.f)); + // outputs[OUT_OUTPUT].setVoltageSimd(5.f * v, c); + // } + // } + + // outputs[OUT_OUTPUT].setChannels(channels); + + // // Light + // if (lightDivider.process()) { + // if (channels == 1) { + // float lightValue = oscillators[0].light()[0]; + // lights[PHASE_LIGHT + 0].setSmoothBrightness(-lightValue, args.sampleTime * lightDivider.getDivision()); + // lights[PHASE_LIGHT + 1].setSmoothBrightness(lightValue, args.sampleTime * lightDivider.getDivision()); + // lights[PHASE_LIGHT + 2].setBrightness(0.f); + // } + // else { + // lights[PHASE_LIGHT + 0].setBrightness(0.f); + // lights[PHASE_LIGHT + 1].setBrightness(0.f); + // lights[PHASE_LIGHT + 2].setBrightness(1.f); + // } + // } + } +}; + + +struct WTVCODisplay : LedDisplay { + WTVCODisplay() { + box.size = mm2px(Vec(35.56, 29.021)); + } +}; + + +struct WTVCOWidget : ModuleWidget { + WTVCOWidget(WTVCO* module) { + setModule(module); + setPanel(createPanel(asset::plugin(pluginInstance, "res/WTVCO.svg"))); + + addChild(createWidget(Vec(RACK_GRID_WIDTH, 0))); + addChild(createWidget(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0))); + addChild(createWidget(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); + addChild(createWidget(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); + + addParam(createParamCentered(mm2px(Vec(8.915, 56.388)), module, WTVCO::FREQ_PARAM)); + addParam(createParamCentered(mm2px(Vec(26.645, 56.388)), module, WTVCO::WAVE_PARAM)); + addParam(createParamCentered(mm2px(Vec(6.897, 80.603)), module, WTVCO::FM_PARAM)); + // addParam(createParamCentered(mm2px(Vec(17.734, 80.603)), module, WTVCO::LFM_PARAM)); + // addParam(createParamCentered(mm2px(Vec(28.571, 80.603)), module, WTVCO::POSCV_PARAM)); + addParam(createParamCentered(mm2px(Vec(17.734, 96.859)), module, WTVCO::SYNC_PARAM)); + + addInput(createInputCentered(mm2px(Vec(6.897, 96.813)), module, WTVCO::FM_INPUT)); + addInput(createInputCentered(mm2px(Vec(28.571, 96.859)), module, WTVCO::WAVE_INPUT)); + // addInput(createInputCentered(mm2px(Vec(6.897, 113.115)), module, WTVCO::PITCH_INPUT)); + addInput(createInputCentered(mm2px(Vec(17.734, 113.115)), module, WTVCO::SYNC_INPUT)); + + addOutput(createOutputCentered(mm2px(Vec(28.571, 113.115)), module, WTVCO::OUT_OUTPUT)); + + addChild(createLightCentered>(mm2px(Vec(17.733, 49.409)), module, WTVCO::PHASE_LIGHT)); + + addChild(createWidget(mm2px(Vec(0.0, 13.039)))); + } +}; + + +Model* modelVCO2 = createModel("VCO2"); \ No newline at end of file