From 607fefa9d8cd7763066af3d6911946076f4105cb Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Mon, 1 Nov 2021 02:15:14 -0400 Subject: [PATCH 1/5] Fix loading of Wavetable if file is not found. --- src/WTLFO.cpp | 131 +++++++++++++++++++++------------------------- src/Wavetable.hpp | 6 ++- 2 files changed, 65 insertions(+), 72 deletions(-) diff --git a/src/WTLFO.cpp b/src/WTLFO.cpp index d5ca482..494c1ae 100644 --- a/src/WTLFO.cpp +++ b/src/WTLFO.cpp @@ -119,14 +119,6 @@ struct WTLFO : Module { } } - 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 { if (offsetTrigger.process(params[OFFSET_PARAM].getValue() > 0.f)) offset ^= true; @@ -158,72 +150,71 @@ struct WTLFO : Module { float posCvParam = params[POS_CV_PARAM].getValue(); // Check valid wave and wavetable size - if (wavetable.waveLen < 2) { - clearOutput(); - return; - } int waveCount = wavetable.getWaveCount(); - if (waveCount < 1) { - clearOutput(); - return; - } - - // Iterate channels - 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 = clockFreq / 2.f * dsp::approxExp2_taylor5(pitch + 30.f) / std::pow(2.f, 30.f); - freq = simd::fmin(freq, 1024.f); - - // Accumulate phase - float_4 phase = phases[c / 4]; - phase += freq * args.sampleTime; - // Wrap phase - phase -= simd::trunc(phase); - // Reset phase - float_4 reset = resetTriggers[c / 4].process(simd::rescale(inputs[RESET_INPUT].getPolyVoltageSimd(c), 0.1f, 2.f, 0.f, 1.f)); - phase = simd::ifelse(reset, 0.f, phase); - phases[c / 4] = phase; - // Scale phase from 0 to waveLen - phase *= wavetable.waveLen; - - // Get wavetable position, scaled from 0 to (waveCount - 1) - float_4 pos = posParam + inputs[POS_INPUT].getPolyVoltageSimd(c) * posCvParam / 10.f; - pos = simd::clamp(pos); - pos *= (waveCount - 1); - - if (c == 0) - lastPos = pos[0]; - - // Get wavetable points - float_4 out = 0.f; - for (int cc = 0; cc < 4 && c + cc < channels; cc++) { - // Get wave indexes - float phaseF = phase[cc] - std::trunc(phase[cc]); - size_t i0 = std::trunc(phase[cc]); - size_t i1 = (i0 + 1) % wavetable.waveLen; - // Get pos indexes - float posF = pos[cc] - std::trunc(pos[cc]); - size_t pos0 = std::trunc(pos[cc]); - size_t pos1 = pos0 + 1; - // Get waves - float out0 = crossfade(wavetable.at(pos0, i0), wavetable.at(pos0, i1), phaseF); - if (posF > 0.f) { - float out1 = crossfade(wavetable.at(pos1, i0), wavetable.at(pos1, i1), phaseF); - out[cc] = crossfade(out0, out1, posF); - } - else { - out[cc] = out0; + if (wavetable.waveLen >= 2 && waveCount >= 1) { + // Iterate channels + 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 = clockFreq / 2.f * dsp::approxExp2_taylor5(pitch + 30.f) / std::pow(2.f, 30.f); + freq = simd::fmin(freq, 1024.f); + + // Accumulate phase + float_4 phase = phases[c / 4]; + phase += freq * args.sampleTime; + // Wrap phase + phase -= simd::trunc(phase); + // Reset phase + float_4 reset = resetTriggers[c / 4].process(simd::rescale(inputs[RESET_INPUT].getPolyVoltageSimd(c), 0.1f, 2.f, 0.f, 1.f)); + phase = simd::ifelse(reset, 0.f, phase); + phases[c / 4] = phase; + // Scale phase from 0 to waveLen + phase *= wavetable.waveLen; + + // Get wavetable position, scaled from 0 to (waveCount - 1) + float_4 pos = posParam + inputs[POS_INPUT].getPolyVoltageSimd(c) * posCvParam / 10.f; + pos = simd::clamp(pos); + pos *= (waveCount - 1); + + if (c == 0) + lastPos = pos[0]; + + // Get wavetable points + float_4 out = 0.f; + for (int cc = 0; cc < 4 && c + cc < channels; cc++) { + // Get wave indexes + float phaseF = phase[cc] - std::trunc(phase[cc]); + size_t i0 = std::trunc(phase[cc]); + size_t i1 = (i0 + 1) % wavetable.waveLen; + // Get pos indexes + float posF = pos[cc] - std::trunc(pos[cc]); + size_t pos0 = std::trunc(pos[cc]); + size_t pos1 = pos0 + 1; + // Get waves + float out0 = crossfade(wavetable.at(pos0, i0), wavetable.at(pos0, i1), phaseF); + if (posF > 0.f) { + float out1 = crossfade(wavetable.at(pos1, i0), wavetable.at(pos1, i1), phaseF); + out[cc] = crossfade(out0, out1, posF); + } + else { + out[cc] = out0; + } } - } - // Invert and offset - if (invert) - out *= -1.f; - if (offset) - out += 1.f; + // Invert and offset + if (invert) + out *= -1.f; + if (offset) + out += 1.f; - outputs[WAVE_OUTPUT].setVoltageSimd(out * 5.f, c); + outputs[WAVE_OUTPUT].setVoltageSimd(out * 5.f, c); + } + } + else { + // Wavetable is invalid, so set 0V + for (int c = 0; c < channels; c += 4) { + outputs[WAVE_OUTPUT].setVoltageSimd(float_4(0.f), c); + } } outputs[WAVE_OUTPUT].setChannels(channels); diff --git a/src/Wavetable.hpp b/src/Wavetable.hpp index 080afaa..0160ed8 100644 --- a/src/Wavetable.hpp +++ b/src/Wavetable.hpp @@ -84,6 +84,8 @@ struct Wavetable { /** Returns the number of waves in the wavetable. */ size_t getWaveCount() const { + if (waveLen == 0) + return 0; return samples.size() / waveLen; } @@ -145,8 +147,6 @@ struct Wavetable { } void load(std::string path) { - samples.clear(); - std::string ext = string::lowercase(system::getExtension(path)); if (ext == ".wav") { // Load WAV @@ -158,6 +158,7 @@ struct Wavetable { #endif return; + samples.clear(); samples.resize(wav.totalPCMFrameCount * wav.channels); drwav_read_pcm_frames_f32(&wav, wav.totalPCMFrameCount, samples.data()); @@ -166,6 +167,7 @@ struct Wavetable { else { // Load bytes from file std::vector data = system::readFile(path); + samples.clear(); if (ext == ".f32") { size_t len = data.size() / sizeof(float); From 6b6d8eeaf0bd0728d85b96e4bd2a781279a7bbab Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Mon, 1 Nov 2021 02:30:40 -0400 Subject: [PATCH 2/5] Implement Viz panel (incorrect positions). --- src/Viz.cpp | 79 +++++++++++++++++++++++++++-------------------------- 1 file changed, 41 insertions(+), 38 deletions(-) diff --git a/src/Viz.cpp b/src/Viz.cpp index a0d1efc..449621a 100644 --- a/src/Viz.cpp +++ b/src/Viz.cpp @@ -44,28 +44,30 @@ struct Viz : Module { struct VizDisplay : Widget { Viz* module; - std::shared_ptr font; VizDisplay() { box.size = mm2px(Vec(15.24, 88.126)); - font = APP->window->loadFont(asset::plugin(pluginInstance, "res/nunito/Nunito-Bold.ttf")); } - void draw(const DrawArgs& args) override { - for (int c = 0; c < 16; c++) { - Vec p = Vec(15, 16 + (float) c / 16 * (box.size.y - 10)); - std::string text = string::f("%d", c + 1); - - nvgFontFaceId(args.vg, font->handle); - nvgFontSize(args.vg, 11); - nvgTextLetterSpacing(args.vg, 0.0); - nvgTextAlign(args.vg, NVG_ALIGN_CENTER | NVG_ALIGN_BASELINE); - if (module && c < module->lastChannel) - nvgFillColor(args.vg, nvgRGB(255, 255, 255)); - else - nvgFillColor(args.vg, nvgRGB(99, 99, 99)); - nvgText(args.vg, p.x, p.y, text.c_str(), NULL); + void drawLayer(const DrawArgs& args, int layer) override { + if (layer == 1) { + for (int c = 0; c < 16; c++) { + Vec p = Vec(15, 16 + (float) c / 16 * (box.size.y - 10)); + std::string text = string::f("%d", c + 1); + std::shared_ptr font = APP->window->loadFont(asset::plugin(pluginInstance, "res/nunito/Nunito-Bold.ttf")); + + nvgFontFaceId(args.vg, font->handle); + nvgFontSize(args.vg, 11); + nvgTextLetterSpacing(args.vg, 0.0); + nvgTextAlign(args.vg, NVG_ALIGN_CENTER | NVG_ALIGN_BASELINE); + if (module && c < module->lastChannel) + nvgFillColor(args.vg, nvgRGB(255, 255, 255)); + else + nvgFillColor(args.vg, nvgRGB(99, 99, 99)); + nvgText(args.vg, p.x, p.y, text.c_str(), NULL); + } } + Widget::drawLayer(args, layer); } }; @@ -80,28 +82,29 @@ struct VizWidget : ModuleWidget { 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))); - addInput(createInputCentered(mm2px(Vec(7.619, 21.346)), module, Viz::POLY_INPUT)); - - addChild(createLightCentered>(mm2px(Vec(10.854, 33.626)), module, Viz::VU_LIGHTS + 0 * 2)); - addChild(createLightCentered>(mm2px(Vec(10.854, 38.916)), module, Viz::VU_LIGHTS + 1 * 2)); - addChild(createLightCentered>(mm2px(Vec(10.854, 44.205)), module, Viz::VU_LIGHTS + 2 * 2)); - addChild(createLightCentered>(mm2px(Vec(10.854, 49.496)), module, Viz::VU_LIGHTS + 3 * 2)); - addChild(createLightCentered>(mm2px(Vec(10.854, 54.785)), module, Viz::VU_LIGHTS + 4 * 2)); - addChild(createLightCentered>(mm2px(Vec(10.854, 60.075)), module, Viz::VU_LIGHTS + 5 * 2)); - addChild(createLightCentered>(mm2px(Vec(10.854, 65.364)), module, Viz::VU_LIGHTS + 6 * 2)); - addChild(createLightCentered>(mm2px(Vec(10.854, 70.654)), module, Viz::VU_LIGHTS + 7 * 2)); - addChild(createLightCentered>(mm2px(Vec(10.854, 75.943)), module, Viz::VU_LIGHTS + 8 * 2)); - addChild(createLightCentered>(mm2px(Vec(10.854, 81.233)), module, Viz::VU_LIGHTS + 9 * 2)); - addChild(createLightCentered>(mm2px(Vec(10.854, 86.522)), module, Viz::VU_LIGHTS + 10 * 2)); - addChild(createLightCentered>(mm2px(Vec(10.854, 91.812)), module, Viz::VU_LIGHTS + 11 * 2)); - addChild(createLightCentered>(mm2px(Vec(10.854, 97.101)), module, Viz::VU_LIGHTS + 12 * 2)); - addChild(createLightCentered>(mm2px(Vec(10.854, 102.392)), module, Viz::VU_LIGHTS + 13 * 2)); - addChild(createLightCentered>(mm2px(Vec(10.854, 107.681)), module, Viz::VU_LIGHTS + 14 * 2)); - addChild(createLightCentered>(mm2px(Vec(10.854, 112.971)), module, Viz::VU_LIGHTS + 15 * 2)); - - VizDisplay* vizDisplay = createWidget(mm2px(Vec(0.0, 29.235))); - vizDisplay->module = module; - addChild(vizDisplay); + addInput(createInputCentered(mm2px(Vec(2.58, 7.229)), module, Viz::POLY_INPUT)); + + addChild(createLightCentered>(mm2px(Vec(3.676, 11.388)), module, Viz::VU_LIGHTS + 0 * 2)); + addChild(createLightCentered>(mm2px(Vec(3.676, 13.18)), module, Viz::VU_LIGHTS + 1 * 2)); + addChild(createLightCentered>(mm2px(Vec(3.676, 14.971)), module, Viz::VU_LIGHTS + 2 * 2)); + addChild(createLightCentered>(mm2px(Vec(3.676, 16.763)), module, Viz::VU_LIGHTS + 3 * 2)); + addChild(createLightCentered>(mm2px(Vec(3.676, 18.554)), module, Viz::VU_LIGHTS + 4 * 2)); + addChild(createLightCentered>(mm2px(Vec(3.676, 20.345)), module, Viz::VU_LIGHTS + 5 * 2)); + addChild(createLightCentered>(mm2px(Vec(3.676, 22.137)), module, Viz::VU_LIGHTS + 6 * 2)); + addChild(createLightCentered>(mm2px(Vec(3.676, 23.928)), module, Viz::VU_LIGHTS + 7 * 2)); + addChild(createLightCentered>(mm2px(Vec(3.676, 25.719)), module, Viz::VU_LIGHTS + 8 * 2)); + addChild(createLightCentered>(mm2px(Vec(3.676, 27.511)), module, Viz::VU_LIGHTS + 9 * 2)); + addChild(createLightCentered>(mm2px(Vec(3.676, 29.302)), module, Viz::VU_LIGHTS + 10 * 2)); + addChild(createLightCentered>(mm2px(Vec(3.676, 31.094)), module, Viz::VU_LIGHTS + 11 * 2)); + addChild(createLightCentered>(mm2px(Vec(3.676, 32.885)), module, Viz::VU_LIGHTS + 12 * 2)); + addChild(createLightCentered>(mm2px(Vec(3.676, 34.677)), module, Viz::VU_LIGHTS + 13 * 2)); + addChild(createLightCentered>(mm2px(Vec(3.676, 36.468)), module, Viz::VU_LIGHTS + 14 * 2)); + addChild(createLightCentered>(mm2px(Vec(3.676, 38.259)), module, Viz::VU_LIGHTS + 15 * 2)); + + VizDisplay* display = createWidget(mm2px(Vec(0.0, 9.901))); + display->box.size = mm2px(Vec(5.161, 29.845)); + display->module = module; + addChild(display); } }; From baf29d361dcd9929a076ccbdbe8dc9c4d92f0a3d Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Mon, 1 Nov 2021 03:31:48 -0400 Subject: [PATCH 3/5] Implement Random panel redesign. Functionality WIP. --- res/Random.svg | 3377 +++++++++++++----------------------------------- res/Viz.svg | 399 +++--- src/Random.cpp | 115 +- 3 files changed, 1202 insertions(+), 2689 deletions(-) diff --git a/res/Random.svg b/res/Random.svg index b26a2f8..a2a5518 100644 --- a/res/Random.svg +++ b/res/Random.svg @@ -1,6 +1,4 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + id="svg291" + sodipodi:docname="RANDOM.svg" + inkscape:version="0.92.3 (2405546, 2018-03-11)"> + id="metadata295"> image/svg+xml - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + id="b1ee2ad7-f2cc-4d5c-a8a9-017d6a13c113" + data-name="FND TXT"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/res/Viz.svg b/res/Viz.svg index 97ecf4f..34c452e 100644 --- a/res/Viz.svg +++ b/res/Viz.svg @@ -1,6 +1,4 @@ - - - - + id="svg154" + sodipodi:docname="VIZ.svg" + inkscape:version="0.92.3 (2405546, 2018-03-11)"> + id="metadata158"> image/svg+xml - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + style="display:none"> + + + style="fill:#ff00ff" /> + style="fill:#ff00ff" /> + style="fill:#ff00ff" /> + style="fill:#ff00ff" /> + style="fill:#ff00ff" /> + style="fill:#ff00ff" /> + style="fill:#ff00ff" /> + style="fill:#ff00ff" /> + style="fill:#ff00ff" /> + style="fill:#ff00ff" /> + style="fill:#ff00ff" /> + style="fill:#ff00ff" /> + style="fill:#ff00ff" /> - - diff --git a/src/Random.cpp b/src/Random.cpp index 27267d2..1b0a430 100644 --- a/src/Random.cpp +++ b/src/Random.cpp @@ -7,6 +7,13 @@ struct Random : Module { SHAPE_PARAM, OFFSET_PARAM, MODE_PARAM, + // new in 2.0 + PROB_PARAM, // TODO + RANDOM_PARAM, // TODO + RATE_CV_PARAM, + SHAPE_CV_PARAM, + PROB_CV_PARAM, + RANDOM_CV_PARAM, NUM_PARAMS }; enum InputIds { @@ -14,6 +21,9 @@ struct Random : Module { SHAPE_INPUT, TRIGGER_INPUT, EXTERNAL_INPUT, + // new in 2.0 + PROB_INPUT, + RANDOM_INPUT, NUM_INPUTS }; enum OutputIds { @@ -21,35 +31,64 @@ struct Random : Module { LINEAR_OUTPUT, SMOOTH_OUTPUT, EXPONENTIAL_OUTPUT, + // new in 2.0 + GATE_OUTPUT, // TODO NUM_OUTPUTS }; enum LightIds { RATE_LIGHT, SHAPE_LIGHT, + PROB_LIGHT, + RANDOM_LIGHT, + OFFSET_LIGHT, NUM_LIGHTS }; dsp::SchmittTrigger trigTrigger; + dsp::BooleanTrigger offsetTrigger; float lastValue = 0.f; float value = 0.f; float clockPhase = 0.f; int trigFrame = 0; int lastTrigFrames = INT_MAX; + bool offset = false; Random() { config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS); configParam(RATE_PARAM, std::log2(0.002f), std::log2(2000.f), std::log2(2.f), "Rate", " Hz", 2); - configParam(SHAPE_PARAM, 0.f, 1.f, 0.5f, "Shape", "%", 0, 100); - configSwitch(OFFSET_PARAM, 0.f, 1.f, 1.f, "Offset", {"Bipolar", "Unipolar"}); - configSwitch(MODE_PARAM, 0.f, 1.f, 1.f, "Randomness mode", {"Relative", "Absolute"}); + configParam(SHAPE_PARAM, 0.f, 1.f, 1.f, "Shape", "%", 0, 100); + configParam(PROB_PARAM, 0.f, 1.f, 1.f, "Probability", "%", 0, 100); + configParam(RANDOM_PARAM, 0.f, 1.f, 1.f, "Randomness", "%", 0, 100); + + configParam(RATE_CV_PARAM, -1.f, 1.f, 0.f, "Rate CV", "%", 0, 100); + getParamQuantity(RATE_CV_PARAM)->randomizeEnabled = false; + configParam(SHAPE_CV_PARAM, -1.f, 1.f, 0.f, "Shape CV", "%", 0, 100); + getParamQuantity(SHAPE_CV_PARAM)->randomizeEnabled = false; + configParam(PROB_CV_PARAM, -1.f, 1.f, 0.f, "Probability CV", "%", 0, 100); + getParamQuantity(PROB_CV_PARAM)->randomizeEnabled = false; + configParam(RANDOM_CV_PARAM, -1.f, 1.f, 0.f, "Randomness CV", "%", 0, 100); + getParamQuantity(RANDOM_CV_PARAM)->randomizeEnabled = false; + + configButton(OFFSET_PARAM, "Offset 0-10V"); + configInput(RATE_INPUT, "Rate"); configInput(SHAPE_INPUT, "Shape"); + configInput(PROB_INPUT, "Probability"); + configInput(RANDOM_INPUT, "Randomness"); configInput(TRIGGER_INPUT, "Trigger"); configInput(EXTERNAL_INPUT, "External"); + configOutput(STEPPED_OUTPUT, "Stepped"); configOutput(LINEAR_OUTPUT, "Linear"); configOutput(SMOOTH_OUTPUT, "Smooth"); configOutput(EXPONENTIAL_OUTPUT, "Exponential"); + configOutput(GATE_OUTPUT, "Gate"); + + onReset(); + } + + void onReset() override { + offset = false; } void trigger() { @@ -87,6 +126,10 @@ struct Random : Module { } void process(const ProcessArgs& args) override { + if (offsetTrigger.process(params[OFFSET_PARAM].getValue() > 0.f)) { + offset ^= true; + } + if (inputs[TRIGGER_INPUT].isConnected()) { // Advance clock phase based on tempo estimate trigFrame++; @@ -104,7 +147,7 @@ struct Random : Module { else { // Advance clock phase by rate float rate = params[RATE_PARAM].getValue(); - rate += inputs[RATE_PARAM].getVoltage(); + rate += inputs[RATE_PARAM].getVoltage() * params[RATE_CV_PARAM].getValue(); float clockFreq = std::pow(2.f, rate); float deltaPhase = std::fmin(clockFreq * args.sampleTime, 0.5f); clockPhase += deltaPhase; @@ -115,10 +158,9 @@ struct Random : Module { } } - // Shape float shape = params[SHAPE_PARAM].getValue(); - shape += inputs[SHAPE_INPUT].getVoltage() / 10.f; + shape += inputs[SHAPE_INPUT].getVoltage() / 10.f * params[SHAPE_CV_PARAM].getValue(); shape = clamp(shape, 0.f, 1.f); // Stepped @@ -178,6 +220,35 @@ struct Random : Module { // Lights lights[RATE_LIGHT].setSmoothBrightness(0.f, args.sampleTime); lights[SHAPE_LIGHT].setBrightness(shape); + lights[OFFSET_LIGHT].setBrightness(offset); + } + + void paramsFromJson(json_t* rootJ) override { + // In <2.0, there were no attenuverters, so set them to 1.0 in case they are not overwritten. + params[RATE_CV_PARAM].setValue(1.f); + params[SHAPE_CV_PARAM].setValue(1.f); + params[PROB_CV_PARAM].setValue(1.f); + params[RANDOM_CV_PARAM].setValue(1.f); + Module::paramsFromJson(rootJ); + // In <2.0, OFFSET_PARAM was a toggle switch instead of a momentary button, so if param is on after deserializing, set boolean states instead. + if (params[OFFSET_PARAM].getValue() > 0.f) { + offset = true; + params[OFFSET_PARAM].setValue(0.f); + } + } + + json_t* dataToJson() override { + json_t* rootJ = json_object(); + // offset + json_object_set_new(rootJ, "offset", json_boolean(offset)); + return rootJ; + } + + void dataFromJson(json_t* rootJ) override { + // offset + json_t* offsetJ = json_object_get(rootJ, "offset"); + if (offsetJ) + offset = json_boolean_value(offsetJ); } }; @@ -192,20 +263,28 @@ struct RandomWidget : ModuleWidget { 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(createLightParamCentered>(mm2px(Vec(7.215, 30.858)), module, Random::RATE_PARAM, Random::RATE_LIGHT)); - addParam(createLightParamCentered>(mm2px(Vec(18.214, 30.858)), module, Random::SHAPE_PARAM, Random::SHAPE_LIGHT)); - addParam(createParamCentered(mm2px(Vec(7.214, 78.259)), module, Random::OFFSET_PARAM)); - addParam(createParamCentered(mm2px(Vec(18.214, 78.259)), module, Random::MODE_PARAM)); + addParam(createLightParamCentered>(mm2px(Vec(6.479, 33.605)), module, Random::RATE_PARAM, Random::RATE_LIGHT)); + addParam(createLightParamCentered>(mm2px(Vec(17.315, 33.605)), module, Random::PROB_PARAM, Random::PROB_LIGHT)); + addParam(createLightParamCentered>(mm2px(Vec(28.152, 33.605)), module, Random::RANDOM_PARAM, Random::RANDOM_LIGHT)); + addParam(createLightParamCentered>(mm2px(Vec(38.98, 33.605)), module, Random::SHAPE_PARAM, Random::SHAPE_LIGHT)); + addParam(createParamCentered(mm2px(Vec(6.479, 64.347)), module, Random::RATE_CV_PARAM)); + addParam(createParamCentered(mm2px(Vec(17.317, 64.347)), module, Random::PROB_CV_PARAM)); + addParam(createParamCentered(mm2px(Vec(28.154, 64.347)), module, Random::RANDOM_CV_PARAM)); + addParam(createParamCentered(mm2px(Vec(38.991, 64.347)), module, Random::SHAPE_CV_PARAM)); + addParam(createLightParamCentered>>(mm2px(Vec(28.154, 96.859)), module, Random::OFFSET_PARAM, Random::OFFSET_LIGHT)); - addInput(createInputCentered(mm2px(Vec(7.214, 50.726)), module, Random::RATE_INPUT)); - addInput(createInputCentered(mm2px(Vec(18.214, 50.726)), module, Random::SHAPE_INPUT)); - addInput(createInputCentered(mm2px(Vec(7.214, 64.513)), module, Random::TRIGGER_INPUT)); - addInput(createInputCentered(mm2px(Vec(18.214, 64.513)), module, Random::EXTERNAL_INPUT)); + addInput(createInputCentered(mm2px(Vec(6.479, 80.549)), module, Random::RATE_INPUT)); + addInput(createInputCentered(mm2px(Vec(17.317, 80.549)), module, Random::PROB_INPUT)); + addInput(createInputCentered(mm2px(Vec(28.154, 80.553)), module, Random::RANDOM_INPUT)); + addInput(createInputCentered(mm2px(Vec(38.991, 80.557)), module, Random::SHAPE_INPUT)); + addInput(createInputCentered(mm2px(Vec(6.479, 96.859)), module, Random::TRIGGER_INPUT)); + addInput(createInputCentered(mm2px(Vec(17.317, 96.859)), module, Random::EXTERNAL_INPUT)); - addOutput(createOutputCentered(mm2px(Vec(7.214, 96.727)), module, Random::STEPPED_OUTPUT)); - addOutput(createOutputCentered(mm2px(Vec(18.214, 96.727)), module, Random::LINEAR_OUTPUT)); - addOutput(createOutputCentered(mm2px(Vec(7.214, 112.182)), module, Random::SMOOTH_OUTPUT)); - addOutput(createOutputCentered(mm2px(Vec(18.214, 112.182)), module, Random::EXPONENTIAL_OUTPUT)); + addOutput(createOutputCentered(mm2px(Vec(38.991, 96.859)), module, Random::GATE_OUTPUT)); + addOutput(createOutputCentered(mm2px(Vec(6.479, 113.115)), module, Random::STEPPED_OUTPUT)); + addOutput(createOutputCentered(mm2px(Vec(17.317, 113.115)), module, Random::LINEAR_OUTPUT)); + addOutput(createOutputCentered(mm2px(Vec(28.154, 113.115)), module, Random::EXPONENTIAL_OUTPUT)); + addOutput(createOutputCentered(mm2px(Vec(38.991, 113.115)), module, Random::SMOOTH_OUTPUT)); } }; From 22a03c1756d490ea26b05d0ce912a0ce20cf2a45 Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Mon, 1 Nov 2021 03:35:00 -0400 Subject: [PATCH 4/5] SEQ 3: Make RUN input/output a gate instead of trigger. --- src/SEQ3.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/SEQ3.cpp b/src/SEQ3.cpp index 60138e1..6dc2a4b 100644 --- a/src/SEQ3.cpp +++ b/src/SEQ3.cpp @@ -50,11 +50,9 @@ struct SEQ3 : Module { dsp::BooleanTrigger gateTriggers[8]; dsp::SchmittTrigger clockTrigger; - dsp::SchmittTrigger runTrigger; dsp::SchmittTrigger resetTrigger; dsp::PulseGenerator clockPulse; - dsp::PulseGenerator runPulse; dsp::PulseGenerator resetPulse; /** Phase of internal LFO */ @@ -147,9 +145,12 @@ struct SEQ3 : Module { void process(const ProcessArgs& args) override { // Run // Use bitwise OR "|" to always evaluate both expressions - if (runButtonTrigger.process(params[RUN_PARAM].getValue()) | runTrigger.process(inputs[RUN_INPUT].getVoltage(), 0.1f, 2.f)) { + if (runButtonTrigger.process(params[RUN_PARAM].getValue())) { running ^= true; - runPulse.trigger(1e-3f); + } + // Run input overrides button + if (inputs[RUN_INPUT].isConnected()) { + running = (inputs[RUN_INPUT].getVoltage() >= 2.f); } // Reset @@ -218,7 +219,7 @@ struct SEQ3 : Module { outputs[STEPS_OUTPUT].setVoltage((numSteps - 1) * 1.f); outputs[CLOCK_OUTPUT].setVoltage(clockGate ? 10.f : 0.f); - outputs[RUN_OUTPUT].setVoltage(runPulse.process(args.sampleTime) ? 10.f : 0.f); + outputs[RUN_OUTPUT].setVoltage(running ? 10.f : 0.f); outputs[RESET_OUTPUT].setVoltage(resetGate ? 10.f : 0.f); lights[CLOCK_LIGHT].setSmoothBrightness(clockGate, args.sampleTime); From 87d3caa41919acb53df16f867defa656191a53a3 Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Mon, 1 Nov 2021 09:02:54 -0400 Subject: [PATCH 5/5] Implement Scope panel redesign. Add LEDLightLatch to LFO and WT LFO. --- src/LFO.cpp | 55 ++++++------------------------------------------ src/Scope.cpp | 58 +++++++++++++++++++++++++-------------------------- src/WTLFO.cpp | 37 ++++++-------------------------- 3 files changed, 40 insertions(+), 110 deletions(-) diff --git a/src/LFO.cpp b/src/LFO.cpp index 6fe490b..9de8415 100644 --- a/src/LFO.cpp +++ b/src/LFO.cpp @@ -48,14 +48,12 @@ struct LFO : Module { float clockFreq = 1.f; dsp::Timer clockTimer; - dsp::BooleanTrigger offsetTrigger; - dsp::BooleanTrigger invertTrigger; dsp::ClockDivider lightDivider; LFO() { config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS); - configButton(OFFSET_PARAM, "Offset 0-10V"); - configButton(INVERT_PARAM, "Invert"); + configSwitch(OFFSET_PARAM, 0.f, 1.f, 0.f, "Offset", {"Bipolar", "Unipolar"}); + configSwitch(INVERT_PARAM, 0.f, 1.f, 0.f, "Invert"); configParam(FREQ_PARAM, -8.f, 10.f, 1.f, "Frequency", " Hz", 2, 1); configParam(FM_PARAM, -1.f, 1.f, 0.f, "Frequency modulation", "%", 0.f, 100.f); getParamQuantity(FM_PARAM)->randomizeEnabled = false; @@ -80,8 +78,6 @@ struct LFO : Module { } void onReset() override { - offset = false; - invert = false; for (int c = 0; c < 16; c += 4) { phases[c / 4] = 0.f; } @@ -94,14 +90,8 @@ struct LFO : Module { float fmParam = params[FM_PARAM].getValue(); float pwParam = params[PW_PARAM].getValue(); float pwmParam = params[PWM_PARAM].getValue(); - - // Buttons - if (offsetTrigger.process(params[OFFSET_PARAM].getValue() > 0.f)) { - offset ^= true; - } - if (invertTrigger.process(params[INVERT_PARAM].getValue() > 0.f)) { - invert ^= true; - } + bool offset = params[OFFSET_PARAM].getValue() > 0.f; + bool invert = params[INVERT_PARAM].getValue() > 0.f; // Clock if (inputs[CLOCK_INPUT].isConnected()) { @@ -212,39 +202,6 @@ struct LFO : Module { lights[INVERT_LIGHT].setBrightness(invert); } } - - json_t* dataToJson() override { - json_t* rootJ = json_object(); - // offset - json_object_set_new(rootJ, "offset", json_boolean(offset)); - // invert - json_object_set_new(rootJ, "invert", json_boolean(invert)); - return rootJ; - } - - void dataFromJson(json_t* rootJ) override { - // offset - json_t* offsetJ = json_object_get(rootJ, "offset"); - if (offsetJ) - offset = json_boolean_value(offsetJ); - // invert - json_t* invertJ = json_object_get(rootJ, "invert"); - if (invertJ) - invert = json_boolean_value(invertJ); - } - - void paramsFromJson(json_t* rootJ) override { - Module::paramsFromJson(rootJ); - // In <2.0, OFFSET_PARAM and INVERT_PARAM were toggle switches instead of momentary buttons, so if params are on after deserializing, set boolean states instead. - if (params[OFFSET_PARAM].getValue() > 0.f) { - offset = true; - params[OFFSET_PARAM].setValue(0.f); - } - if (params[INVERT_PARAM].getValue() > 0.f) { - invert = true; - params[INVERT_PARAM].setValue(0.f); - } - } }; @@ -261,8 +218,8 @@ struct LFOWidget : ModuleWidget { 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::FM_PARAM)); - addParam(createLightParamCentered>>(mm2px(Vec(17.441, 80.603)), module, LFO::INVERT_PARAM, LFO::INVERT_LIGHT)); - addParam(createLightParamCentered>>(mm2px(Vec(28.279, 80.603)), module, LFO::OFFSET_PARAM, LFO::OFFSET_LIGHT)); + addParam(createLightParamCentered>>(mm2px(Vec(17.441, 80.603)), module, LFO::INVERT_PARAM, LFO::INVERT_LIGHT)); + addParam(createLightParamCentered>>(mm2px(Vec(28.279, 80.603)), module, LFO::OFFSET_PARAM, LFO::OFFSET_LIGHT)); addParam(createParamCentered(mm2px(Vec(39.116, 80.603)), module, LFO::PWM_PARAM)); addInput(createInputCentered(mm2px(Vec(6.604, 96.859)), module, LFO::FM_INPUT)); diff --git a/src/Scope.cpp b/src/Scope.cpp index 8366085..85db1fb 100644 --- a/src/Scope.cpp +++ b/src/Scope.cpp @@ -24,6 +24,9 @@ struct Scope : Module { NUM_INPUTS }; enum OutputIds { + // new in 2.0 + X_OUTPUT, + Y_OUTPUT, NUM_OUTPUTS }; enum LightIds { @@ -353,36 +356,31 @@ struct ScopeWidget : ModuleWidget { setModule(module); setPanel(createPanel(asset::plugin(pluginInstance, "res/Scope.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))); - - { - ScopeDisplay* display = new ScopeDisplay(); - display->module = module; - display->box.pos = Vec(0, 44); - display->box.size = Vec(box.size.x, 140); - addChild(display); - } - - addParam(createParam(Vec(15, 209), module, Scope::X_SCALE_PARAM)); - addParam(createParam(Vec(15, 263), module, Scope::X_POS_PARAM)); - addParam(createParam(Vec(61, 209), module, Scope::Y_SCALE_PARAM)); - addParam(createParam(Vec(61, 263), module, Scope::Y_POS_PARAM)); - addParam(createParam(Vec(107, 209), module, Scope::TIME_PARAM)); - addParam(createParam(Vec(106, 262), module, Scope::LISSAJOUS_PARAM)); - addParam(createParam(Vec(153, 209), module, Scope::TRIG_PARAM)); - addParam(createParam(Vec(152, 262), module, Scope::EXTERNAL_PARAM)); - - addInput(createInput(Vec(17, 319), module, Scope::X_INPUT)); - addInput(createInput(Vec(63, 319), module, Scope::Y_INPUT)); - addInput(createInput(Vec(154, 319), module, Scope::TRIG_INPUT)); - - addChild(createLight>(Vec(104, 251), module, Scope::PLOT_LIGHT)); - addChild(createLight>(Vec(104, 296), module, Scope::LISSAJOUS_LIGHT)); - addChild(createLight>(Vec(150, 251), module, Scope::INTERNAL_LIGHT)); - addChild(createLight>(Vec(150, 296), module, Scope::EXTERNAL_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(8.643, 80.603)), module, Scope::_1X2_PARAM)); + addParam(createParamCentered(mm2px(Vec(24.897, 80.551)), module, Scope::X_SCALE_PARAM)); + addParam(createParamCentered(mm2px(Vec(41.147, 80.551)), module, Scope::Y_SCALE_PARAM)); + // addParam(createParamCentered(mm2px(Vec(57.397, 80.521)), module, Scope::TRIG_PARAM)); + addParam(createParamCentered(mm2px(Vec(8.643, 96.819)), module, Scope::TIME_PARAM)); + addParam(createParamCentered(mm2px(Vec(24.897, 96.789)), module, Scope::X_POS_PARAM)); + addParam(createParamCentered(mm2px(Vec(41.147, 96.815)), module, Scope::Y_POS_PARAM)); + // addParam(createParamCentered(mm2px(Vec(57.397, 96.815)), module, Scope::THERS_PARAM)); + + addInput(createInputCentered(mm2px(Vec(8.643, 113.115)), module, Scope::X_INPUT)); + addInput(createInputCentered(mm2px(Vec(33.023, 113.115)), module, Scope::Y_INPUT)); + addInput(createInputCentered(mm2px(Vec(57.397, 113.115)), module, Scope::TRIG_INPUT)); + + addOutput(createOutputCentered(mm2px(Vec(20.833, 113.115)), module, Scope::X_OUTPUT)); + addOutput(createOutputCentered(mm2px(Vec(45.212, 113.115)), module, Scope::Y_OUTPUT)); + + ScopeDisplay* display = createWidget(Vec(0, 44)); + display->box.size = Vec(box.size.x, 140); + display->module = module; + addChild(display); } }; diff --git a/src/WTLFO.cpp b/src/WTLFO.cpp index 494c1ae..bf2085f 100644 --- a/src/WTLFO.cpp +++ b/src/WTLFO.cpp @@ -36,8 +36,6 @@ struct WTLFO : Module { }; Wavetable wavetable; - bool offset = false; - bool invert = false; float_4 phases[4] = {}; float lastPos = 0.f; @@ -52,9 +50,8 @@ struct WTLFO : Module { 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"); + configSwitch(OFFSET_PARAM, 0.f, 1.f, 0.f, "Offset", {"Bipolar", "Unipolar"}); + configSwitch(INVERT_PARAM, 0.f, 1.f, 0.f, "Invert"); struct FrequencyQuantity : ParamQuantity { float getDisplayValue() override { @@ -88,8 +85,6 @@ struct WTLFO : Module { } void onReset() override { - offset = false; - invert = false; wavetable.reset(); // Reset state @@ -100,12 +95,6 @@ struct WTLFO : Module { clockTimer.reset(); } - void onRandomize(const RandomizeEvent& e) override { - Module::onRandomize(e); - offset = random::get(); - invert = random::get(); - } - void onAdd(const AddEvent& e) override { std::string path = system::join(getPatchStorageDirectory(), "wavetable.wav"); // Silently fails @@ -120,10 +109,8 @@ struct WTLFO : Module { } void process(const ProcessArgs& args) override { - if (offsetTrigger.process(params[OFFSET_PARAM].getValue() > 0.f)) - offset ^= true; - if (invertTrigger.process(params[INVERT_PARAM].getValue() > 0.f)) - invert ^= true; + bool offset = (params[OFFSET_PARAM].getValue() > 0.f); + bool invert = (params[INVERT_PARAM].getValue() > 0.f); int channels = std::max(1, inputs[FM_INPUT].getChannels()); @@ -239,10 +226,6 @@ struct WTLFO : Module { json_t* dataToJson() override { json_t* rootJ = json_object(); - // offset - json_object_set_new(rootJ, "offset", json_boolean(offset)); - // invert - json_object_set_new(rootJ, "invert", json_boolean(invert)); // Merge wavetable json_t* wavetableJ = wavetable.toJson(); json_object_update(rootJ, wavetableJ); @@ -251,14 +234,6 @@ struct WTLFO : Module { } void dataFromJson(json_t* rootJ) override { - // offset - json_t* offsetJ = json_object_get(rootJ, "offset"); - if (offsetJ) - offset = json_boolean_value(offsetJ); - // invert - json_t* invertJ = json_object_get(rootJ, "invert"); - if (invertJ) - invert = json_boolean_value(invertJ); // wavetable wavetable.fromJson(rootJ); } @@ -278,9 +253,9 @@ struct WTLFOWidget : ModuleWidget { 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(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)); + 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));