diff --git a/plugins/Cardinal/orig/HostCV.svg b/plugins/Cardinal/orig/HostCV.svg new file mode 100644 index 0000000..4ec0f2b --- /dev/null +++ b/plugins/Cardinal/orig/HostCV.svg @@ -0,0 +1,153 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + Host CV + + diff --git a/plugins/Cardinal/plugin.json b/plugins/Cardinal/plugin.json index 894d497..fcb8397 100644 --- a/plugins/Cardinal/plugin.json +++ b/plugins/Cardinal/plugin.json @@ -13,11 +13,20 @@ "changelogUrl": "", "modules": [ + { + "slug": "HostCV", + "disabled": false, + "name": "Host CV", + "description": "Exposes host-provided CV ports in a module", + "tags": [ + "Utility" + ] + }, { "slug": "HostParameters", "disabled": false, "name": "Host Parameters", - "description": "Exposes host-controlled plugin parameters as module", + "description": "Exposes host-controlled plugin parameters in a module", "tags": [ "Utility" ] @@ -26,7 +35,7 @@ "slug": "HostTime", "disabled": false, "name": "Host Time", - "description": "Exposes host-provided timing information as module", + "description": "Exposes host-provided time/transport information in a module", "tags": [ "Utility" ] @@ -53,7 +62,7 @@ "slug": "Ildaeil", "disabled": false, "name": "Ildaeil Plugin Host", - "description": "A plugin host within Cardinal for loading any FX", + "description": "A mini plugin host within Cardinal for loading any FX", "tags": [ "Utility" ] diff --git a/plugins/Cardinal/res/HostCV.svg b/plugins/Cardinal/res/HostCV.svg new file mode 100644 index 0000000..0ca8180 --- /dev/null +++ b/plugins/Cardinal/res/HostCV.svg @@ -0,0 +1,170 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/plugins/Cardinal/src/Carla.cpp b/plugins/Cardinal/src/Carla.cpp index 3bbcd83..dd47ce0 100644 --- a/plugins/Cardinal/src/Carla.cpp +++ b/plugins/Cardinal/src/Carla.cpp @@ -424,14 +424,6 @@ struct CarlaModuleWidget : ModuleWidget { } } - void drawTextLine(NVGcontext* const vg, const uint offset, const char* const text) - { - const float y = startY + offset * padding; - nvgBeginPath(vg); - nvgFillColor(vg, color::WHITE); - nvgText(vg, middleX, y + 16, text, nullptr); - } - void onContextCreate(const ContextCreateEvent& e) override { ModuleWidget::onContextCreate(e); @@ -456,6 +448,14 @@ struct CarlaModuleWidget : ModuleWidget { ModuleWidget::onContextDestroy(e); } + void drawTextLine(NVGcontext* const vg, const uint offset, const char* const text) + { + const float y = startY + offset * padding; + nvgBeginPath(vg); + nvgFillColor(vg, color::WHITE); + nvgText(vg, middleX, y + 16, text, nullptr); + } + void draw(const DrawArgs& args) override { nvgBeginPath(args.vg); @@ -511,18 +511,18 @@ struct CarlaModuleWidget : ModuleWidget { [=]() {showUI();} )); - menu->addChild(createCheckMenuItem("Bipolar Inputs", "", + menu->addChild(createCheckMenuItem("Bipolar CV Inputs", "", [=]() {return module->params[CarlaModule::BIPOLAR_INPUTS].getValue() > 0.1f;}, [=]() {module->params[CarlaModule::BIPOLAR_INPUTS].setValue(1.0f - module->params[CarlaModule::BIPOLAR_INPUTS].getValue());} )); - menu->addChild(createCheckMenuItem("Bipolar Outputs", "", + menu->addChild(createCheckMenuItem("Bipolar CV Outputs", "", [=]() {return module->params[CarlaModule::BIPOLAR_OUTPUTS].getValue() > 0.1f;}, [=]() {module->params[CarlaModule::BIPOLAR_OUTPUTS].setValue(1.0f - module->params[CarlaModule::BIPOLAR_OUTPUTS].getValue());} )); } - void onDoubleClick(const DoubleClickEvent& e) + void onDoubleClick(const DoubleClickEvent& e) override { e.consume(this); showUI(); diff --git a/plugins/Cardinal/src/HostCV.cpp b/plugins/Cardinal/src/HostCV.cpp new file mode 100644 index 0000000..cb98370 --- /dev/null +++ b/plugins/Cardinal/src/HostCV.cpp @@ -0,0 +1,172 @@ +/* + * DISTRHO Cardinal Plugin + * Copyright (C) 2021 Filipe Coelho + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 3 of + * the License, or any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * For a full copy of the GNU General Public License see the LICENSE file. + */ + +#include "plugincontext.hpp" + +// ----------------------------------------------------------------------------------------------------------- + +USE_NAMESPACE_DISTRHO; + +struct HostCV : Module { + enum ParamIds { + BIPOLAR_INPUTS, + BIPOLAR_OUTPUTS, + NUM_PARAMS + }; + enum InputIds { + NUM_INPUTS = 10 + }; + enum OutputIds { + NUM_OUTPUTS = 10 + }; + enum LightIds { + NUM_LIGHTS + }; + + HostCV() + { + config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS); + configParam(BIPOLAR_INPUTS, 0.f, 1.f, 1.f, "Bipolar Inputs")->randomizeEnabled = false; + configParam(BIPOLAR_OUTPUTS, 0.f, 1.f, 1.f, "Bipolar Outputs")->randomizeEnabled = false; + + CardinalPluginContext* const pcontext = reinterpret_cast(APP); + + if (pcontext == nullptr) + throw rack::Exception("Plugin context is null."); + + if (pcontext->loadedHostCV) + throw rack::Exception("Another instance of a Host CV module is already loaded, only one can be used at a time."); + + pcontext->loadedHostCV = true; + } + + ~HostCV() override + { + CardinalPluginContext* const pcontext = reinterpret_cast(APP); + DISTRHO_SAFE_ASSERT_RETURN(pcontext != nullptr,); + + pcontext->loadedHostCV = false; + } + + void process(const ProcessArgs&) override + { + if (CardinalPluginContext* const pcontext = reinterpret_cast(APP)) + { + const float** dataIns = pcontext->dataIns; + float** dataOuts = pcontext->dataOuts; + + if (dataIns == nullptr || dataOuts == nullptr) + return; + + const uint32_t dataFrame = pcontext->dataFrame++; + const float inputOffset = params[BIPOLAR_INPUTS].getValue() > 0.1f ? 5.0f : 0.0f; + const float outputOffset = params[BIPOLAR_OUTPUTS].getValue() > 0.1f ? 5.0f : 0.0f; + + for (int i=0; i<10; ++i) + { + outputs[i].setVoltage(dataIns[i+2][dataFrame] - inputOffset); + dataOuts[i+2][dataFrame] = inputs[i].getVoltage() + outputOffset; + } + } + } +}; + +struct HostCVWidget : ModuleWidget { + static constexpr const float startX_In = 14.0f; + static constexpr const float startX_Out = 96.0f; + static constexpr const float startY = 74.0f; + static constexpr const float padding = 29.0f; + static constexpr const float middleX = startX_In + (startX_Out - startX_In) * 0.5f + padding * 0.25f; + + HostCVWidget(HostCV* const module) + { + setModule(module); + setPanel(APP->window->loadSvg(asset::plugin(pluginInstance, "res/HostCV.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))); + + for (uint i=0; i(Vec(startX_In, startY + padding * i), module, i)); + + for (uint i=0; i(Vec(startX_Out, startY + padding * i), module, i)); + } + + void drawTextLine(NVGcontext* const vg, const uint offset, const char* const text) + { + const float y = startY + offset * padding; + nvgBeginPath(vg); + nvgFillColor(vg, color::WHITE); + nvgText(vg, middleX, y + 16, text, nullptr); + } + + void draw(const DrawArgs& args) override + { + nvgBeginPath(args.vg); + nvgRect(args.vg, 0, 0, box.size.x, box.size.y); + nvgFillPaint(args.vg, nvgLinearGradient(args.vg, 0, 0, 0, box.size.y, + nvgRGB(0x18, 0x19, 0x19), nvgRGB(0x21, 0x22, 0x22))); + nvgFill(args.vg); + + nvgFontFaceId(args.vg, 0); + nvgFontSize(args.vg, 11); + nvgTextAlign(args.vg, NVG_ALIGN_CENTER); + // nvgTextBounds(vg, 0, 0, text, nullptr, nullptr); + + nvgBeginPath(args.vg); + nvgRoundedRect(args.vg, startX_Out - 4.0f, startY - 2.0f, padding, padding * HostCV::NUM_INPUTS, 4); + nvgFillColor(args.vg, nvgRGB(0xd0, 0xd0, 0xd0)); + nvgFill(args.vg); + + drawTextLine(args.vg, 0, "CV 1"); + drawTextLine(args.vg, 1, "CV 2"); + drawTextLine(args.vg, 2, "CV 3"); + drawTextLine(args.vg, 3, "CV 4"); + drawTextLine(args.vg, 4, "CV 5"); + drawTextLine(args.vg, 5, "CV 6"); + drawTextLine(args.vg, 6, "CV 7"); + drawTextLine(args.vg, 7, "CV 8"); + drawTextLine(args.vg, 8, "CV 9"); + drawTextLine(args.vg, 9, "CV 10"); + + ModuleWidget::draw(args); + } + + void appendContextMenu(ui::Menu* const menu) override + { + menu->addChild(new ui::MenuSeparator); + + menu->addChild(createCheckMenuItem("Bipolar Inputs", "", + [=]() {return module->params[HostCV::BIPOLAR_INPUTS].getValue() > 0.1f;}, + [=]() {module->params[HostCV::BIPOLAR_INPUTS].setValue(1.0f - module->params[HostCV::BIPOLAR_INPUTS].getValue());} + )); + + menu->addChild(createCheckMenuItem("Bipolar Outputs", "", + [=]() {return module->params[HostCV::BIPOLAR_OUTPUTS].getValue() > 0.1f;}, + [=]() {module->params[HostCV::BIPOLAR_OUTPUTS].setValue(1.0f - module->params[HostCV::BIPOLAR_OUTPUTS].getValue());} + )); + } +}; + +// -------------------------------------------------------------------------------------------------------------------- + +Model* modelHostCV = createModel("HostCV"); + +// -------------------------------------------------------------------------------------------------------------------- diff --git a/plugins/Cardinal/src/plugin.hpp b/plugins/Cardinal/src/plugin.hpp index f54146a..f6ed020 100644 --- a/plugins/Cardinal/src/plugin.hpp +++ b/plugins/Cardinal/src/plugin.hpp @@ -29,6 +29,7 @@ extern Plugin* pluginInstance; extern Model* modelCarla; extern Model* modelGlBars; -extern Model* modelIldaeil; +extern Model* modelHostCV; extern Model* modelHostParameters; extern Model* modelHostTime; +extern Model* modelIldaeil; diff --git a/plugins/Cardinal/src/plugincontext.hpp b/plugins/Cardinal/src/plugincontext.hpp index 339f285..cc952e0 100644 --- a/plugins/Cardinal/src/plugincontext.hpp +++ b/plugins/Cardinal/src/plugincontext.hpp @@ -33,12 +33,15 @@ struct CardinalPluginContext : rack::Context { uint32_t bufferSize; double sampleRate; float parameters[kModuleParameters]; - bool playing, reset, bbtValid; + bool playing, reset, bbtValid, loadedHostCV; int32_t bar, beat, beatsPerBar, beatType; uint64_t frame; double barStartTick, beatsPerMinute; double tick, tickClock, ticksPerBeat, ticksPerClock, ticksPerFrame; uintptr_t nativeWindowId; + uint32_t dataFrame; + const float** dataIns; + float** dataOuts; Plugin* const plugin; CardinalPluginContext(Plugin* const p); }; diff --git a/plugins/Makefile b/plugins/Makefile index d4233b2..68695ed 100644 --- a/plugins/Makefile +++ b/plugins/Makefile @@ -179,6 +179,7 @@ PLUGIN_FILES = plugins.cpp PLUGIN_FILES += Cardinal/src/Carla.cpp PLUGIN_FILES += Cardinal/src/glBars.cpp +PLUGIN_FILES += Cardinal/src/HostCV.cpp PLUGIN_FILES += Cardinal/src/HostParameters.cpp PLUGIN_FILES += Cardinal/src/HostTime.cpp PLUGIN_FILES += Cardinal/src/Ildaeil.cpp diff --git a/plugins/plugins.cpp b/plugins/plugins.cpp index 49d9f93..27c0c45 100644 --- a/plugins/plugins.cpp +++ b/plugins/plugins.cpp @@ -407,9 +407,10 @@ static void initStatic__Cardinal() { p->addModel(modelCarla); p->addModel(modelGlBars); - p->addModel(modelIldaeil); + p->addModel(modelHostCV); p->addModel(modelHostParameters); p->addModel(modelHostTime); + p->addModel(modelIldaeil); } } diff --git a/src/CardinalPlugin.cpp b/src/CardinalPlugin.cpp index 8ff62f0..9346ded 100644 --- a/src/CardinalPlugin.cpp +++ b/src/CardinalPlugin.cpp @@ -875,6 +875,12 @@ protected: #endif } +#if DISTRHO_PLUGIN_NUM_OUTPUTS != 2 + context->dataFrame = 0; + context->dataIns = inputs; + context->dataOuts = outputs; +#endif + context->engine->stepBlock(frames); if (fCurrentAudioDevice != nullptr) diff --git a/src/PluginContext.hpp b/src/PluginContext.hpp index e34d684..453db61 100644 --- a/src/PluginContext.hpp +++ b/src/PluginContext.hpp @@ -40,12 +40,15 @@ struct CardinalPluginContext : rack::Context { uint32_t bufferSize; double sampleRate; float parameters[kModuleParameters]; - bool playing, reset, bbtValid; + bool playing, reset, bbtValid, loadedHostCV; int32_t bar, beat, beatsPerBar, beatType; uint64_t frame; double barStartTick, beatsPerMinute; double tick, tickClock, ticksPerBeat, ticksPerClock, ticksPerFrame; uintptr_t nativeWindowId; + uint32_t dataFrame; + const float** dataIns; + float** dataOuts; Plugin* const plugin; CardinalPluginContext(Plugin* const p) @@ -54,6 +57,7 @@ struct CardinalPluginContext : rack::Context { playing(false), reset(false), bbtValid(false), + loadedHostCV(false), bar(1), beat(1), beatsPerBar(4), @@ -67,6 +71,9 @@ struct CardinalPluginContext : rack::Context { ticksPerClock(0.0), ticksPerFrame(0.0), nativeWindowId(0), + dataFrame(0), + dataIns(nullptr), + dataOuts(nullptr), plugin(p) { std::memset(parameters, 0, sizeof(parameters));