diff --git a/carla b/carla
index d691978..1edb1bb 160000
--- a/carla
+++ b/carla
@@ -1 +1 @@
-Subproject commit d6919785586228020e347bf24288b8317e0b24ef
+Subproject commit 1edb1bb172fce8ae89cf666675ff531fc5dca628
diff --git a/plugins/Cardinal/orig/Carla.svg b/plugins/Cardinal/orig/Carla.svg
new file mode 100644
index 0000000..780a92e
--- /dev/null
+++ b/plugins/Cardinal/orig/Carla.svg
@@ -0,0 +1,151 @@
+
+
+
+
diff --git a/plugins/Cardinal/plugin.json b/plugins/Cardinal/plugin.json
index 3228ec9..894d497 100644
--- a/plugins/Cardinal/plugin.json
+++ b/plugins/Cardinal/plugin.json
@@ -40,6 +40,15 @@
"Visual"
]
},
+ {
+ "slug": "Carla",
+ "disabled": false,
+ "name": "Carla Plugin Host",
+ "description": "A modular plugin host within Cardinal",
+ "tags": [
+ "Utility"
+ ]
+ },
{
"slug": "Ildaeil",
"disabled": false,
diff --git a/plugins/Cardinal/res/Carla.svg b/plugins/Cardinal/res/Carla.svg
new file mode 100644
index 0000000..e69dc97
--- /dev/null
+++ b/plugins/Cardinal/res/Carla.svg
@@ -0,0 +1,205 @@
+
+
+
+
diff --git a/plugins/Cardinal/src/Carla.cpp b/plugins/Cardinal/src/Carla.cpp
new file mode 100644
index 0000000..40ae9fe
--- /dev/null
+++ b/plugins/Cardinal/src/Carla.cpp
@@ -0,0 +1,553 @@
+/*
+ * 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"
+
+#include "CarlaNativePlugin.h"
+#include "CarlaBackendUtils.hpp"
+#include "CarlaEngine.hpp"
+#include "water/streams/MemoryOutputStream.h"
+#include "water/xml/XmlDocument.h"
+
+extern "C" {
+// private method that takes ownership, we can use it to avoid superfulous allocations
+json_t *jsonp_stringn_nocheck_own(const char* value, size_t len);
+}
+
+#define BUFFER_SIZE 128
+
+// generates a warning if this is defined as anything else
+#define CARLA_API
+
+// --------------------------------------------------------------------------------------------------------------------
+
+using namespace CarlaBackend;
+
+static uint32_t host_get_buffer_size(NativeHostHandle);
+static double host_get_sample_rate(NativeHostHandle);
+static bool host_is_offline(NativeHostHandle);
+static const NativeTimeInfo* host_get_time_info(NativeHostHandle handle);
+static bool host_write_midi_event(NativeHostHandle handle, const NativeMidiEvent* event);
+static void host_ui_parameter_changed(NativeHostHandle handle, uint32_t index, float value);
+static void host_ui_midi_program_changed(NativeHostHandle handle, uint8_t channel, uint32_t bank, uint32_t program);
+static void host_ui_custom_data_changed(NativeHostHandle handle, const char* key, const char* value);
+static void host_ui_closed(NativeHostHandle handle);
+static const char* host_ui_open_file(NativeHostHandle handle, bool isDir, const char* title, const char* filter);
+static const char* host_ui_save_file(NativeHostHandle handle, bool isDir, const char* title, const char* filter);
+static intptr_t host_dispatcher(NativeHostHandle handle, NativeHostDispatcherOpcode opcode, int32_t index, intptr_t value, void* ptr, float opt);
+
+// --------------------------------------------------------------------------------------------------------------------
+
+struct CarlaModule : Module {
+ enum ParamIds {
+ BIPOLAR_INPUTS,
+ BIPOLAR_OUTPUTS,
+ NUM_PARAMS
+ };
+ enum InputIds {
+ AUDIO_INPUT1,
+ AUDIO_INPUT2,
+ CV_INPUT1,
+ NUM_INPUTS = CV_INPUT1 + 8
+ };
+ enum OutputIds {
+ AUDIO_OUTPUT1,
+ AUDIO_OUTPUT2,
+ CV_OUTPUT1,
+ NUM_OUTPUTS = CV_OUTPUT1 + 8
+ };
+ enum LightIds {
+ NUM_LIGHTS
+ };
+
+ CardinalPluginContext* const pcontext;
+
+ const NativePluginDescriptor* fCarlaPluginDescriptor = nullptr;
+ NativePluginHandle fCarlaPluginHandle = nullptr;
+
+ NativeHostDescriptor fCarlaHostDescriptor = {};
+ CarlaHostHandle fCarlaHostHandle = nullptr;
+
+ mutable NativeTimeInfo fCarlaTimeInfo;
+
+ void* fUI = nullptr;
+
+ float dataIn[NUM_INPUTS][BUFFER_SIZE];
+ float dataOut[NUM_OUTPUTS][BUFFER_SIZE];
+ float* dataInPtr[NUM_INPUTS];
+ float* dataOutPtr[NUM_OUTPUTS];
+ unsigned audioDataFill = 0;
+ std::string patchStorage;
+
+ CarlaModule()
+ : pcontext(reinterpret_cast(APP))
+ {
+ config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
+ configParam(BIPOLAR_INPUTS, 0.f, 1.f, 1.f, "Bipolar CV Inputs")->randomizeEnabled = false;
+ configParam(BIPOLAR_OUTPUTS, 0.f, 1.f, 1.f, "Bipolar CV Outputs")->randomizeEnabled = false;
+
+ for (uint i=0; i('0'+i+1),'\0' };
+ configInput(i, name);
+ configOutput(i, name);
+ }
+ for (uint i=2; i('0'+i-1),'\0' };
+ configInput(i, name);
+ configOutput(i, name);
+ }
+
+ std::memset(dataOut, 0, sizeof(dataOut));
+
+ fCarlaPluginDescriptor = carla_get_native_patchbay_cv8_plugin();
+ DISTRHO_SAFE_ASSERT_RETURN(fCarlaPluginDescriptor != nullptr,);
+
+ memset(&fCarlaHostDescriptor, 0, sizeof(fCarlaHostDescriptor));
+ memset(&fCarlaTimeInfo, 0, sizeof(fCarlaTimeInfo));
+
+ fCarlaHostDescriptor.handle = this;
+#ifdef CARLA_OS_MAC
+ fCarlaHostDescriptor.resourceDir = "/Applications/Carla.app/Contents/MacOS/resources";
+#else
+ fCarlaHostDescriptor.resourceDir = "/usr/share/carla/resources";
+#endif
+ fCarlaHostDescriptor.uiName = "Carla";
+ fCarlaHostDescriptor.uiParentId = 0;
+
+ fCarlaHostDescriptor.get_buffer_size = host_get_buffer_size;
+ fCarlaHostDescriptor.get_sample_rate = host_get_sample_rate;
+ fCarlaHostDescriptor.is_offline = host_is_offline;
+
+ fCarlaHostDescriptor.get_time_info = host_get_time_info;
+ fCarlaHostDescriptor.write_midi_event = host_write_midi_event;
+ fCarlaHostDescriptor.ui_parameter_changed = host_ui_parameter_changed;
+ fCarlaHostDescriptor.ui_midi_program_changed = host_ui_midi_program_changed;
+ fCarlaHostDescriptor.ui_custom_data_changed = host_ui_custom_data_changed;
+ fCarlaHostDescriptor.ui_closed = host_ui_closed;
+ fCarlaHostDescriptor.ui_open_file = host_ui_open_file;
+ fCarlaHostDescriptor.ui_save_file = host_ui_save_file;
+ fCarlaHostDescriptor.dispatcher = host_dispatcher;
+
+ fCarlaPluginHandle = fCarlaPluginDescriptor->instantiate(&fCarlaHostDescriptor);
+ DISTRHO_SAFE_ASSERT_RETURN(fCarlaPluginHandle != nullptr,);
+
+ fCarlaHostHandle = carla_create_native_plugin_host_handle(fCarlaPluginDescriptor, fCarlaPluginHandle);
+ DISTRHO_SAFE_ASSERT_RETURN(fCarlaHostHandle != nullptr,);
+
+#ifdef CARLA_OS_MAC
+ carla_set_engine_option(fCarlaHostHandle, ENGINE_OPTION_PATH_BINARIES, 0, "/Applications/Carla.app/Contents/MacOS");
+ carla_set_engine_option(fCarlaHostHandle, ENGINE_OPTION_PATH_RESOURCES, 0, "/Applications/Carla.app/Contents/MacOS/resources");
+#else
+ carla_set_engine_option(fCarlaHostHandle, ENGINE_OPTION_PATH_BINARIES, 0, "/usr/lib/carla");
+ carla_set_engine_option(fCarlaHostHandle, ENGINE_OPTION_PATH_RESOURCES, 0, "/usr/share/carla/resources");
+#endif
+
+ fCarlaPluginDescriptor->dispatcher(fCarlaPluginHandle, NATIVE_PLUGIN_OPCODE_HOST_USES_EMBED,
+ 0, 0, nullptr, 0.0f);
+
+ fCarlaPluginDescriptor->activate(fCarlaPluginHandle);
+ }
+
+ ~CarlaModule() override
+ {
+ if (fCarlaPluginHandle != nullptr)
+ fCarlaPluginDescriptor->deactivate(fCarlaPluginHandle);
+
+ if (fCarlaHostHandle != nullptr)
+ carla_host_handle_free(fCarlaHostHandle);
+
+ if (fCarlaPluginHandle != nullptr)
+ fCarlaPluginDescriptor->cleanup(fCarlaPluginHandle);
+ }
+
+ const NativeTimeInfo* hostGetTimeInfo() const noexcept
+ {
+ if (pcontext != nullptr)
+ {
+ fCarlaTimeInfo.playing = pcontext->playing;
+ fCarlaTimeInfo.frame = pcontext->frame;
+ fCarlaTimeInfo.bbt.valid = pcontext->bbtValid;
+ fCarlaTimeInfo.bbt.bar = pcontext->bar;
+ fCarlaTimeInfo.bbt.beat = pcontext->beat;
+ fCarlaTimeInfo.bbt.tick = pcontext->tick;
+ fCarlaTimeInfo.bbt.barStartTick = pcontext->barStartTick;
+ fCarlaTimeInfo.bbt.beatsPerBar = pcontext->beatsPerBar;
+ fCarlaTimeInfo.bbt.beatType = pcontext->beatType;
+ fCarlaTimeInfo.bbt.ticksPerBeat = pcontext->ticksPerBeat;
+ fCarlaTimeInfo.bbt.beatsPerMinute = pcontext->beatsPerMinute;
+ }
+
+ return &fCarlaTimeInfo;
+ }
+
+ intptr_t hostDispatcher(const NativeHostDispatcherOpcode opcode,
+ const int32_t index, const intptr_t value, void* const ptr, const float opt)
+ {
+ switch (opcode)
+ {
+ // cannnot be supported
+ case NATIVE_HOST_OPCODE_HOST_IDLE:
+ break;
+ // other stuff
+ case NATIVE_HOST_OPCODE_NULL:
+ case NATIVE_HOST_OPCODE_UPDATE_PARAMETER:
+ case NATIVE_HOST_OPCODE_UPDATE_MIDI_PROGRAM:
+ case NATIVE_HOST_OPCODE_RELOAD_PARAMETERS:
+ case NATIVE_HOST_OPCODE_RELOAD_MIDI_PROGRAMS:
+ case NATIVE_HOST_OPCODE_RELOAD_ALL:
+ case NATIVE_HOST_OPCODE_UI_UNAVAILABLE:
+ case NATIVE_HOST_OPCODE_INTERNAL_PLUGIN:
+ case NATIVE_HOST_OPCODE_QUEUE_INLINE_DISPLAY:
+ case NATIVE_HOST_OPCODE_UI_TOUCH_PARAMETER:
+ case NATIVE_HOST_OPCODE_REQUEST_IDLE:
+ case NATIVE_HOST_OPCODE_UI_RESIZE:
+ case NATIVE_HOST_OPCODE_PREVIEW_BUFFER_DATA:
+ // TESTING
+ d_stdout("dispatcher %i, %i, %li, %p, %f", opcode, index, value, ptr, opt);
+ break;
+ case NATIVE_HOST_OPCODE_GET_FILE_PATH:
+ return (intptr_t)(void*)patchStorage.c_str();
+ break;
+ }
+
+ return 0;
+ }
+
+ json_t* dataToJson() override
+ {
+ if (fCarlaHostHandle == nullptr)
+ return nullptr;
+
+ CarlaEngine* const engine = carla_get_engine_from_handle(fCarlaHostHandle);
+
+ water::MemoryOutputStream projectState;
+ engine->saveProjectInternal(projectState);
+
+ const size_t dataSize = projectState.getDataSize();
+ return jsonp_stringn_nocheck_own(static_cast(projectState.getDataAndRelease()), dataSize);
+ }
+
+ void dataFromJson(json_t* const rootJ) override
+ {
+ if (fCarlaHostHandle == nullptr)
+ return;
+
+ const char* const projectState = json_string_value(rootJ);
+ DISTRHO_SAFE_ASSERT_RETURN(projectState != nullptr,);
+
+ CarlaEngine* const engine = carla_get_engine_from_handle(fCarlaHostHandle);
+
+ water::XmlDocument xml(projectState);
+ engine->loadProjectInternal(xml, true);
+ }
+
+ void onAdd(const AddEvent&) override
+ {
+ patchStorage = getPatchStorageDirectory();
+ }
+
+ void process(const ProcessArgs&) override
+ {
+ if (fCarlaPluginHandle == nullptr)
+ return;
+
+ 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;
+
+ const unsigned k = audioDataFill++;
+
+ for (uint i=0; i<2; ++i)
+ dataIn[i][k] = inputs[i].getVoltage() * 0.1f;
+ for (uint i=2; iprocess(fCarlaPluginHandle, dataInPtr, dataOutPtr, BUFFER_SIZE, nullptr, 0);
+ }
+ }
+
+ void onSampleRateChange(const SampleRateChangeEvent& e) override
+ {
+ if (fCarlaPluginHandle == nullptr)
+ return;
+
+ fCarlaPluginDescriptor->deactivate(fCarlaPluginHandle);
+ fCarlaPluginDescriptor->dispatcher(fCarlaPluginHandle, NATIVE_PLUGIN_OPCODE_SAMPLE_RATE_CHANGED,
+ 0, 0, nullptr, e.sampleRate);
+ fCarlaPluginDescriptor->activate(fCarlaPluginHandle);
+ }
+};
+
+static_assert((int)CarlaModule::NUM_INPUTS == (int)CarlaModule::NUM_OUTPUTS, "inputs must match outputs");
+
+// -----------------------------------------------------------------------------------------------------------
+
+static uint32_t host_get_buffer_size(const NativeHostHandle handle)
+{
+ return BUFFER_SIZE;
+}
+
+static double host_get_sample_rate(const NativeHostHandle handle)
+{
+ CardinalPluginContext* const pcontext = static_cast(handle)->pcontext;
+ DISTRHO_SAFE_ASSERT_RETURN(pcontext != nullptr, 48000.0);
+ return pcontext->sampleRate;
+}
+
+static bool host_is_offline(NativeHostHandle)
+{
+ return false;
+}
+
+static const NativeTimeInfo* host_get_time_info(const NativeHostHandle handle)
+{
+ return static_cast(handle)->hostGetTimeInfo();
+}
+
+static bool host_write_midi_event(const NativeHostHandle handle, const NativeMidiEvent* const event)
+{
+ return false;
+}
+
+static void host_ui_midi_program_changed(NativeHostHandle handle, uint8_t channel, uint32_t bank, uint32_t program)
+{
+ d_stdout("%s %p %u %u %u", __FUNCTION__, handle, channel, bank, program);
+}
+
+static void host_ui_custom_data_changed(NativeHostHandle handle, const char* key, const char* value)
+{
+ d_stdout("%s %p %s %s", __FUNCTION__, handle, key, value);
+}
+
+static const char* host_ui_save_file(NativeHostHandle, bool, const char*, const char*)
+{
+ return nullptr;
+}
+
+static intptr_t host_dispatcher(const NativeHostHandle handle, const NativeHostDispatcherOpcode opcode,
+ const int32_t index, const intptr_t value, void* const ptr, const float opt)
+{
+ return static_cast(handle)->hostDispatcher(opcode, index, value, ptr, opt);
+}
+
+// --------------------------------------------------------------------------------------------------------------------
+
+struct CarlaModuleWidget : 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;
+
+ CarlaModule* const module;
+ bool visible = false;
+
+ CarlaModuleWidget(CarlaModule* const m)
+ : ModuleWidget(),
+ module(m)
+ {
+ setModule(module);
+ setPanel(APP->window->loadSvg(asset::plugin(pluginInstance, "res/Carla.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));
+
+ if (module != nullptr && module->fCarlaHostHandle != nullptr)
+ {
+ const CarlaHostHandle handle = module->fCarlaHostHandle;
+
+ char winIdStr[24];
+ std::snprintf(winIdStr, sizeof(winIdStr), "%lx", (ulong)module->pcontext->nativeWindowId);
+ carla_set_engine_option(handle, ENGINE_OPTION_FRONTEND_WIN_ID, 0, winIdStr);
+ module->fCarlaHostDescriptor.uiParentId = module->pcontext->nativeWindowId;
+ /*
+ carla_set_engine_option(handle, ENGINE_OPTION_FRONTEND_UI_SCALE, getScaleFactor()*1000, nullptr);
+ */
+
+ module->fUI = this;
+ }
+ }
+
+ ~CarlaModuleWidget() override
+ {
+ if (module != nullptr && module->fCarlaHostHandle != nullptr)
+ {
+ module->fUI = nullptr;
+
+ if (visible)
+ module->fCarlaPluginDescriptor->ui_show(module->fCarlaPluginHandle, false);
+
+ module->fCarlaHostDescriptor.uiParentId = 0;
+ carla_set_engine_option(module->fCarlaHostHandle, ENGINE_OPTION_FRONTEND_WIN_ID, 0, "0");
+ }
+ }
+
+ 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);
+
+ if (module == nullptr || module->pcontext == nullptr || module->fCarlaHostHandle == nullptr)
+ return;
+
+ char winIdStr[24];
+ std::snprintf(winIdStr, sizeof(winIdStr), "%lx", (ulong)module->pcontext->nativeWindowId);
+ carla_set_engine_option(module->fCarlaHostHandle, ENGINE_OPTION_FRONTEND_WIN_ID, 0, winIdStr);
+ module->fCarlaHostDescriptor.uiParentId = module->pcontext->nativeWindowId;
+ }
+
+ void onContextDestroy(const ContextDestroyEvent& e) override
+ {
+ if (module != nullptr && module->fCarlaHostHandle != nullptr)
+ {
+ module->fCarlaHostDescriptor.uiParentId = 0;
+ carla_set_engine_option(module->fCarlaHostHandle, ENGINE_OPTION_FRONTEND_WIN_ID, 0, "0");
+ }
+
+ ModuleWidget::onContextDestroy(e);
+ }
+
+ 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 * CarlaModule::NUM_INPUTS, 4);
+ nvgFillColor(args.vg, nvgRGB(0xd0, 0xd0, 0xd0));
+ nvgFill(args.vg);
+
+ drawTextLine(args.vg, 0, "Audio 1");
+ drawTextLine(args.vg, 1, "Audio 2");
+ drawTextLine(args.vg, 2, "CV 1");
+ drawTextLine(args.vg, 3, "CV 2");
+ drawTextLine(args.vg, 4, "CV 3");
+ drawTextLine(args.vg, 5, "CV 4");
+ drawTextLine(args.vg, 6, "CV 5");
+ drawTextLine(args.vg, 7, "CV 6");
+ drawTextLine(args.vg, 8, "CV 7");
+ drawTextLine(args.vg, 9, "CV 8");
+
+ ModuleWidget::draw(args);
+ }
+
+ void step() override
+ {
+ if (module != nullptr && module->fCarlaHostHandle != nullptr && visible)
+ module->fCarlaPluginDescriptor->ui_idle(module->fCarlaPluginHandle);
+
+ ModuleWidget::step();
+ }
+
+ void showUI()
+ {
+ visible = true;
+ module->fCarlaPluginDescriptor->ui_show(module->fCarlaPluginHandle, true);
+ }
+
+ void appendContextMenu(ui::Menu* const menu) override
+ {
+ menu->addChild(new ui::MenuSeparator);
+
+ menu->addChild(createCheckMenuItem(visible ? "Bring GUI to Front" : "Show GUI", "",
+ [=]() {return visible;},
+ [=]() {showUI();}
+ ));
+
+ menu->addChild(createCheckMenuItem("Bipolar 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", "",
+ [=]() {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)
+ {
+ e.consume(this);
+ showUI();
+ }
+};
+
+// --------------------------------------------------------------------------------------------------------------------
+
+static void host_ui_closed(NativeHostHandle handle)
+{
+ if (CarlaModuleWidget* const ui = static_cast(static_cast(handle)->fUI))
+ ui->visible = false;
+}
+
+static void host_ui_parameter_changed(const NativeHostHandle handle, const uint32_t index, const float value)
+{
+// if (CarlaWidget* const ui = static_cast(static_cast(handle)->fUI))
+// ui->changeParameterFromDSP(index, value);
+}
+
+static const char* host_ui_open_file(const NativeHostHandle handle,
+ const bool isDir, const char* const title, const char* const filter)
+{
+// if (CarlaWidget* const ui = static_cast(static_cast(handle)->fUI))
+// ui->openFileFromDSP(isDir, title, filter);
+
+ return nullptr;
+}
+
+// --------------------------------------------------------------------------------------------------------------------
+
+Model* modelCarla = createModel("Carla");
+
+// --------------------------------------------------------------------------------------------------------------------
diff --git a/plugins/Cardinal/src/HostParameters.cpp b/plugins/Cardinal/src/HostParameters.cpp
index 4b69d59..87cb8a3 100644
--- a/plugins/Cardinal/src/HostParameters.cpp
+++ b/plugins/Cardinal/src/HostParameters.cpp
@@ -123,10 +123,8 @@ struct HostParametersWidget : ModuleWidget {
const float y = startY + int(i % 6) * paddingV;
nvgBeginPath(args.vg);
nvgRoundedRect(args.vg, x - 1.0f, y - 19.0f, paddingH - 4.0f, paddingV - 4.0f, 4);
- nvgFillColor(args.vg, nvgRGBA(0xda, 0xda, 0xda, 0xf0));
+ nvgFillColor(args.vg, nvgRGB(0xd0, 0xd0, 0xd0));
nvgFill(args.vg);
- nvgStrokeColor(args.vg, nvgRGBA(0x4a, 0x4a, 0x4a, 0xc0));
- nvgStroke(args.vg);
if (text[1]++ == '9')
{
diff --git a/plugins/Cardinal/src/HostTime.cpp b/plugins/Cardinal/src/HostTime.cpp
index fbd7416..87caa5b 100644
--- a/plugins/Cardinal/src/HostTime.cpp
+++ b/plugins/Cardinal/src/HostTime.cpp
@@ -53,8 +53,9 @@ struct HostTime : Module {
if (CardinalPluginContext* const pcontext = reinterpret_cast(APP))
{
const bool playing = pcontext->playing;
+ const bool playingWithBBT = playing && pcontext->bbtValid;
- if (playing)
+ if (playingWithBBT)
{
if (pcontext->tick == 0.0)
{
@@ -95,8 +96,12 @@ struct HostTime : Module {
const bool hasBar = pulseBar.process(args.sampleTime);
const bool hasBeat = pulseBeat.process(args.sampleTime);
const bool hasClock = pulseClock.process(args.sampleTime);
- const float beatPhase = pcontext->ticksPerBeat > 0.0 ? pcontext->tick / pcontext->ticksPerBeat : 0.0;
- const float barPhase = pcontext->beatsPerBar > 0 ? ((float) (pcontext->beat - 1) + beatPhase) / pcontext->beatsPerBar : 0.0;
+ const float beatPhase = playingWithBBT && pcontext->ticksPerBeat > 0.0
+ ? pcontext->tick / pcontext->ticksPerBeat
+ : 0.0f;
+ const float barPhase = playingWithBBT && pcontext->beatsPerBar > 0
+ ? ((float) (pcontext->beat - 1) + beatPhase) / pcontext->beatsPerBar
+ : 0.0f;
lights[kHostTimeRolling].setBrightness(playing ? 1.0f : 0.0f);
lights[kHostTimeReset].setBrightnessSmooth(hasReset ? 1.0f : 0.0f, args.sampleTime * 0.5f);
@@ -155,10 +160,10 @@ struct HostTimeWidget : ModuleWidget {
const float y = startY + offset * padding;
nvgBeginPath(vg);
nvgRoundedRect(vg, startX - 1.0f, y - 2.0f, box.size.x - (startX + 1) * 2, 28.0f, 4);
- nvgFillColor(vg, nvgRGBA(0xda, 0xda, 0xda, 0xf0));
+ nvgFillColor(vg, nvgRGB(0xd0, 0xd0, 0xd0));
nvgFill(vg);
- nvgStrokeColor(vg, nvgRGBA(0x4a, 0x4a, 0x4a, 0xc0));
- nvgStroke(vg);
+// nvgStrokeColor(vg, nvgRGBA(0x4a, 0x4a, 0x4a, 0xc0));
+// nvgStroke(vg);
nvgBeginPath(vg);
nvgFillColor(vg, color::BLACK);
nvgText(vg, startX + 36, y + 16, text, nullptr);
diff --git a/plugins/Cardinal/src/Ildaeil.cpp b/plugins/Cardinal/src/Ildaeil.cpp
index 278b06a..d401b06 100644
--- a/plugins/Cardinal/src/Ildaeil.cpp
+++ b/plugins/Cardinal/src/Ildaeil.cpp
@@ -296,8 +296,9 @@ static uint32_t host_get_buffer_size(const NativeHostHandle handle)
static double host_get_sample_rate(const NativeHostHandle handle)
{
- // TODO
- return 48000; // static_cast(handle)->getSampleRate();
+ CardinalPluginContext* const pcontext = static_cast(handle)->pcontext;
+ DISTRHO_SAFE_ASSERT_RETURN(pcontext != nullptr, 48000.0);
+ return pcontext->sampleRate;
}
static bool host_is_offline(NativeHostHandle)
@@ -1140,7 +1141,7 @@ struct IldaeilModuleWidget : ModuleWidget {
IldaeilModuleWidget(IldaeilModule* const module)
{
setModule(module);
- setPanel(APP->window->loadSvg(asset::plugin(pluginInstance, "res/glBars.svg")));
+ setPanel(APP->window->loadSvg(asset::plugin(pluginInstance, "res/Ildaeil.svg")));
if (module != nullptr && module->pcontext != nullptr)
{
diff --git a/plugins/Cardinal/src/plugin.hpp b/plugins/Cardinal/src/plugin.hpp
index f119925..f54146a 100644
--- a/plugins/Cardinal/src/plugin.hpp
+++ b/plugins/Cardinal/src/plugin.hpp
@@ -27,6 +27,7 @@ using namespace rack;
extern Plugin* pluginInstance;
+extern Model* modelCarla;
extern Model* modelGlBars;
extern Model* modelIldaeil;
extern Model* modelHostParameters;
diff --git a/plugins/plugins.cpp b/plugins/plugins.cpp
index 6f0ba10..49d9f93 100644
--- a/plugins/plugins.cpp
+++ b/plugins/plugins.cpp
@@ -405,6 +405,7 @@ static void initStatic__Cardinal()
const StaticPluginLoader spl(p, "Cardinal");
if (spl.ok())
{
+ p->addModel(modelCarla);
p->addModel(modelGlBars);
p->addModel(modelIldaeil);
p->addModel(modelHostParameters);
diff --git a/src/template.vcv b/src/template.vcv
index b8ed41b..8c5a3ad 100644
--- a/src/template.vcv
+++ b/src/template.vcv
@@ -104,7 +104,7 @@
{
"id": 6,
"plugin": "Cardinal",
- "model": "Ildaeil",
+ "model": "Carla",
"version": "2.0",
"params": [],
"leftModuleId": 4,