Add Host Parameters Map module, functional but still WIP
Signed-off-by: falkTX <falktx@falktx.com>
This commit is contained in:
parent
952304999d
commit
e861389537
7 changed files with 639 additions and 39 deletions
|
|
@ -90,6 +90,15 @@
|
||||||
"External"
|
"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",
|
"slug": "HostTime",
|
||||||
"name": "Host Time",
|
"name": "Host Time",
|
||||||
|
|
|
||||||
|
|
@ -59,6 +59,7 @@ struct HostMIDIMap : TerminalModule {
|
||||||
uint32_t lastProcessCounter;
|
uint32_t lastProcessCounter;
|
||||||
int nextLearningId;
|
int nextLearningId;
|
||||||
uint8_t channel;
|
uint8_t channel;
|
||||||
|
bool bypassed = false;
|
||||||
|
|
||||||
// from Rack
|
// from Rack
|
||||||
bool smooth;
|
bool smooth;
|
||||||
|
|
@ -95,12 +96,10 @@ struct HostMIDIMap : TerminalModule {
|
||||||
{
|
{
|
||||||
paramHandles[id].color = nvgRGBf(0.76f, 0.11f, 0.22f);
|
paramHandles[id].color = nvgRGBf(0.76f, 0.11f, 0.22f);
|
||||||
paramHandles[id].text.reserve(25);
|
paramHandles[id].text.reserve(25);
|
||||||
|
valueFilters[id].setTau(1 / 30.f);
|
||||||
pcontext->engine->addParamHandle(¶mHandles[id]);
|
pcontext->engine->addParamHandle(¶mHandles[id]);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < MAX_MIDI_CONTROL; i++)
|
|
||||||
valueFilters[i].setTau(1 / 30.f);
|
|
||||||
|
|
||||||
divider.setDivision(32);
|
divider.setDivision(32);
|
||||||
onReset();
|
onReset();
|
||||||
}
|
}
|
||||||
|
|
@ -141,13 +140,14 @@ struct HostMIDIMap : TerminalModule {
|
||||||
|
|
||||||
if (processCounterChanged)
|
if (processCounterChanged)
|
||||||
{
|
{
|
||||||
|
bypassed = isBypassed();
|
||||||
lastProcessCounter = processCounter;
|
lastProcessCounter = processCounter;
|
||||||
midiEvents = pcontext->midiEvents;
|
midiEvents = pcontext->midiEvents;
|
||||||
midiEventsLeft = pcontext->midiEventCount;
|
midiEventsLeft = pcontext->midiEventCount;
|
||||||
midiEventFrame = 0;
|
midiEventFrame = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isBypassed() || !divider.process())
|
if (bypassed || !divider.process())
|
||||||
{
|
{
|
||||||
++midiEventFrame;
|
++midiEventFrame;
|
||||||
return;
|
return;
|
||||||
|
|
@ -262,21 +262,6 @@ struct HostMIDIMap : TerminalModule {
|
||||||
void processTerminalOutput(const ProcessArgs&) override
|
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(¶mHandles[id], -1, 0, true);
|
|
||||||
valueFilters[id].reset();
|
|
||||||
refreshParamHandleText(id);
|
|
||||||
updateMapLen();
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------------------------------------------
|
||||||
// stuff for resetting state
|
// stuff for resetting state
|
||||||
|
|
||||||
|
|
@ -308,7 +293,21 @@ struct HostMIDIMap : TerminalModule {
|
||||||
// ----------------------------------------------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------------------------------------------
|
||||||
// stuff called from panel side, must lock engine
|
// 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(¶mHandles[id], -1, 0, true);
|
||||||
|
valueFilters[id].reset();
|
||||||
|
refreshParamHandleText(id);
|
||||||
|
updateMapLen();
|
||||||
|
}
|
||||||
|
|
||||||
void enableLearn(const int id)
|
void enableLearn(const int id)
|
||||||
{
|
{
|
||||||
if (learningId == id)
|
if (learningId == id)
|
||||||
|
|
@ -321,16 +320,6 @@ struct HostMIDIMap : TerminalModule {
|
||||||
learnedParam = false;
|
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)
|
void learnParam(const int id, const int64_t moduleId, const int paramId)
|
||||||
{
|
{
|
||||||
pcontext->engine->updateParamHandle(¶mHandles[id], moduleId, paramId, true);
|
pcontext->engine->updateParamHandle(¶mHandles[id], moduleId, paramId, true);
|
||||||
|
|
@ -339,6 +328,16 @@ struct HostMIDIMap : TerminalModule {
|
||||||
updateMapLen();
|
updateMapLen();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
void disableLearn(const int id)
|
||||||
|
{
|
||||||
|
nextLearningId = -1;
|
||||||
|
|
||||||
|
if (learningId == id)
|
||||||
|
learningId = -1;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------------------------------------------
|
||||||
// common utils
|
// common utils
|
||||||
|
|
||||||
|
|
@ -371,14 +370,16 @@ struct HostMIDIMap : TerminalModule {
|
||||||
// this is called during RT!!
|
// this is called during RT!!
|
||||||
void refreshParamHandleText(const int id)
|
void refreshParamHandleText(const int id)
|
||||||
{
|
{
|
||||||
char textBuf[25];
|
|
||||||
|
|
||||||
if (ccs[id] >= 0)
|
if (ccs[id] >= 0)
|
||||||
|
{
|
||||||
|
char textBuf[25];
|
||||||
std::sprintf(textBuf, "CC%02d", ccs[id]);
|
std::sprintf(textBuf, "CC%02d", ccs[id]);
|
||||||
|
paramHandles[id].text.assign(textBuf);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
std::strcpy(textBuf, "MIDI-Map");
|
{
|
||||||
|
paramHandles[id].text.clear();
|
||||||
paramHandles[id].text.assign(textBuf);
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void updateMapLen()
|
void updateMapLen()
|
||||||
|
|
@ -471,7 +472,6 @@ struct HostMIDIMap : TerminalModule {
|
||||||
struct CardinalMIDIMapChoice : CardinalLedDisplayChoice {
|
struct CardinalMIDIMapChoice : CardinalLedDisplayChoice {
|
||||||
HostMIDIMap* const module;
|
HostMIDIMap* const module;
|
||||||
const int id;
|
const int id;
|
||||||
int disableLearnFrames = -1;
|
|
||||||
ParamWidget* lastTouchedParam = nullptr;
|
ParamWidget* lastTouchedParam = nullptr;
|
||||||
|
|
||||||
CardinalMIDIMapChoice(HostMIDIMap* const m, const int i)
|
CardinalMIDIMapChoice(HostMIDIMap* const m, const int i)
|
||||||
|
|
@ -576,10 +576,10 @@ struct CardinalMIDIMapChoice : CardinalLedDisplayChoice {
|
||||||
switch (e.button)
|
switch (e.button)
|
||||||
{
|
{
|
||||||
case GLFW_MOUSE_BUTTON_RIGHT:
|
case GLFW_MOUSE_BUTTON_RIGHT:
|
||||||
|
APP->scene->rack->touchedParam = lastTouchedParam = nullptr;
|
||||||
module->clearMap(id);
|
module->clearMap(id);
|
||||||
e.consume(this);
|
e.consume(this);
|
||||||
break;
|
break;
|
||||||
// fall-through
|
|
||||||
case GLFW_MOUSE_BUTTON_LEFT:
|
case GLFW_MOUSE_BUTTON_LEFT:
|
||||||
APP->scene->rack->touchedParam = lastTouchedParam = nullptr;
|
APP->scene->rack->touchedParam = lastTouchedParam = nullptr;
|
||||||
module->enableLearn(id);
|
module->enableLearn(id);
|
||||||
|
|
|
||||||
589
plugins/Cardinal/src/HostParameters-Map.cpp
Normal file
589
plugins/Cardinal/src/HostParameters-Map.cpp
Normal 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");
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
@ -53,7 +53,6 @@ struct HostParameters : TerminalModule {
|
||||||
{
|
{
|
||||||
const uint32_t processCounter = pcontext->processCounter;
|
const uint32_t processCounter = pcontext->processCounter;
|
||||||
|
|
||||||
// only checked on input
|
|
||||||
if (lastProcessCounter != processCounter)
|
if (lastProcessCounter != processCounter)
|
||||||
{
|
{
|
||||||
bypassed = isBypassed();
|
bypassed = isBypassed();
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,7 @@ extern Model* modelHostMIDICC;
|
||||||
extern Model* modelHostMIDIGate;
|
extern Model* modelHostMIDIGate;
|
||||||
extern Model* modelHostMIDIMap;
|
extern Model* modelHostMIDIMap;
|
||||||
extern Model* modelHostParameters;
|
extern Model* modelHostParameters;
|
||||||
|
extern Model* modelHostParametersMap;
|
||||||
extern Model* modelHostTime;
|
extern Model* modelHostTime;
|
||||||
extern Model* modelIldaeil;
|
extern Model* modelIldaeil;
|
||||||
extern Model* modelMPV;
|
extern Model* modelMPV;
|
||||||
|
|
|
||||||
|
|
@ -200,6 +200,7 @@ PLUGIN_FILES += Cardinal/src/HostMIDI-CC.cpp
|
||||||
PLUGIN_FILES += Cardinal/src/HostMIDI-Gate.cpp
|
PLUGIN_FILES += Cardinal/src/HostMIDI-Gate.cpp
|
||||||
PLUGIN_FILES += Cardinal/src/HostMIDI-Map.cpp
|
PLUGIN_FILES += Cardinal/src/HostMIDI-Map.cpp
|
||||||
PLUGIN_FILES += Cardinal/src/HostParameters.cpp
|
PLUGIN_FILES += Cardinal/src/HostParameters.cpp
|
||||||
|
PLUGIN_FILES += Cardinal/src/HostParameters-Map.cpp
|
||||||
PLUGIN_FILES += Cardinal/src/HostTime.cpp
|
PLUGIN_FILES += Cardinal/src/HostTime.cpp
|
||||||
PLUGIN_FILES += Cardinal/src/TextEditor.cpp
|
PLUGIN_FILES += Cardinal/src/TextEditor.cpp
|
||||||
|
|
||||||
|
|
@ -219,7 +220,6 @@ ifneq ($(HEADLESS),true)
|
||||||
PLUGIN_FILES += Cardinal/src/ImGuiWidget.cpp
|
PLUGIN_FILES += Cardinal/src/ImGuiWidget.cpp
|
||||||
PLUGIN_FILES += Cardinal/src/ImGuiTextEditor.cpp
|
PLUGIN_FILES += Cardinal/src/ImGuiTextEditor.cpp
|
||||||
PLUGIN_FILES += Cardinal/src/SassyScope.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/DearImGui/*.cpp)
|
||||||
PLUGIN_FILES += $(wildcard Cardinal/src/DearImGuiColorTextEditor/*.cpp)
|
PLUGIN_FILES += $(wildcard Cardinal/src/DearImGuiColorTextEditor/*.cpp)
|
||||||
endif
|
endif
|
||||||
|
|
|
||||||
|
|
@ -895,6 +895,7 @@ static void initStatic__Cardinal()
|
||||||
p->addModel(modelHostMIDIGate);
|
p->addModel(modelHostMIDIGate);
|
||||||
p->addModel(modelHostMIDIMap);
|
p->addModel(modelHostMIDIMap);
|
||||||
p->addModel(modelHostParameters);
|
p->addModel(modelHostParameters);
|
||||||
|
p->addModel(modelHostParametersMap);
|
||||||
p->addModel(modelHostTime);
|
p->addModel(modelHostTime);
|
||||||
p->addModel(modelTextEditor);
|
p->addModel(modelTextEditor);
|
||||||
#ifndef STATIC_BUILD
|
#ifndef STATIC_BUILD
|
||||||
|
|
@ -931,6 +932,7 @@ static void initStatic__Cardinal()
|
||||||
modelHostMIDIGate,
|
modelHostMIDIGate,
|
||||||
modelHostMIDIMap,
|
modelHostMIDIMap,
|
||||||
modelHostParameters,
|
modelHostParameters,
|
||||||
|
modelHostParametersMap,
|
||||||
modelHostTime,
|
modelHostTime,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue