diff --git a/plugin.json b/plugin.json index f333793..203a90c 100644 --- a/plugin.json +++ b/plugin.json @@ -1,305 +1,316 @@ -{ - "slug": "Fundamental", - "name": "Fundamental", - "version": "2.1.0", - "license": "GPL-3.0-or-later", - "brand": "VCV", - "author": "VCV", - "authorEmail": "support@vcvrack.com", - "pluginUrl": "https://vcvrack.com/Fundamental", - "authorUrl": "https://vcvrack.com/", - "manualUrl": "https://vcvrack.com/Fundamental#manual", - "sourceUrl": "https://github.com/VCVRack/Fundamental", - "changelogUrl": "https://github.com/VCVRack/Fundamental/blob/v2/CHANGELOG.md", - "modules": [ - { - "slug": "VCO", - "name": "VCO", - "description": "Voltage-controlled oscillator", - "manualUrl": "https://vcvrack.com/Fundamental#VCO", - "tags": [ - "VCO", - "Polyphonic" - ] - }, - { - "slug": "VCO2", - "name": "Wavetable VCO", - "description": "Voltage-controlled wavetable oscillator", - "manualUrl": "https://vcvrack.com/Fundamental#WavetableVCO", - "tags": [ - "VCO", - "Polyphonic" - ] - }, - { - "slug": "VCF", - "name": "VCF", - "description": "Voltage-controlled filter", - "manualUrl": "https://vcvrack.com/Fundamental#VCF", - "tags": [ - "VCF", - "Polyphonic" - ] - }, - { - "slug": "VCA-1", - "name": "VCA", - "description": "Voltage-controlled amplifier", - "manualUrl": "https://vcvrack.com/Fundamental#VCA", - "tags": [ - "VCA", - "Polyphonic" - ] - }, - { - "slug": "VCA", - "name": "VCA-2", - "description": "2-channel voltage-controlled amplifier", - "tags": [ - "VCA", - "Dual", - "Polyphonic" - ], - "hidden": true - }, - { - "slug": "LFO", - "name": "LFO", - "description": "Low-frequency oscillator", - "manualUrl": "https://vcvrack.com/Fundamental#LFO", - "tags": [ - "LFO", - "Polyphonic" - ] - }, - { - "slug": "LFO2", - "name": "Wavetable LFO", - "description": "Low-frequency wavetable oscillator", - "manualUrl": "https://vcvrack.com/Fundamental#WavetableLFO", - "tags": [ - "LFO", - "Polyphonic" - ] - }, - { - "slug": "Delay", - "name": "Delay", - "description": "", - "manualUrl": "https://vcvrack.com/Fundamental#Delay", - "tags": [ - "Delay" - ] - }, - { - "slug": "ADSR", - "name": "ADSR", - "description": "Generates an envelope with Attack/Decay/Sustain/Release", - "manualUrl": "https://vcvrack.com/Fundamental#ADSR", - "tags": [ - "Envelope Generator", - "Polyphonic" - ] - }, - { - "slug": "Mixer", - "name": "Mix", - "description": "Mixes 6 signals", - "manualUrl": "https://vcvrack.com/Fundamental#Mix", - "tags": [ - "Mixer", - "Polyphonic" - ] - }, - { - "slug": "VCMixer", - "name": "VCA Mix", - "description": "Mixes 4 signals with built-in VCAs", - "manualUrl": "https://vcvrack.com/Fundamental#VCMixer", - "tags": [ - "Mixer", - "VCA", - "Polyphonic" - ] - }, - { - "slug": "8vert", - "name": "8vert", - "description": "Attenuverts 8 signals or creates constant voltages", - "manualUrl": "https://vcvrack.com/Fundamental#8vert", - "tags": [ - "Attenuator", - "Polyphonic" - ] - }, - { - "slug": "Unity", - "name": "Unity", - "description": "Mixes or averages signals with unity gain", - "tags": [ - "Mixer", - "Utility", - "Dual" - ], - "hidden": true - }, - { - "slug": "Mutes", - "name": "Mutes", - "description": "Toggles up to 10 signals", - "manualUrl": "https://vcvrack.com/Fundamental#Mutes", - "tags": [ - "Switch", - "Polyphonic" - ] - }, - { - "slug": "Pulses", - "name": "Pulses", - "description": "Generates up to 10 trigger and gate signals", - "manualUrl": "https://vcvrack.com/Fundamental#Pulses", - "tags": [ - "Switch" - ] - }, - { - "slug": "Scope", - "name": "Scope", - "description": "Inspect waveforms with an oscilloscope", - "manualUrl": "https://vcvrack.com/Fundamental#Scope", - "tags": [ - "Visual", - "Polyphonic" - ] - }, - { - "slug": "SEQ3", - "name": "SEQ 3", - "description": "3-channel 8-step sequencer also with gate outputs", - "manualUrl": "https://vcvrack.com/Fundamental#SEQ3", - "tags": [ - "Sequencer" - ] - }, - { - "slug": "SequentialSwitch1", - "name": "Sequential Switch 1 to 4", - "description": "Routes 1 input to one of 4 outputs", - "manualUrl": "https://vcvrack.com/Fundamental#SequentialSwitch", - "tags": [ - "Utility", - "Polyphonic" - ] - }, - { - "slug": "SequentialSwitch2", - "name": "Sequential Switch 4 to 1", - "description": "Routes one of 4 inputs to 1 output", - "manualUrl": "https://vcvrack.com/Fundamental#SequentialSwitch", - "tags": [ - "Utility", - "Polyphonic" - ] - }, - { - "slug": "Octave", - "name": "Octave", - "description": "Shifts 1V/oct pitch CV by octaves", - "manualUrl": "https://vcvrack.com/Fundamental#Octave", - "tags": [ - "Utility", - "Polyphonic" - ] - }, - { - "slug": "Quantizer", - "name": "Quantizer", - "description": "12-note quantizer and scale selector", - "manualUrl": "https://vcvrack.com/Fundamental#Quantizer", - "tags": [ - "Quantizer", - "Polyphonic" - ] - }, - { - "slug": "Split", - "name": "Split", - "description": "Splits a polyphonic cable into multiple monophonic cables", - "manualUrl": "https://vcvrack.com/Fundamental#Split", - "tags": [ - "Polyphonic", - "Utility" - ] - }, - { - "slug": "Merge", - "name": "Merge", - "description": "Combines multiple monophonic cables into a polyphonic cable", - "manualUrl": "https://vcvrack.com/Fundamental#Split", - "tags": [ - "Polyphonic", - "Utility" - ] - }, - { - "slug": "Sum", - "name": "Sum", - "description": "Sums all channels of a polyphonic cable", - "manualUrl": "https://vcvrack.com/Fundamental#Sum", - "tags": [ - "Polyphonic", - "Utility" - ] - }, - { - "slug": "Viz", - "name": "Viz", - "description": "Visualizes all channels of a polyphonic cable", - "manualUrl": "https://vcvrack.com/Fundamental#Viz", - "tags": [ - "Polyphonic", - "Visual" - ] - }, - { - "slug": "MidSide", - "name": "Mid/Side", - "description": "Encodes/decodes between mid/side and left/right stereo signals", - "manualUrl": "https://vcvrack.com/Fundamental#MidSide", - "tags": [ - "Utility", - "Polyphonic" - ] - }, - { - "slug": "Noise", - "name": "Noise", - "description": "Multicolored noise generator", - "manualUrl": "https://vcvrack.com/Fundamental#Noise", - "tags": [ - "Noise" - ] - }, - { - "slug": "Random", - "name": "Random", - "description": "Random CV generator", - "manualUrl": "https://vcvrack.com/Fundamental#Random", - "tags": [ - "Random", - "Sample and hold" - ] - }, - { - "slug": "CVMix", - "name": "CV Mix", - "description": "Mixes 3 CV signals with attenuverters", - "tags": [ - "Utility", - "Polyphonic", - "Mixer" - ] - } - ] +{ + "slug": "Fundamental", + "name": "Fundamental", + "version": "2.1.0", + "license": "GPL-3.0-or-later", + "brand": "VCV", + "author": "VCV", + "authorEmail": "support@vcvrack.com", + "pluginUrl": "https://vcvrack.com/Fundamental", + "authorUrl": "https://vcvrack.com/", + "manualUrl": "https://vcvrack.com/Fundamental#manual", + "sourceUrl": "https://github.com/VCVRack/Fundamental", + "changelogUrl": "https://github.com/VCVRack/Fundamental/blob/v2/CHANGELOG.md", + "modules": [ + { + "slug": "VCO", + "name": "VCO", + "description": "Voltage-controlled oscillator", + "manualUrl": "https://vcvrack.com/Fundamental#VCO", + "tags": [ + "VCO", + "Polyphonic" + ] + }, + { + "slug": "VCO2", + "name": "Wavetable VCO", + "description": "Voltage-controlled wavetable oscillator", + "manualUrl": "https://vcvrack.com/Fundamental#WavetableVCO", + "tags": [ + "VCO", + "Polyphonic" + ] + }, + { + "slug": "VCF", + "name": "VCF", + "description": "Voltage-controlled filter", + "manualUrl": "https://vcvrack.com/Fundamental#VCF", + "tags": [ + "VCF", + "Polyphonic" + ] + }, + { + "slug": "VCA-1", + "name": "VCA", + "description": "Voltage-controlled amplifier", + "manualUrl": "https://vcvrack.com/Fundamental#VCA", + "tags": [ + "VCA", + "Polyphonic" + ] + }, + { + "slug": "VCA", + "name": "VCA-2", + "description": "2-channel voltage-controlled amplifier", + "tags": [ + "VCA", + "Dual", + "Polyphonic" + ], + "hidden": true + }, + { + "slug": "LFO", + "name": "LFO", + "description": "Low-frequency oscillator", + "manualUrl": "https://vcvrack.com/Fundamental#LFO", + "tags": [ + "LFO", + "Polyphonic" + ] + }, + { + "slug": "LFO2", + "name": "Wavetable LFO", + "description": "Low-frequency wavetable oscillator", + "manualUrl": "https://vcvrack.com/Fundamental#WavetableLFO", + "tags": [ + "LFO", + "Polyphonic" + ] + }, + { + "slug": "Delay", + "name": "Delay", + "description": "", + "manualUrl": "https://vcvrack.com/Fundamental#Delay", + "tags": [ + "Delay" + ] + }, + { + "slug": "ADSR", + "name": "ADSR", + "description": "Generates an envelope with Attack/Decay/Sustain/Release", + "manualUrl": "https://vcvrack.com/Fundamental#ADSR", + "tags": [ + "Envelope Generator", + "Polyphonic" + ] + }, + { + "slug": "Mixer", + "name": "Mix", + "description": "Mixes 6 signals", + "manualUrl": "https://vcvrack.com/Fundamental#Mix", + "tags": [ + "Mixer", + "Polyphonic" + ] + }, + { + "slug": "VCMixer", + "name": "VCA Mix", + "description": "Mixes 4 signals with built-in VCAs", + "manualUrl": "https://vcvrack.com/Fundamental#VCMixer", + "tags": [ + "Mixer", + "VCA", + "Polyphonic" + ] + }, + { + "slug": "8vert", + "name": "8vert", + "description": "Attenuverts 8 signals or creates constant voltages", + "manualUrl": "https://vcvrack.com/Fundamental#8vert", + "tags": [ + "Attenuator", + "Polyphonic" + ] + }, + { + "slug": "Unity", + "name": "Unity", + "description": "Mixes or averages signals with unity gain", + "tags": [ + "Mixer", + "Utility", + "Dual" + ], + "hidden": true + }, + { + "slug": "Mutes", + "name": "Mutes", + "description": "Toggles up to 10 signals", + "manualUrl": "https://vcvrack.com/Fundamental#Mutes", + "tags": [ + "Switch", + "Polyphonic" + ] + }, + { + "slug": "Pulses", + "name": "Pulses", + "description": "Generates up to 10 trigger and gate signals", + "manualUrl": "https://vcvrack.com/Fundamental#Pulses", + "tags": [ + "Switch" + ] + }, + { + "slug": "Scope", + "name": "Scope", + "description": "Inspect waveforms with an oscilloscope", + "manualUrl": "https://vcvrack.com/Fundamental#Scope", + "tags": [ + "Visual", + "Polyphonic" + ] + }, + { + "slug": "SEQ3", + "name": "SEQ 3", + "description": "3-channel 8-step sequencer also with gate outputs", + "manualUrl": "https://vcvrack.com/Fundamental#SEQ3", + "tags": [ + "Sequencer" + ] + }, + { + "slug": "SequentialSwitch1", + "name": "Sequential Switch 1 to 4", + "description": "Routes 1 input to one of 4 outputs", + "manualUrl": "https://vcvrack.com/Fundamental#SequentialSwitch", + "tags": [ + "Utility", + "Polyphonic" + ] + }, + { + "slug": "SequentialSwitch2", + "name": "Sequential Switch 4 to 1", + "description": "Routes one of 4 inputs to 1 output", + "manualUrl": "https://vcvrack.com/Fundamental#SequentialSwitch", + "tags": [ + "Utility", + "Polyphonic" + ] + }, + { + "slug": "Octave", + "name": "Octave", + "description": "Shifts 1V/oct pitch CV by octaves", + "manualUrl": "https://vcvrack.com/Fundamental#Octave", + "tags": [ + "Utility", + "Polyphonic" + ] + }, + { + "slug": "Quantizer", + "name": "Quantizer", + "description": "12-note quantizer and scale selector", + "manualUrl": "https://vcvrack.com/Fundamental#Quantizer", + "tags": [ + "Quantizer", + "Polyphonic" + ] + }, + { + "slug": "Split", + "name": "Split", + "description": "Splits a polyphonic cable into multiple monophonic cables", + "manualUrl": "https://vcvrack.com/Fundamental#Split", + "tags": [ + "Polyphonic", + "Utility" + ] + }, + { + "slug": "Merge", + "name": "Merge", + "description": "Combines multiple monophonic cables into a polyphonic cable", + "manualUrl": "https://vcvrack.com/Fundamental#Split", + "tags": [ + "Polyphonic", + "Utility" + ] + }, + { + "slug": "Sum", + "name": "Sum", + "description": "Sums all channels of a polyphonic cable", + "manualUrl": "https://vcvrack.com/Fundamental#Sum", + "tags": [ + "Polyphonic", + "Utility" + ] + }, + { + "slug": "Viz", + "name": "Viz", + "description": "Visualizes all channels of a polyphonic cable", + "manualUrl": "https://vcvrack.com/Fundamental#Viz", + "tags": [ + "Polyphonic", + "Visual" + ] + }, + { + "slug": "MidSide", + "name": "Mid/Side", + "description": "Encodes/decodes between mid/side and left/right stereo signals", + "manualUrl": "https://vcvrack.com/Fundamental#MidSide", + "tags": [ + "Utility", + "Polyphonic" + ] + }, + { + "slug": "Noise", + "name": "Noise", + "description": "Multicolored noise generator", + "manualUrl": "https://vcvrack.com/Fundamental#Noise", + "tags": [ + "Noise" + ] + }, + { + "slug": "Random", + "name": "Random", + "description": "Random CV generator", + "manualUrl": "https://vcvrack.com/Fundamental#Random", + "tags": [ + "Random", + "Sample and hold" + ] + }, + { + "slug": "CVMix", + "name": "CV Mix", + "description": "Mixes 3 CV signals with attenuverters", + "tags": [ + "Mixer", + "Utility", + "Polyphonic" + ] + }, + { + "slug": "Fade", + "name": "Fade", + "description": "Crossfades audio or CV", + "tags": [ + "Panning", + "Voltage-controlled amplifier", + "Utility", + "Polyphonic" + ] + } + ] } \ No newline at end of file diff --git a/res/Fade.svg b/res/Fade.svg new file mode 100644 index 0000000..493dc69 --- /dev/null +++ b/res/Fade.svg @@ -0,0 +1,369 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/CVMix.cpp b/src/CVMix.cpp index ae3c183..da40153 100644 --- a/src/CVMix.cpp +++ b/src/CVMix.cpp @@ -1,6 +1,9 @@ #include "plugin.hpp" +using simd::float_4; + + struct CVMix : Module { enum ParamId { ENUMS(LEVEL_PARAMS, 3), @@ -22,8 +25,11 @@ struct CVMix : Module { config(PARAMS_LEN, INPUTS_LEN, OUTPUTS_LEN, LIGHTS_LEN); for (int i = 0; i < 3; i++) configParam(LEVEL_PARAMS + i, -1.f, 1.f, 0.f, string::f("Level %d", i + 1), "%", 0, 100); + for (int i = 0; i < 3; i++) configInput(CV_INPUTS + i, string::f("CV %d", i + 1)); + getInputInfo(CV_INPUTS + 0)->description = "Normalized to 10 V"; + configOutput(MIX_OUTPUT, "Mix"); } @@ -31,8 +37,6 @@ struct CVMix : Module { if (!outputs[MIX_OUTPUT].isConnected()) return; - using simd::float_4; - // Get number of channels int channels = 1; for (int i = 0; i < 3; i++) diff --git a/src/Fade.cpp b/src/Fade.cpp new file mode 100644 index 0000000..7c13f06 --- /dev/null +++ b/src/Fade.cpp @@ -0,0 +1,94 @@ +#include "plugin.hpp" + + +using simd::float_4; + + +struct Fade : Module { + enum ParamId { + CROSSFADE_PARAM, + CROSSFADE_CV_PARAM, + PARAMS_LEN + }; + enum InputId { + CROSSFADE_INPUT, + IN1_INPUT, + IN2_INPUT, + INPUTS_LEN + }; + enum OutputId { + OUT1_OUTPUT, + OUT2_OUTPUT, + OUTPUTS_LEN + }; + enum LightId { + LIGHTS_LEN + }; + + Fade() { + config(PARAMS_LEN, INPUTS_LEN, OUTPUTS_LEN, LIGHTS_LEN); + configParam(CROSSFADE_PARAM, 0.f, 1.f, 0.5f, "Crossfade", "%", 0, 100); + configParam(CROSSFADE_CV_PARAM, -1.f, 1.f, 0.f, "Crossfade CV", "%", 0, 100); + configInput(CROSSFADE_INPUT, "Crossfade"); + configInput(IN1_INPUT, "Ch 1"); + configInput(IN2_INPUT, "Ch 2"); + configOutput(OUT1_OUTPUT, "Ch 1"); + configOutput(OUT2_OUTPUT, "Ch 2"); + + configBypass(IN1_INPUT, OUT1_OUTPUT); + configBypass(IN2_INPUT, OUT2_OUTPUT); + } + + void process(const ProcessArgs& args) override { + if (!outputs[OUT1_OUTPUT].isConnected() && !outputs[OUT2_OUTPUT].isConnected()) + return; + + int channels = std::max({1, inputs[IN1_INPUT].getChannels(), inputs[IN2_INPUT].getChannels()}); + + for (int c = 0; c < channels; c += 4) { + // Get crossfade amount + float_4 crossfade = params[CROSSFADE_PARAM].getValue(); + crossfade += inputs[CROSSFADE_INPUT].getPolyVoltageSimd(c) / 10.f * params[CROSSFADE_CV_PARAM].getValue(); + crossfade = clamp(crossfade, 0.f, 1.f); + + // Get inputs + float_4 in1 = inputs[IN1_INPUT].getPolyVoltageSimd(c); + float_4 in2 = inputs[IN2_INPUT].getPolyVoltageSimd(c); + + // Calculate outputs + float_4 out1 = (1.f - crossfade) * in1 + crossfade * in2; + float_4 out2 = crossfade * in1 + (1.f - crossfade) * in2; + outputs[OUT1_OUTPUT].setVoltageSimd(out1, c); + outputs[OUT2_OUTPUT].setVoltageSimd(out2, c); + } + + outputs[OUT1_OUTPUT].setChannels(channels); + outputs[OUT2_OUTPUT].setChannels(channels); + } +}; + + +struct FadeWidget : ModuleWidget { + FadeWidget(Fade* module) { + setModule(module); + setPanel(createPanel(asset::plugin(pluginInstance, "res/Fade.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(7.62, 24.723)), module, Fade::CROSSFADE_PARAM)); + addParam(createParamCentered(mm2px(Vec(7.62, 37.064)), module, Fade::CROSSFADE_CV_PARAM)); + + addInput(createInputCentered(mm2px(Vec(7.62, 52.987)), module, Fade::CROSSFADE_INPUT)); + addInput(createInputCentered(mm2px(Vec(7.62, 67.53)), module, Fade::IN1_INPUT)); + addInput(createInputCentered(mm2px(Vec(7.62, 82.732)), module, Fade::IN2_INPUT)); + + addOutput(createOutputCentered(mm2px(Vec(7.62, 97.923)), module, Fade::OUT1_OUTPUT)); + addOutput(createOutputCentered(mm2px(Vec(7.62, 113.115)), module, Fade::OUT2_OUTPUT)); + } +}; + + +Model* modelFade = createModel("Fade"); \ No newline at end of file diff --git a/src/plugin.cpp b/src/plugin.cpp index 4816544..9742595 100644 --- a/src/plugin.cpp +++ b/src/plugin.cpp @@ -35,4 +35,5 @@ void init(Plugin* p) { p->addModel(modelNoise); p->addModel(modelRandom); p->addModel(modelCVMix); + p->addModel(modelFade); } diff --git a/src/plugin.hpp b/src/plugin.hpp index 09e4ba2..1352d22 100644 --- a/src/plugin.hpp +++ b/src/plugin.hpp @@ -35,6 +35,7 @@ extern Model* modelMidSide; extern Model* modelNoise; extern Model* modelRandom; extern Model* modelCVMix; +extern Model* modelFade; struct DigitalDisplay : Widget {