Browse Source

Add Host Parameters Map module, functional but still WIP

Signed-off-by: falkTX <falktx@falktx.com>
tags/22.06
falkTX 1 month ago
parent
commit
e861389537
Signed by: falkTX <falktx@falktx.com> GPG Key ID: CDBAA37ABC74FBA0
7 changed files with 639 additions and 39 deletions
  1. +9
    -0
      plugins/Cardinal/plugin.json
  2. +37
    -37
      plugins/Cardinal/src/HostMIDI-Map.cpp
  3. +589
    -0
      plugins/Cardinal/src/HostParameters-Map.cpp
  4. +0
    -1
      plugins/Cardinal/src/HostParameters.cpp
  5. +1
    -0
      plugins/Cardinal/src/plugin.hpp
  6. +1
    -1
      plugins/Makefile
  7. +2
    -0
      plugins/plugins.cpp

+ 9
- 0
plugins/Cardinal/plugin.json View File

@@ -90,6 +90,15 @@
"External"
]
},
{
"slug": "HostParametersMap",
"name": "Host Parameters Map",
"description": "Allows host-controlled plugin parameters to control other module parameters",
"manualUrl": "https://github.com/DISTRHO/Cardinal/blob/main/docs/CARDINAL-MODULES.md#host-parameters-map",
"tags": [
"External"
]
},
{
"slug": "HostTime",
"name": "Host Time",


+ 37
- 37
plugins/Cardinal/src/HostMIDI-Map.cpp View File

@@ -59,6 +59,7 @@ struct HostMIDIMap : TerminalModule {
uint32_t lastProcessCounter;
int nextLearningId;
uint8_t channel;
bool bypassed = false;

// from Rack
bool smooth;
@@ -95,12 +96,10 @@ struct HostMIDIMap : TerminalModule {
{
paramHandles[id].color = nvgRGBf(0.76f, 0.11f, 0.22f);
paramHandles[id].text.reserve(25);
valueFilters[id].setTau(1 / 30.f);
pcontext->engine->addParamHandle(&paramHandles[id]);
}

for (int i = 0; i < MAX_MIDI_CONTROL; i++)
valueFilters[i].setTau(1 / 30.f);

divider.setDivision(32);
onReset();
}
@@ -141,13 +140,14 @@ struct HostMIDIMap : TerminalModule {

if (processCounterChanged)
{
bypassed = isBypassed();
lastProcessCounter = processCounter;
midiEvents = pcontext->midiEvents;
midiEventsLeft = pcontext->midiEventCount;
midiEventFrame = 0;
}

if (isBypassed() || !divider.process())
if (bypassed || !divider.process())
{
++midiEventFrame;
return;
@@ -262,21 +262,6 @@ struct HostMIDIMap : TerminalModule {
void processTerminalOutput(const ProcessArgs&) override
{}

void clearMap(int id)
{
nextLearningId = -1;
learningId = -1;
learnedCc = false;
learnedParam = false;

ccs[id] = -1;
values[id] = -1;
pcontext->engine->updateParamHandle(&paramHandles[id], -1, 0, true);
valueFilters[id].reset();
refreshParamHandleText(id);
updateMapLen();
}

// ----------------------------------------------------------------------------------------------------------------
// stuff for resetting state

@@ -308,7 +293,21 @@ struct HostMIDIMap : TerminalModule {
// ----------------------------------------------------------------------------------------------------------------
// stuff called from panel side, must lock engine

// called from onSelect
void clearMap(int id)
{
nextLearningId = -1;
learningId = -1;
learnedCc = false;
learnedParam = false;

ccs[id] = -1;
values[id] = -1;
pcontext->engine->updateParamHandle(&paramHandles[id], -1, 0, true);
valueFilters[id].reset();
refreshParamHandleText(id);
updateMapLen();
}

void enableLearn(const int id)
{
if (learningId == id)
@@ -321,16 +320,6 @@ struct HostMIDIMap : TerminalModule {
learnedParam = false;
}

// called from onDeselect
void disableLearn(const int id)
{
nextLearningId = -1;

if (learningId == id)
learningId = -1;
}

// called from onDeselect
void learnParam(const int id, const int64_t moduleId, const int paramId)
{
pcontext->engine->updateParamHandle(&paramHandles[id], moduleId, paramId, true);
@@ -339,6 +328,16 @@ struct HostMIDIMap : TerminalModule {
updateMapLen();
}

/*
void disableLearn(const int id)
{
nextLearningId = -1;

if (learningId == id)
learningId = -1;
}
*/

// ----------------------------------------------------------------------------------------------------------------
// common utils

@@ -371,14 +370,16 @@ struct HostMIDIMap : TerminalModule {
// this is called during RT!!
void refreshParamHandleText(const int id)
{
char textBuf[25];

if (ccs[id] >= 0)
{
char textBuf[25];
std::sprintf(textBuf, "CC%02d", ccs[id]);
paramHandles[id].text.assign(textBuf);
}
else
std::strcpy(textBuf, "MIDI-Map");
paramHandles[id].text.assign(textBuf);
{
paramHandles[id].text.clear();
}
}

void updateMapLen()
@@ -471,7 +472,6 @@ struct HostMIDIMap : TerminalModule {
struct CardinalMIDIMapChoice : CardinalLedDisplayChoice {
HostMIDIMap* const module;
const int id;
int disableLearnFrames = -1;
ParamWidget* lastTouchedParam = nullptr;

CardinalMIDIMapChoice(HostMIDIMap* const m, const int i)
@@ -576,10 +576,10 @@ struct CardinalMIDIMapChoice : CardinalLedDisplayChoice {
switch (e.button)
{
case GLFW_MOUSE_BUTTON_RIGHT:
APP->scene->rack->touchedParam = lastTouchedParam = nullptr;
module->clearMap(id);
e.consume(this);
break;
// fall-through
case GLFW_MOUSE_BUTTON_LEFT:
APP->scene->rack->touchedParam = lastTouchedParam = nullptr;
module->enableLearn(id);


+ 589
- 0
plugins/Cardinal/src/HostParameters-Map.cpp View File

@@ -0,0 +1,589 @@
/*
* DISTRHO Cardinal Plugin
* Copyright (C) 2021-2022 Filipe Coelho <falktx@falktx.com>
*
* 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.
*/

/**
* This file contains portions from VCVRack's core/MIDIMap.cpp
* Copyright (C) 2016-2021 VCV.
*
* 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 (at your option) any later version.
*/

#include "plugincontext.hpp"
#include "ModuleWidgets.hpp"
#include "Widgets.hpp"

// -----------------------------------------------------------------------------------------------------------

static constexpr const uint8_t MAX_MAPPED_PARAMS = 64;

struct HostParametersMap : TerminalModule {
enum ParamIds {
NUM_PARAMS
};
enum InputIds {
NUM_INPUTS
};
enum OutputIds {
NUM_OUTPUTS
};
enum LightIds {
NUM_LIGHTS
};

struct Mapping {
uint8_t hostParamId = UINT8_MAX;
bool inverted = false;
bool smooth = false;
ParamHandle paramHandle;
};

Mapping mappings[MAX_MAPPED_PARAMS];
dsp::ExponentialFilter valueFilters[MAX_MAPPED_PARAMS];
bool filterInitialized[MAX_MAPPED_PARAMS] = {};
bool valueReached[MAX_MAPPED_PARAMS] = {};

uint8_t numMappedParmeters = 1;
uint8_t learningId = UINT8_MAX;

CardinalPluginContext* const pcontext;
bool parametersChanged[kModuleParameters] = {};
float parameterValues[kModuleParameters];
bool bypassed = false;
bool firstRun = true;
uint32_t lastProcessCounter = 0;

HostParametersMap()
: pcontext(static_cast<CardinalPluginContext*>(APP))
{
if (pcontext == nullptr)
throw rack::Exception("Plugin context is null.");

config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);

for (uint8_t id = 0; id < MAX_MAPPED_PARAMS; ++id)
{
mappings[id].paramHandle.color = nvgRGBf(0.76f, 0.11f, 0.22f);
valueFilters[id].setTau(1 / 30.f);
pcontext->engine->addParamHandle(&mappings[id].paramHandle);
}

std::memcpy(parameterValues, pcontext->parameters, sizeof(parameterValues));
}

~HostParametersMap()
{
if (pcontext == nullptr)
return;

for (uint8_t id = 0; id < MAX_MAPPED_PARAMS; ++id)
pcontext->engine->removeParamHandle(&mappings[id].paramHandle);
}

void onReset() override
{
lastProcessCounter = 0;

// Use NoLock because we're already in an Engine write-lock if Engine::resetModule().
// We also might be in the MIDIMap() constructor, which could cause problems, but when constructing, all ParamHandles will point to no Modules anyway.
clearMaps_NoLock();
numMappedParmeters = 1;
}

void clearMaps_NoLock()
{
learningId = UINT8_MAX;

for (uint8_t id = 0; id < MAX_MAPPED_PARAMS; ++id)
{
pcontext->engine->updateParamHandle_NoLock(&mappings[id].paramHandle, -1, 0, true);
valueReached[id] = false;
valueFilters[id].reset();
mappings[id].hostParamId = UINT8_MAX;
}

firstRun = true;
std::memcpy(parameterValues, pcontext->parameters, sizeof(parameterValues));
std::memset(parametersChanged, 0, sizeof(parametersChanged));
}

void processTerminalInput(const ProcessArgs& args) override
{
const uint32_t processCounter = pcontext->processCounter;

if (lastProcessCounter == processCounter)
return;

lastProcessCounter = processCounter;

if (isBypassed())
return;

for (uint32_t i = 0; i < kModuleParameters; ++i)
{
if (d_isEqual(pcontext->parameters[i], parameterValues[i]))
continue;

parameterValues[i] = pcontext->parameters[i];
parametersChanged[i] = true;
}

for (uint id = 0; id < numMappedParmeters; ++id)
{
ParamHandle& paramHandle(mappings[id].paramHandle);

if (paramHandle.module == nullptr)
continue;

// Get ParamQuantity from ParamHandle
const int paramId = paramHandle.paramId;
ParamQuantity* const paramQuantity = paramHandle.module->paramQuantities[paramId];
if (!paramQuantity)
continue;
if (!paramQuantity->isBounded())
continue;

// Validate hostParamId
const uint8_t hostParamId = mappings[id].hostParamId;
if (hostParamId >= kModuleParameters)
continue;

// Set filter from param value if filter is uninitialized
if (!filterInitialized[id])
{
valueFilters[id].out = paramQuantity->getScaledValue();
filterInitialized[id] = true;
continue;
}

// Check if parameter was changed by the host
if (parametersChanged[hostParamId] && !firstRun)
valueReached[id] = false;
else if (valueReached[id])
continue;

// Apply value, smooth as needed.
const float value = 0.1f * (mappings[id].inverted ? 10.f - parameterValues[hostParamId]
: parameterValues[hostParamId]);

if (mappings[id].smooth && std::fabs(valueFilters[id].out - value) < 1.f)
{
// Smooth value with filter
if (d_isEqual(valueFilters[id].process(args.sampleTime * pcontext->bufferSize, value), value))
{
valueReached[id] = true;
continue;
}
}
else
{
// Jump value
if (d_isEqual(valueFilters[id].out, value))
{
valueReached[id] = true;
continue;
}

valueFilters[id].out = value;
}

paramQuantity->setScaledValue(valueFilters[id].out);
}

firstRun = false;
std::memset(parametersChanged, 0, sizeof(parametersChanged));
}

void processTerminalOutput(const ProcessArgs&) override
{}

// ----------------------------------------------------------------------------------------------------------------
// save and load json stuff

json_t* dataToJson() override
{
json_t* const rootJ = json_object();
DISTRHO_SAFE_ASSERT_RETURN(rootJ != nullptr, nullptr);

if (json_t* const mapsJ = json_array())
{
for (uint id = 0; id < numMappedParmeters; ++id)
{
json_t* const mapJ = json_object();
DISTRHO_SAFE_ASSERT_CONTINUE(mapJ != nullptr);
json_object_set_new(mapJ, "hostParamId", json_integer(mappings[id].hostParamId));
json_object_set_new(mapJ, "inverted", json_boolean(mappings[id].inverted));
json_object_set_new(mapJ, "smooth", json_boolean(mappings[id].smooth));
json_object_set_new(mapJ, "moduleId", json_integer(mappings[id].paramHandle.moduleId));
json_object_set_new(mapJ, "paramId", json_integer(mappings[id].paramHandle.paramId));
json_array_append_new(mapsJ, mapJ);
}
json_object_set_new(rootJ, "maps", mapsJ);
}

return rootJ;
}

void dataFromJson(json_t* const rootJ) override
{
// Use NoLock because we're already in an Engine write-lock.
clearMaps_NoLock();

if (json_t* const mapsJ = json_object_get(rootJ, "maps"))
{
json_t* mapJ;
size_t id;
json_array_foreach(mapsJ, id, mapJ)
{
if (id >= MAX_MAPPED_PARAMS)
break;
json_t* const hostParamIdJ = json_object_get(mapJ, "hostParamId");
json_t* const invertedJ = json_object_get(mapJ, "inverted");
json_t* const smoothJ = json_object_get(mapJ, "smooth");
json_t* const moduleIdJ = json_object_get(mapJ, "moduleId");
json_t* const paramIdJ = json_object_get(mapJ, "paramId");
if (hostParamIdJ == nullptr)
continue;
if (invertedJ == nullptr)
continue;
if (smoothJ == nullptr)
continue;
if (moduleIdJ == nullptr)
continue;
if (paramIdJ == nullptr)
continue;

filterInitialized[id] = false;
valueReached[id] = true;
valueFilters[id].reset();
mappings[id].hostParamId = json_integer_value(hostParamIdJ);
mappings[id].inverted = json_boolean_value(invertedJ);
mappings[id].smooth = json_boolean_value(smoothJ);
pcontext->engine->updateParamHandle_NoLock(&mappings[id].paramHandle,
json_integer_value(moduleIdJ),
json_integer_value(paramIdJ),
false);
}
}

updateMapLen();
}

// ----------------------------------------------------------------------------------------------------------------
// stuff called from panel side

void clearMap(const uint8_t id)
{
learningId = UINT8_MAX;

mappings[id].hostParamId = UINT8_MAX;
pcontext->engine->updateParamHandle(&mappings[id].paramHandle, -1, 0, true);
updateMapLen();
}

void learnParam(const uint8_t hostParamId, const bool inverted, const bool smooth,
const int64_t moduleId, const int paramId)
{
const uint8_t id = learningId;
learningId = UINT8_MAX;
DISTRHO_SAFE_ASSERT_RETURN(id < MAX_MAPPED_PARAMS,);

filterInitialized[id] = false;
valueFilters[id].reset();
valueReached[id] = true;
mappings[id].inverted = inverted;
mappings[id].smooth = smooth;
mappings[id].hostParamId = hostParamId;

if (mappings[id].paramHandle.moduleId != moduleId || mappings[id].paramHandle.paramId != paramId)
pcontext->engine->updateParamHandle(&mappings[id].paramHandle, moduleId, paramId, true);

updateMapLen();
}

void updateMapLen()
{
// Find last nonempty map
int16_t id;
for (id = MAX_MAPPED_PARAMS; --id >= 0;)
{
if (mappings[id].paramHandle.moduleId >= 0)
break;
}

numMappedParmeters = static_cast<uint8_t>(id + 1);

// Add an empty "Mapping..." slot
if (numMappedParmeters < MAX_MAPPED_PARAMS)
++numMappedParmeters;
}
};

// --------------------------------------------------------------------------------------------------------------------

#ifndef HEADLESS
struct HostParametersMapChoice : CardinalLedDisplayChoice {
HostParametersMap* const module;
const uint8_t id;
uint8_t hostParamId = UINT8_MAX;
bool inverted = false;
bool smooth = true;

HostParametersMapChoice(HostParametersMap* const m, const uint8_t i)
: CardinalLedDisplayChoice(),
module(m),
id(i)
{
alignTextCenter = false;

// Module browser setup
if (m == nullptr)
{
bgColor = nvgRGB(0, 0, 0);
color.a = 0.75f;
text = "Click here to map";
}
}

void draw(const DrawArgs& args) override
{
if (bgColor.a > 0.0f)
{
nvgBeginPath(args.vg);
nvgRoundedRect(args.vg, 0, 0, box.size.x, box.size.y, 4);
nvgFillColor(args.vg, bgColor);
nvgFill(args.vg);
}

Widget::draw(args);
}

void step() override
{
if (module == nullptr)
return;

// Set bgColor and selected state
if (module->learningId == id)
{
bgColor = color;
bgColor.a = 0.125f;

if (ParamWidget* const touchedParam = APP->scene->rack->touchedParam)
{
APP->scene->rack->touchedParam = nullptr;
DISTRHO_SAFE_ASSERT_RETURN(hostParamId < kModuleParameters,);

const int64_t moduleId = touchedParam->module->id;
const int paramId = touchedParam->paramId;
module->learnParam(hostParamId, inverted, smooth, moduleId, paramId);
}
}
else
{
bgColor = nvgRGB(0, 0, 0);
}

// Set text
text.clear();

// mapped
if (module->mappings[id].hostParamId < kModuleParameters)
text += string::f("P%02d: ", module->mappings[id].hostParamId + 1);
if (module->mappings[id].paramHandle.moduleId >= 0)
text += getParamName();

// Set text color
if (text.empty() && module->learningId != id)
color.a = 0.75f;
else
color.a = 1.0f;

// unmapped
if (text.empty())
{
if (module->learningId == id)
text = "Mapping...";
else
text = module->numMappedParmeters == 1 ? "Click here to map" : "Unmapped";
}
}

void onButton(const ButtonEvent& e) override
{
DISTRHO_SAFE_ASSERT_RETURN(module != nullptr,);

e.stopPropagating();

if (e.action != GLFW_PRESS)
return;

switch (e.button)
{
case GLFW_MOUSE_BUTTON_RIGHT:
APP->scene->rack->touchedParam = nullptr;
module->clearMap(id);
e.consume(this);
break;
case GLFW_MOUSE_BUTTON_LEFT:
APP->scene->rack->touchedParam = nullptr;
e.consume(this);
// reset before dialog
module->learningId = hostParamId = UINT8_MAX;
inverted = smooth = false;
HostParametersMapChoice* const self = this;
// open dialog
async_dialog_text_input("Plugin-exposed parameter index to map to", "1", [self](char* newText){
if (self == nullptr || newText == nullptr)
return;
// FIXME use a proper dialog
const int hostParamIdTry = std::atoi(newText);
if (hostParamIdTry > 0 && hostParamIdTry < (int)kModuleParameters)
{
self->module->learningId = self->id;
self->hostParamId = static_cast<uint8_t>(hostParamIdTry - 1);
self->inverted = false;
self->smooth = false;
}
std::free(newText);
});
break;
}
}

std::string getParamName() const
{
DISTRHO_SAFE_ASSERT_RETURN(module != nullptr, "error");
DISTRHO_SAFE_ASSERT_RETURN(id < module->numMappedParmeters, "error");

ParamHandle paramHandle(module->mappings[id].paramHandle);

Module* const paramModule = paramHandle.module;
DISTRHO_CUSTOM_SAFE_ASSERT_ONCE_RETURN("paramModule is null", paramModule != nullptr, "error");

const int paramId = paramHandle.paramId;
DISTRHO_CUSTOM_SAFE_ASSERT_ONCE_RETURN("paramId is out of bounds", paramId < (int) paramModule->params.size(), "error");

ParamQuantity* const paramQuantity = paramModule->paramQuantities[paramId];
std::string s = paramQuantity->name;
if (s.empty())
s = "Unnamed";
s += " (";
s += paramModule->model->name;
s += ")";
return s;
}
};

struct HostParametersMapDisplay : Widget {
HostParametersMap* module;
ScrollWidget* scroll;
HostParametersMapChoice* choices[MAX_MAPPED_PARAMS];
LedDisplaySeparator* separators[MAX_MAPPED_PARAMS];

void drawLayer(const DrawArgs& args, int layer) override
{
nvgScissor(args.vg, RECT_ARGS(args.clipBox));
Widget::drawLayer(args, layer);
nvgResetScissor(args.vg);
}

void setModule(HostParametersMap* const module)
{
this->module = module;

scroll = new ScrollWidget;
scroll->box.size = box.size;
addChild(scroll);

float posY = 0.0f;
for (uint8_t id = 0; id < MAX_MAPPED_PARAMS; ++id)
{
if (id != 0)
{
LedDisplaySeparator* separator = createWidget<LedDisplaySeparator>(Vec(0.0f, posY));
separator->box.size = Vec(box.size.x, 1.0f);
separator->visible = false;
scroll->container->addChild(separator);
separators[id] = separator;
}

HostParametersMapChoice* const choice = new HostParametersMapChoice(module, id);
choice->box.pos = Vec(0.0f, posY);
choice->box.size = Vec(box.size.x, 20.0f);
choice->visible = id == 0;
scroll->container->addChild(choice);
choices[id] = choice;

posY += choice->box.size.y;
}
}

void step() override
{
if (module != nullptr)
{
const uint8_t numMappedParmeters = module->numMappedParmeters;

for (uint8_t id = 1; id < MAX_MAPPED_PARAMS; ++id)
{
separators[id]->visible = (id < numMappedParmeters);
choices[id]->visible = (id < numMappedParmeters);
}
}

Widget::step();
}
};

struct HostParametersMapWidget : ModuleWidgetWith11HP {
HostParametersMap* const module;

HostParametersMapWidget(HostParametersMap* const m)
: module(m)
{
setModule(module);
setPanel(APP->window->loadSvg(asset::plugin(pluginInstance, "res/HostParamsMap.svg")));
createAndAddScrews();

HostParametersMapDisplay* const display = createWidget<HostParametersMapDisplay>(Vec(1.0f, 71.0f));
display->box.size = Vec(box.size.x - 2.0f, box.size.y - 89.0f);
display->setModule(m);
addChild(display);
}

void draw(const DrawArgs& args) override
{
drawBackground(args.vg);
ModuleWidgetWith11HP::draw(args);
}
};
#else
struct HostParametersMapWidget : ModuleWidget {
HostParametersMapWidget(HostParametersMap* const module) {
setModule(module);
}
};
#endif

// --------------------------------------------------------------------------------------------------------------------

Model* modelHostParametersMap = createModel<HostParametersMap, HostParametersMapWidget>("HostParametersMap");

// --------------------------------------------------------------------------------------------------------------------

+ 0
- 1
plugins/Cardinal/src/HostParameters.cpp View File

@@ -53,7 +53,6 @@ struct HostParameters : TerminalModule {
{
const uint32_t processCounter = pcontext->processCounter;

// only checked on input
if (lastProcessCounter != processCounter)
{
bypassed = isBypassed();


+ 1
- 0
plugins/Cardinal/src/plugin.hpp View File

@@ -43,6 +43,7 @@ extern Model* modelHostMIDICC;
extern Model* modelHostMIDIGate;
extern Model* modelHostMIDIMap;
extern Model* modelHostParameters;
extern Model* modelHostParametersMap;
extern Model* modelHostTime;
extern Model* modelIldaeil;
extern Model* modelMPV;


+ 1
- 1
plugins/Makefile View File

@@ -200,6 +200,7 @@ PLUGIN_FILES += Cardinal/src/HostMIDI-CC.cpp
PLUGIN_FILES += Cardinal/src/HostMIDI-Gate.cpp
PLUGIN_FILES += Cardinal/src/HostMIDI-Map.cpp
PLUGIN_FILES += Cardinal/src/HostParameters.cpp
PLUGIN_FILES += Cardinal/src/HostParameters-Map.cpp
PLUGIN_FILES += Cardinal/src/HostTime.cpp
PLUGIN_FILES += Cardinal/src/TextEditor.cpp

@@ -219,7 +220,6 @@ ifneq ($(HEADLESS),true)
PLUGIN_FILES += Cardinal/src/ImGuiWidget.cpp
PLUGIN_FILES += Cardinal/src/ImGuiTextEditor.cpp
PLUGIN_FILES += Cardinal/src/SassyScope.cpp
# PLUGIN_FILES += Cardinal/src/sassy/sassy_scope.cpp
PLUGIN_FILES += $(wildcard Cardinal/src/DearImGui/*.cpp)
PLUGIN_FILES += $(wildcard Cardinal/src/DearImGuiColorTextEditor/*.cpp)
endif


+ 2
- 0
plugins/plugins.cpp View File

@@ -895,6 +895,7 @@ static void initStatic__Cardinal()
p->addModel(modelHostMIDIGate);
p->addModel(modelHostMIDIMap);
p->addModel(modelHostParameters);
p->addModel(modelHostParametersMap);
p->addModel(modelHostTime);
p->addModel(modelTextEditor);
#ifndef STATIC_BUILD
@@ -931,6 +932,7 @@ static void initStatic__Cardinal()
modelHostMIDIGate,
modelHostMIDIMap,
modelHostParameters,
modelHostParametersMap,
modelHostTime,
};
}


Loading…
Cancel
Save