Signed-off-by: falkTX <falktx@falktx.com>tags/22.02
@@ -46,27 +46,12 @@ struct Cable; | |||||
struct Port { | struct Port { | ||||
/** Voltage of the port. */ | /** Voltage of the port. */ | ||||
/** NOTE Purposefully renamed in Cardinal as a way to catch plugins using it directly. */ | |||||
union { | union { | ||||
/** Unstable API. Use getVoltage() and setVoltage() instead. */ | /** Unstable API. Use getVoltage() and setVoltage() instead. */ | ||||
float cvoltages[PORT_MAX_CHANNELS] = {}; | |||||
float voltages[PORT_MAX_CHANNELS] = {}; | |||||
/** DEPRECATED. Unstable API. Use getVoltage() and setVoltage() instead. */ | /** DEPRECATED. Unstable API. Use getVoltage() and setVoltage() instead. */ | ||||
float cvalue; | |||||
float value; | |||||
}; | }; | ||||
/** Special trickery for backwards compatibility with plugins using DEPRECATED APIs */ | |||||
struct BackwardsCompatPortValue { | |||||
Port* const port; | |||||
BackwardsCompatPortValue(Port* p) : port(p) {} | |||||
void operator=(float value) { port->setVoltage(value); } | |||||
void operator-=(float value) { port->setVoltage(port->cvalue - value); } | |||||
void operator+=(float value) { port->setVoltage(port->cvalue + value); } | |||||
void operator*=(float value) { port->setVoltage(port->cvalue * value); } | |||||
void operator/=(float value) { port->setVoltage(port->cvalue / value); } | |||||
operator float() const { return port->cvalue; } | |||||
} value; | |||||
Port() : value(this) {} | |||||
union { | union { | ||||
/** Number of polyphonic channels. | /** Number of polyphonic channels. | ||||
DEPRECATED. Unstable API. Use set/getChannels() instead. | DEPRECATED. Unstable API. Use set/getChannels() instead. | ||||
@@ -87,24 +72,19 @@ struct Port { | |||||
OUTPUT, | OUTPUT, | ||||
}; | }; | ||||
/** Cables connected to this output port. */ | |||||
/** List of cables connected to this port (if output type). */ | |||||
std::list<Cable*> cables; | std::list<Cable*> cables; | ||||
/** Step-through the cables. | |||||
Called whenever voltage changes, required for zero latency operation. */ | |||||
void stepCables(); | |||||
/** Sets the voltage of the given channel. */ | /** Sets the voltage of the given channel. */ | ||||
void setVoltage(float voltage, int channel = 0) { | void setVoltage(float voltage, int channel = 0) { | ||||
cvoltages[channel] = voltage; | |||||
stepCables(); | |||||
voltages[channel] = voltage; | |||||
} | } | ||||
/** Returns the voltage of the given channel. | /** Returns the voltage of the given channel. | ||||
Because of proper bookkeeping, all channels higher than the input port's number of channels should be 0V. | Because of proper bookkeeping, all channels higher than the input port's number of channels should be 0V. | ||||
*/ | */ | ||||
float getVoltage(int channel = 0) { | float getVoltage(int channel = 0) { | ||||
return cvoltages[channel]; | |||||
return voltages[channel]; | |||||
} | } | ||||
/** Returns the given channel's voltage if the port is polyphonic, otherwise returns the first voltage (channel 0). */ | /** Returns the given channel's voltage if the port is polyphonic, otherwise returns the first voltage (channel 0). */ | ||||
@@ -124,15 +104,14 @@ struct Port { | |||||
/** Returns a pointer to the array of voltages beginning with firstChannel. | /** Returns a pointer to the array of voltages beginning with firstChannel. | ||||
The pointer can be used for reading and writing. | The pointer can be used for reading and writing. | ||||
*/ | */ | ||||
// TODO convert to const float* for zero-latency cable stuff and fix all plugins after | |||||
float* getVoltages(int firstChannel = 0) { | float* getVoltages(int firstChannel = 0) { | ||||
return &cvoltages[firstChannel]; | |||||
return &voltages[firstChannel]; | |||||
} | } | ||||
/** Copies the port's voltages to an array of size at least `channels`. */ | /** Copies the port's voltages to an array of size at least `channels`. */ | ||||
void readVoltages(float* v) { | void readVoltages(float* v) { | ||||
for (int c = 0; c < channels; c++) { | for (int c = 0; c < channels; c++) { | ||||
v[c] = cvoltages[c]; | |||||
v[c] = voltages[c]; | |||||
} | } | ||||
} | } | ||||
@@ -141,24 +120,22 @@ struct Port { | |||||
*/ | */ | ||||
void writeVoltages(const float* v) { | void writeVoltages(const float* v) { | ||||
for (int c = 0; c < channels; c++) { | for (int c = 0; c < channels; c++) { | ||||
cvoltages[c] = v[c]; | |||||
voltages[c] = v[c]; | |||||
} | } | ||||
stepCables(); | |||||
} | } | ||||
/** Sets all voltages to 0. */ | /** Sets all voltages to 0. */ | ||||
void clearVoltages() { | void clearVoltages() { | ||||
for (int c = 0; c < channels; c++) { | for (int c = 0; c < channels; c++) { | ||||
cvoltages[c] = 0.f; | |||||
voltages[c] = 0.f; | |||||
} | } | ||||
stepCables(); | |||||
} | } | ||||
/** Returns the sum of all voltages. */ | /** Returns the sum of all voltages. */ | ||||
float getVoltageSum() { | float getVoltageSum() { | ||||
float sum = 0.f; | float sum = 0.f; | ||||
for (int c = 0; c < channels; c++) { | for (int c = 0; c < channels; c++) { | ||||
sum += cvoltages[c]; | |||||
sum += voltages[c]; | |||||
} | } | ||||
return sum; | return sum; | ||||
} | } | ||||
@@ -171,12 +148,12 @@ struct Port { | |||||
return 0.f; | return 0.f; | ||||
} | } | ||||
else if (channels == 1) { | else if (channels == 1) { | ||||
return std::fabs(cvoltages[0]); | |||||
return std::fabs(voltages[0]); | |||||
} | } | ||||
else { | else { | ||||
float sum = 0.f; | float sum = 0.f; | ||||
for (int c = 0; c < channels; c++) { | for (int c = 0; c < channels; c++) { | ||||
sum += std::pow(cvoltages[c], 2); | |||||
sum += std::pow(voltages[c], 2); | |||||
} | } | ||||
return std::sqrt(sum); | return std::sqrt(sum); | ||||
} | } | ||||
@@ -184,7 +161,7 @@ struct Port { | |||||
template <typename T> | template <typename T> | ||||
T getVoltageSimd(int firstChannel) { | T getVoltageSimd(int firstChannel) { | ||||
return T::load(&cvoltages[firstChannel]); | |||||
return T::load(&voltages[firstChannel]); | |||||
} | } | ||||
template <typename T> | template <typename T> | ||||
@@ -204,8 +181,7 @@ struct Port { | |||||
template <typename T> | template <typename T> | ||||
void setVoltageSimd(T voltage, int firstChannel) { | void setVoltageSimd(T voltage, int firstChannel) { | ||||
voltage.store(&cvoltages[firstChannel]); | |||||
stepCables(); | |||||
voltage.store(&voltages[firstChannel]); | |||||
} | } | ||||
/** Sets the number of polyphony channels. | /** Sets the number of polyphony channels. | ||||
@@ -220,7 +196,7 @@ struct Port { | |||||
} | } | ||||
// Set higher channel voltages to 0 | // Set higher channel voltages to 0 | ||||
for (int c = channels; c < this->channels; c++) { | for (int c = channels; c < this->channels; c++) { | ||||
cvoltages[c] = 0.f; | |||||
voltages[c] = 0.f; | |||||
} | } | ||||
// Don't allow caller to set port as disconnected | // Don't allow caller to set port as disconnected | ||||
if (channels == 0) { | if (channels == 0) { | ||||
@@ -0,0 +1,31 @@ | |||||
/* | |||||
* 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. | |||||
*/ | |||||
#pragma once | |||||
#include <engine/Module.hpp> | |||||
namespace rack { | |||||
namespace engine { | |||||
struct TerminalModule : Module { | |||||
virtual void processTerminalInput(const ProcessArgs& args) = 0; | |||||
virtual void processTerminalOutput(const ProcessArgs& args) = 0; | |||||
}; | |||||
} | |||||
} |
@@ -1 +1 @@ | |||||
Subproject commit 988c2372a95d163b71d04b217080e612b767c539 | |||||
Subproject commit e55fcd2e1d7c0fef69d4919baac6f791172c89ca |
@@ -24,7 +24,7 @@ | |||||
USE_NAMESPACE_DISTRHO; | USE_NAMESPACE_DISTRHO; | ||||
template<int numIO> | template<int numIO> | ||||
struct HostAudio : Module { | |||||
struct HostAudio : TerminalModule { | |||||
CardinalPluginContext* const pcontext; | CardinalPluginContext* const pcontext; | ||||
const int numParams; | const int numParams; | ||||
const int numInputs; | const int numInputs; | ||||
@@ -74,45 +74,65 @@ struct HostAudio : Module { | |||||
dcFilters[i].setCutoffFreq(10.f * e.sampleTime); | dcFilters[i].setCutoffFreq(10.f * e.sampleTime); | ||||
} | } | ||||
void process(const ProcessArgs&) override | |||||
void processTerminalInput(const ProcessArgs&) override | |||||
{ | { | ||||
const float* const* const dataIns = pcontext->dataIns; | const float* const* const dataIns = pcontext->dataIns; | ||||
float** const dataOuts = pcontext->dataOuts; | |||||
const int blockFrames = pcontext->engine->getBlockFrames(); | const int blockFrames = pcontext->engine->getBlockFrames(); | ||||
const int64_t blockFrame = pcontext->engine->getBlockFrame(); | const int64_t blockFrame = pcontext->engine->getBlockFrame(); | ||||
// only checked on input | |||||
if (lastBlockFrame != blockFrame) | if (lastBlockFrame != blockFrame) | ||||
{ | { | ||||
dataFrame = 0; | dataFrame = 0; | ||||
lastBlockFrame = blockFrame; | lastBlockFrame = blockFrame; | ||||
} | } | ||||
const int k = dataFrame++; | |||||
// only incremented on output | |||||
const int k = dataFrame; | |||||
DISTRHO_SAFE_ASSERT_INT2_RETURN(k < blockFrames, k, blockFrames,); | DISTRHO_SAFE_ASSERT_INT2_RETURN(k < blockFrames, k, blockFrames,); | ||||
const float gain = numParams != 0 ? std::pow(params[0].getValue(), 2.f) : 1.0f; | |||||
// from host into cardinal, shows as output plug | // from host into cardinal, shows as output plug | ||||
if (dataIns != nullptr) | |||||
if (isBypassed()) | |||||
{ | |||||
for (int i=0; i<numOutputs; ++i) | |||||
outputs[i].setVoltage(0.0f); | |||||
} | |||||
else if (dataIns != nullptr) | |||||
{ | { | ||||
for (int i=0; i<numOutputs; ++i) | for (int i=0; i<numOutputs; ++i) | ||||
outputs[i].setVoltage(dataIns[i][k] * 10.0f); | outputs[i].setVoltage(dataIns[i][k] * 10.0f); | ||||
} | } | ||||
} | |||||
void processTerminalOutput(const ProcessArgs&) override | |||||
{ | |||||
float** const dataOuts = pcontext->dataOuts; | |||||
const int blockFrames = pcontext->engine->getBlockFrames(); | |||||
// only incremented on output | |||||
const int k = dataFrame++; | |||||
DISTRHO_SAFE_ASSERT_INT2_RETURN(k < blockFrames, k, blockFrames,); | |||||
const float gain = numParams != 0 ? std::pow(params[0].getValue(), 2.f) : 1.0f; | |||||
// from cardinal into host, shows as input plug | // from cardinal into host, shows as input plug | ||||
for (int i=0; i<numInputs; ++i) | |||||
if (! isBypassed()) | |||||
{ | { | ||||
float v = inputs[i].getVoltageSum() * 0.1f; | |||||
// Apply DC filter | |||||
if (dcFilterEnabled) | |||||
for (int i=0; i<numInputs; ++i) | |||||
{ | { | ||||
dcFilters[i].process(v); | |||||
v = dcFilters[i].highpass(); | |||||
} | |||||
float v = inputs[i].getVoltageSum() * 0.1f; | |||||
dataOuts[i][k] += clamp(v * gain, -1.0f, 1.0f); | |||||
// Apply DC filter | |||||
if (dcFilterEnabled) | |||||
{ | |||||
dcFilters[i].process(v); | |||||
v = dcFilters[i].highpass(); | |||||
} | |||||
dataOuts[i][k] += clamp(v * gain, -1.0f, 1.0f); | |||||
} | |||||
} | } | ||||
if (numInputs == 2) | if (numInputs == 2) | ||||
@@ -18,6 +18,7 @@ | |||||
#pragma once | #pragma once | ||||
#include "rack.hpp" | #include "rack.hpp" | ||||
#include "engine/TerminalModule.hpp" | |||||
#ifdef NDEBUG | #ifdef NDEBUG | ||||
# undef DEBUG | # undef DEBUG | ||||
@@ -35,6 +35,7 @@ | |||||
#include <pmmintrin.h> | #include <pmmintrin.h> | ||||
#include <engine/Engine.hpp> | #include <engine/Engine.hpp> | ||||
#include <engine/TerminalModule.hpp> | |||||
#include <settings.hpp> | #include <settings.hpp> | ||||
#include <system.hpp> | #include <system.hpp> | ||||
#include <random.hpp> | #include <random.hpp> | ||||
@@ -49,12 +50,19 @@ | |||||
#include "DistrhoUtils.hpp" | #include "DistrhoUtils.hpp" | ||||
// known terminal modules | |||||
extern rack::plugin::Model* modelHostAudio2; | |||||
extern rack::plugin::Model* modelHostAudio8; | |||||
namespace rack { | namespace rack { | ||||
namespace engine { | namespace engine { | ||||
struct Engine::Internal { | struct Engine::Internal { | ||||
std::vector<Module*> modules; | std::vector<Module*> modules; | ||||
std::vector<TerminalModule*> terminalModules; | |||||
std::vector<Cable*> cables; | std::vector<Cable*> cables; | ||||
std::set<ParamHandle*> paramHandles; | std::set<ParamHandle*> paramHandles; | ||||
@@ -127,24 +135,64 @@ static void Cable_step(Cable* that) { | |||||
const int channels = output->channels; | const int channels = output->channels; | ||||
// Copy all voltages from output to input | // Copy all voltages from output to input | ||||
for (int c = 0; c < channels; c++) { | for (int c = 0; c < channels; c++) { | ||||
float v = output->cvoltages[c]; | |||||
float v = output->voltages[c]; | |||||
// Set 0V if infinite or NaN | // Set 0V if infinite or NaN | ||||
if (!std::isfinite(v)) | if (!std::isfinite(v)) | ||||
v = 0.f; | v = 0.f; | ||||
input->cvoltages[c] = v; | |||||
input->voltages[c] = v; | |||||
} | } | ||||
// Set higher channel voltages to 0 | // Set higher channel voltages to 0 | ||||
for (int c = channels; c < input->channels; c++) { | for (int c = channels; c < input->channels; c++) { | ||||
input->cvoltages[c] = 0.f; | |||||
input->voltages[c] = 0.f; | |||||
} | } | ||||
input->channels = channels; | input->channels = channels; | ||||
} | } | ||||
void Port::stepCables() | |||||
{ | |||||
for (Cable* cable : cables) | |||||
Cable_step(cable); | |||||
static void Port_step(Port* that, float deltaTime) { | |||||
// Set plug lights | |||||
if (that->channels == 0) { | |||||
that->plugLights[0].setBrightness(0.f); | |||||
that->plugLights[1].setBrightness(0.f); | |||||
that->plugLights[2].setBrightness(0.f); | |||||
} | |||||
else if (that->channels == 1) { | |||||
float v = that->getVoltage() / 10.f; | |||||
that->plugLights[0].setSmoothBrightness(-v, deltaTime); | |||||
that->plugLights[1].setSmoothBrightness(v, deltaTime); | |||||
that->plugLights[2].setBrightness(0.f); | |||||
} | |||||
else { | |||||
float v = that->getVoltageRMS() / 10.f; | |||||
that->plugLights[0].setBrightness(0.f); | |||||
that->plugLights[1].setBrightness(0.f); | |||||
that->plugLights[2].setSmoothBrightness(v, deltaTime); | |||||
} | |||||
} | |||||
static void TerminalModule__doProcess(TerminalModule* terminalModule, const Module::ProcessArgs& args, bool input) { | |||||
// Step module | |||||
if (input) { | |||||
terminalModule->processTerminalInput(args); | |||||
for (Output& output : terminalModule->outputs) { | |||||
for (Cable* cable : output.cables) | |||||
Cable_step(cable); | |||||
} | |||||
} else { | |||||
terminalModule->processTerminalOutput(args); | |||||
} | |||||
// Iterate ports to step plug lights | |||||
if (args.frame % 7 /* PORT_DIVIDER */ == 0) { | |||||
float portTime = args.sampleTime * 7 /* PORT_DIVIDER */; | |||||
for (Input& input : terminalModule->inputs) { | |||||
Port_step(&input, portTime); | |||||
} | |||||
for (Output& output : terminalModule->outputs) { | |||||
Port_step(&output, portTime); | |||||
} | |||||
} | |||||
} | } | ||||
@@ -174,14 +222,6 @@ static void Engine_stepFrame(Engine* that) { | |||||
} | } | ||||
} | } | ||||
/* NOTE this is likely not needed in Cardinal, but needs testing. | |||||
* Leaving it as comment in case we need it bring it back | |||||
// Step cables | |||||
for (Cable* cable : internal->cables) { | |||||
Cable_step(cable); | |||||
} | |||||
*/ | |||||
// Flip messages for each module | // Flip messages for each module | ||||
for (Module* module : internal->modules) { | for (Module* module : internal->modules) { | ||||
if (module->leftExpander.messageFlipRequested) { | if (module->leftExpander.messageFlipRequested) { | ||||
@@ -200,16 +240,25 @@ static void Engine_stepFrame(Engine* that) { | |||||
processArgs.sampleTime = internal->sampleTime; | processArgs.sampleTime = internal->sampleTime; | ||||
processArgs.frame = internal->frame; | processArgs.frame = internal->frame; | ||||
// Step each module | |||||
// Process terminal inputs first | |||||
for (TerminalModule* terminalModule : internal->terminalModules) { | |||||
TerminalModule__doProcess(terminalModule, processArgs, true); | |||||
} | |||||
// Step each module and cables | |||||
for (Module* module : internal->modules) { | for (Module* module : internal->modules) { | ||||
module->doProcess(processArgs); | module->doProcess(processArgs); | ||||
// FIXME remove this section below after all modules can use zero-latency cable stuff | |||||
for (Output& output : module->outputs) { | for (Output& output : module->outputs) { | ||||
for (Cable* cable : output.cables) | for (Cable* cable : output.cables) | ||||
Cable_step(cable); | Cable_step(cable); | ||||
} | } | ||||
} | } | ||||
// Process terminal outputs last | |||||
for (TerminalModule* terminalModule : internal->terminalModules) { | |||||
TerminalModule__doProcess(terminalModule, processArgs, false); | |||||
} | |||||
++internal->frame; | ++internal->frame; | ||||
} | } | ||||
@@ -217,7 +266,7 @@ static void Engine_stepFrame(Engine* that) { | |||||
static void Port_setDisconnected(Port* that) { | static void Port_setDisconnected(Port* that) { | ||||
that->channels = 0; | that->channels = 0; | ||||
for (int c = 0; c < PORT_MAX_CHANNELS; c++) { | for (int c = 0; c < PORT_MAX_CHANNELS; c++) { | ||||
that->cvoltages[c] = 0.f; | |||||
that->voltages[c] = 0.f; | |||||
} | } | ||||
} | } | ||||
@@ -240,6 +289,14 @@ static void Engine_updateConnected(Engine* that) { | |||||
disconnectedPorts.insert(&output); | disconnectedPorts.insert(&output); | ||||
} | } | ||||
} | } | ||||
for (TerminalModule* terminalModule : that->internal->terminalModules) { | |||||
for (Input& input : terminalModule->inputs) { | |||||
disconnectedPorts.insert(&input); | |||||
} | |||||
for (Output& output : terminalModule->outputs) { | |||||
disconnectedPorts.insert(&output); | |||||
} | |||||
} | |||||
for (Cable* cable : that->internal->cables) { | for (Cable* cable : that->internal->cables) { | ||||
// Connect input | // Connect input | ||||
Input& input = cable->inputModule->inputs[cable->inputId]; | Input& input = cable->inputModule->inputs[cable->inputId]; | ||||
@@ -287,6 +344,7 @@ Engine::~Engine() { | |||||
// If this happens, a module must have failed to remove itself before the RackWidget was destroyed. | // If this happens, a module must have failed to remove itself before the RackWidget was destroyed. | ||||
DISTRHO_SAFE_ASSERT(internal->cables.empty()); | DISTRHO_SAFE_ASSERT(internal->cables.empty()); | ||||
DISTRHO_SAFE_ASSERT(internal->modules.empty()); | DISTRHO_SAFE_ASSERT(internal->modules.empty()); | ||||
DISTRHO_SAFE_ASSERT(internal->terminalModules.empty()); | |||||
DISTRHO_SAFE_ASSERT(internal->paramHandles.empty()); | DISTRHO_SAFE_ASSERT(internal->paramHandles.empty()); | ||||
DISTRHO_SAFE_ASSERT(internal->modulesCache.empty()); | DISTRHO_SAFE_ASSERT(internal->modulesCache.empty()); | ||||
@@ -320,6 +378,11 @@ void Engine::clear_NoLock() { | |||||
removeModule_NoLock(module); | removeModule_NoLock(module); | ||||
delete module; | delete module; | ||||
} | } | ||||
std::vector<TerminalModule*> terminalModules = internal->terminalModules; | |||||
for (TerminalModule* terminalModule : terminalModules) { | |||||
removeModule_NoLock(terminalModule); | |||||
delete terminalModule; | |||||
} | |||||
} | } | ||||
@@ -404,6 +467,9 @@ void Engine::setSampleRate(float sampleRate) { | |||||
for (Module* module : internal->modules) { | for (Module* module : internal->modules) { | ||||
module->onSampleRateChange(e); | module->onSampleRateChange(e); | ||||
} | } | ||||
for (TerminalModule* terminalModule : internal->terminalModules) { | |||||
terminalModule->onSampleRateChange(e); | |||||
} | |||||
} | } | ||||
@@ -474,7 +540,7 @@ double Engine::getMeterMax() { | |||||
size_t Engine::getNumModules() { | size_t Engine::getNumModules() { | ||||
return internal->modules.size(); | |||||
return internal->modules.size() + internal->terminalModules.size(); | |||||
} | } | ||||
@@ -484,8 +550,12 @@ size_t Engine::getModuleIds(int64_t* moduleIds, size_t len) { | |||||
for (Module* m : internal->modules) { | for (Module* m : internal->modules) { | ||||
if (i >= len) | if (i >= len) | ||||
break; | break; | ||||
moduleIds[i] = m->id; | |||||
i++; | |||||
moduleIds[i++] = m->id; | |||||
} | |||||
for (TerminalModule* m : internal->terminalModules) { | |||||
if (i >= len) | |||||
break; | |||||
moduleIds[i++] = m->id; | |||||
} | } | ||||
return i; | return i; | ||||
} | } | ||||
@@ -494,27 +564,43 @@ size_t Engine::getModuleIds(int64_t* moduleIds, size_t len) { | |||||
std::vector<int64_t> Engine::getModuleIds() { | std::vector<int64_t> Engine::getModuleIds() { | ||||
SharedLock<SharedMutex> lock(internal->mutex); | SharedLock<SharedMutex> lock(internal->mutex); | ||||
std::vector<int64_t> moduleIds; | std::vector<int64_t> moduleIds; | ||||
moduleIds.reserve(internal->modules.size()); | |||||
moduleIds.reserve(getNumModules()); | |||||
for (Module* m : internal->modules) { | for (Module* m : internal->modules) { | ||||
moduleIds.push_back(m->id); | moduleIds.push_back(m->id); | ||||
} | } | ||||
for (TerminalModule* tm : internal->terminalModules) { | |||||
moduleIds.push_back(tm->id); | |||||
} | |||||
return moduleIds; | return moduleIds; | ||||
} | } | ||||
static TerminalModule* asTerminalModule(Module* const module) { | |||||
const plugin::Model* const model = module->model; | |||||
if (model == modelHostAudio2 || model == modelHostAudio8) | |||||
return static_cast<TerminalModule*>(module); | |||||
return nullptr; | |||||
} | |||||
void Engine::addModule(Module* module) { | void Engine::addModule(Module* module) { | ||||
std::lock_guard<SharedMutex> lock(internal->mutex); | std::lock_guard<SharedMutex> lock(internal->mutex); | ||||
DISTRHO_SAFE_ASSERT_RETURN(module,); | |||||
DISTRHO_SAFE_ASSERT_RETURN(module != nullptr,); | |||||
// Check that the module is not already added | // Check that the module is not already added | ||||
auto it = std::find(internal->modules.begin(), internal->modules.end(), module); | auto it = std::find(internal->modules.begin(), internal->modules.end(), module); | ||||
DISTRHO_SAFE_ASSERT_RETURN(it == internal->modules.end(),); | DISTRHO_SAFE_ASSERT_RETURN(it == internal->modules.end(),); | ||||
auto tit = std::find(internal->terminalModules.begin(), internal->terminalModules.end(), module); | |||||
DISTRHO_SAFE_ASSERT_RETURN(tit == internal->terminalModules.end(),); | |||||
// Set ID if unset or collides with an existing ID | // Set ID if unset or collides with an existing ID | ||||
while (module->id < 0 || internal->modulesCache.find(module->id) != internal->modulesCache.end()) { | while (module->id < 0 || internal->modulesCache.find(module->id) != internal->modulesCache.end()) { | ||||
// Randomly generate ID | // Randomly generate ID | ||||
module->id = random::u64() % (1ull << 53); | module->id = random::u64() % (1ull << 53); | ||||
} | } | ||||
// Add module | // Add module | ||||
internal->modules.push_back(module); | |||||
if (TerminalModule* const terminalModule = asTerminalModule(module)) | |||||
internal->terminalModules.push_back(terminalModule); | |||||
else | |||||
internal->modules.push_back(module); | |||||
internal->modulesCache[module->id] = module; | internal->modulesCache[module->id] = module; | ||||
// Dispatch AddEvent | // Dispatch AddEvent | ||||
Module::AddEvent eAdd; | Module::AddEvent eAdd; | ||||
@@ -538,11 +624,7 @@ void Engine::removeModule(Module* module) { | |||||
} | } | ||||
void Engine::removeModule_NoLock(Module* module) { | |||||
DISTRHO_SAFE_ASSERT_RETURN(module,); | |||||
// Check that the module actually exists | |||||
auto it = std::find(internal->modules.begin(), internal->modules.end(), module); | |||||
DISTRHO_SAFE_ASSERT_RETURN(it != internal->modules.end(),); | |||||
static void removeModule_NoLock_common(Engine::Internal* internal, Module* module) { | |||||
// Remove from widgets cache | // Remove from widgets cache | ||||
CardinalPluginModelHelper* const helper = dynamic_cast<CardinalPluginModelHelper*>(module->model); | CardinalPluginModelHelper* const helper = dynamic_cast<CardinalPluginModelHelper*>(module->model); | ||||
DISTRHO_SAFE_ASSERT_RETURN(helper != nullptr,); | DISTRHO_SAFE_ASSERT_RETURN(helper != nullptr,); | ||||
@@ -575,14 +657,31 @@ void Engine::removeModule_NoLock(Module* module) { | |||||
m->rightExpander.module = NULL; | m->rightExpander.module = NULL; | ||||
} | } | ||||
} | } | ||||
// Remove module | |||||
internal->modulesCache.erase(module->id); | |||||
internal->modules.erase(it); | |||||
// Reset expanders | // Reset expanders | ||||
module->leftExpander.moduleId = -1; | module->leftExpander.moduleId = -1; | ||||
module->leftExpander.module = NULL; | module->leftExpander.module = NULL; | ||||
module->rightExpander.moduleId = -1; | module->rightExpander.moduleId = -1; | ||||
module->rightExpander.module = NULL; | module->rightExpander.module = NULL; | ||||
// Remove module | |||||
internal->modulesCache.erase(module->id); | |||||
} | |||||
void Engine::removeModule_NoLock(Module* module) { | |||||
DISTRHO_SAFE_ASSERT_RETURN(module,); | |||||
// Check that the module actually exists | |||||
if (TerminalModule* const terminalModule = asTerminalModule(module)) { | |||||
auto tit = std::find(internal->terminalModules.begin(), internal->terminalModules.end(), terminalModule); | |||||
DISTRHO_SAFE_ASSERT_RETURN(tit != internal->terminalModules.end(),); | |||||
removeModule_NoLock_common(internal, module); | |||||
internal->terminalModules.erase(tit); | |||||
} | |||||
else { | |||||
auto it = std::find(internal->modules.begin(), internal->modules.end(), module); | |||||
DISTRHO_SAFE_ASSERT_RETURN(it != internal->modules.end(),); | |||||
removeModule_NoLock_common(internal, module); | |||||
internal->modules.erase(it); | |||||
} | |||||
} | } | ||||
@@ -590,7 +689,8 @@ bool Engine::hasModule(Module* module) { | |||||
SharedLock<SharedMutex> lock(internal->mutex); | SharedLock<SharedMutex> lock(internal->mutex); | ||||
// TODO Performance could be improved by searching modulesCache, but more testing would be needed to make sure it's always valid. | // TODO Performance could be improved by searching modulesCache, but more testing would be needed to make sure it's always valid. | ||||
auto it = std::find(internal->modules.begin(), internal->modules.end(), module); | auto it = std::find(internal->modules.begin(), internal->modules.end(), module); | ||||
return it != internal->modules.end(); | |||||
auto tit = std::find(internal->terminalModules.begin(), internal->terminalModules.end(), module); | |||||
return it != internal->modules.end() && tit != internal->terminalModules.end(); | |||||
} | } | ||||
@@ -678,6 +778,10 @@ void Engine::prepareSave() { | |||||
Module::SaveEvent e; | Module::SaveEvent e; | ||||
module->onSave(e); | module->onSave(e); | ||||
} | } | ||||
for (TerminalModule* terminalModule : internal->terminalModules) { | |||||
Module::SaveEvent e; | |||||
terminalModule->onSave(e); | |||||
} | |||||
} | } | ||||
@@ -957,6 +1061,10 @@ json_t* Engine::toJson() { | |||||
json_t* moduleJ = module->toJson(); | json_t* moduleJ = module->toJson(); | ||||
json_array_append_new(modulesJ, moduleJ); | json_array_append_new(modulesJ, moduleJ); | ||||
} | } | ||||
for (TerminalModule* terminalModule : internal->terminalModules) { | |||||
json_t* terminalModuleJ = terminalModule->toJson(); | |||||
json_array_append_new(modulesJ, terminalModuleJ); | |||||
} | |||||
json_object_set_new(rootJ, "modules", modulesJ); | json_object_set_new(rootJ, "modules", modulesJ); | ||||
// cables | // cables | ||||
@@ -1,5 +1,5 @@ | |||||
--- ../Rack/src/engine/Engine.cpp 2022-01-15 14:44:46.395281005 +0000 | |||||
+++ Engine.cpp 2022-01-23 17:13:03.200930905 +0000 | |||||
--- ../Rack/src/engine/Engine.cpp 2022-02-05 22:30:09.253393116 +0000 | |||||
+++ Engine.cpp 2022-02-08 02:48:26.045085405 +0000 | |||||
@@ -1,3 +1,30 @@ | @@ -1,3 +1,30 @@ | ||||
+/* | +/* | ||||
+ * DISTRHO Cardinal Plugin | + * DISTRHO Cardinal Plugin | ||||
@@ -31,7 +31,11 @@ | |||||
#include <algorithm> | #include <algorithm> | ||||
#include <set> | #include <set> | ||||
#include <thread> | #include <thread> | ||||
@@ -11,178 +38,25 @@ | |||||
@@ -8,181 +35,36 @@ | |||||
#include <pmmintrin.h> | |||||
#include <engine/Engine.hpp> | |||||
+#include <engine/TerminalModule.hpp> | |||||
#include <settings.hpp> | #include <settings.hpp> | ||||
#include <system.hpp> | #include <system.hpp> | ||||
#include <random.hpp> | #include <random.hpp> | ||||
@@ -40,17 +44,15 @@ | |||||
#include <plugin.hpp> | #include <plugin.hpp> | ||||
#include <mutex.hpp> | #include <mutex.hpp> | ||||
+#include <helpers.hpp> | +#include <helpers.hpp> | ||||
+ | |||||
+#ifdef NDEBUG | +#ifdef NDEBUG | ||||
+# undef DEBUG | +# undef DEBUG | ||||
+#endif | +#endif | ||||
+#include "DistrhoUtils.hpp" | |||||
namespace rack { | |||||
namespace engine { | |||||
-namespace rack { | |||||
-namespace engine { | |||||
- | |||||
- | |||||
-static void initMXCSR() { | -static void initMXCSR() { | ||||
- // Set CPU to flush-to-zero (FTZ) and denormals-are-zero (DAZ) mode | - // Set CPU to flush-to-zero (FTZ) and denormals-are-zero (DAZ) mode | ||||
- // https://software.intel.com/en-us/node/682949 | - // https://software.intel.com/en-us/node/682949 | ||||
@@ -107,7 +109,8 @@ | |||||
- void setThreads(int threads) { | - void setThreads(int threads) { | ||||
- this->threads = threads; | - this->threads = threads; | ||||
- } | - } | ||||
- | |||||
+#include "DistrhoUtils.hpp" | |||||
- void wait() { | - void wait() { | ||||
- uint8_t s = step; | - uint8_t s = step; | ||||
- if (count.fetch_add(1, std::memory_order_acquire) + 1 >= threads) { | - if (count.fetch_add(1, std::memory_order_acquire) + 1 >= threads) { | ||||
@@ -126,8 +129,11 @@ | |||||
- } | - } | ||||
- } | - } | ||||
-}; | -}; | ||||
- | |||||
- | |||||
+// known terminal modules | |||||
+extern rack::plugin::Model* modelHostAudio2; | |||||
+extern rack::plugin::Model* modelHostAudio8; | |||||
-/** Barrier that spin-locks until yield() is called, and then all threads switch to a mutex. | -/** Barrier that spin-locks until yield() is called, and then all threads switch to a mutex. | ||||
-yield() should be called if it is likely that all threads will block for a while and continuing to spin-lock is unnecessary. | -yield() should be called if it is likely that all threads will block for a while and continuing to spin-lock is unnecessary. | ||||
-Saves CPU power after yield is called. | -Saves CPU power after yield is called. | ||||
@@ -164,7 +170,7 @@ | |||||
- } | - } | ||||
- return; | - return; | ||||
- } | - } | ||||
- | |||||
- // Spin until the last thread begins waiting | - // Spin until the last thread begins waiting | ||||
- while (!yielded.load(std::memory_order_relaxed)) { | - while (!yielded.load(std::memory_order_relaxed)) { | ||||
- if (step.load(std::memory_order_relaxed) != s) | - if (step.load(std::memory_order_relaxed) != s) | ||||
@@ -206,17 +212,36 @@ | |||||
- | - | ||||
- void run(); | - void run(); | ||||
-}; | -}; | ||||
- | |||||
- | |||||
+namespace rack { | |||||
+namespace engine { | |||||
struct Engine::Internal { | struct Engine::Internal { | ||||
std::vector<Module*> modules; | std::vector<Module*> modules; | ||||
+ std::vector<TerminalModule*> terminalModules; | |||||
std::vector<Cable*> cables; | std::vector<Cable*> cables; | ||||
std::set<ParamHandle*> paramHandles; | std::set<ParamHandle*> paramHandles; | ||||
- Module* masterModule = NULL; | - Module* masterModule = NULL; | ||||
// moduleId | // moduleId | ||||
std::map<int64_t, Module*> modulesCache; | std::map<int64_t, Module*> modulesCache; | ||||
@@ -217,22 +91,6 @@ | |||||
@@ -199,6 +81,7 @@ | |||||
double blockTime = 0.0; | |||||
int blockFrames = 0; | |||||
+#ifndef HEADLESS | |||||
// Meter | |||||
int meterCount = 0; | |||||
double meterTotal = 0.0; | |||||
@@ -206,6 +89,7 @@ | |||||
double meterLastTime = -INFINITY; | |||||
double meterLastAverage = 0.0; | |||||
double meterLastMax = 0.0; | |||||
+#endif | |||||
// Parameter smoothing | |||||
Module* smoothModule = NULL; | |||||
@@ -217,22 +101,6 @@ | |||||
Readers lock when using the engine's state. | Readers lock when using the engine's state. | ||||
*/ | */ | ||||
SharedMutex mutex; | SharedMutex mutex; | ||||
@@ -239,7 +264,7 @@ | |||||
}; | }; | ||||
@@ -260,71 +118,6 @@ | |||||
@@ -260,76 +128,11 @@ | |||||
} | } | ||||
@@ -311,22 +336,82 @@ | |||||
static void Cable_step(Cable* that) { | static void Cable_step(Cable* that) { | ||||
Output* output = &that->outputModule->outputs[that->outputId]; | Output* output = &that->outputModule->outputs[that->outputId]; | ||||
Input* input = &that->inputModule->inputs[that->inputId]; | Input* input = &that->inputModule->inputs[that->inputId]; | ||||
@@ -373,12 +166,12 @@ | |||||
} | |||||
// Match number of polyphonic channels to output port | |||||
- int channels = output->channels; | |||||
+ const int channels = output->channels; | |||||
// Copy all voltages from output to input | |||||
for (int c = 0; c < channels; c++) { | |||||
float v = output->voltages[c]; | |||||
@@ -346,6 +149,53 @@ | |||||
} | |||||
// Step cables | |||||
- for (Cable* cable : that->internal->cables) { | |||||
+ for (Cable* cable : internal->cables) { | |||||
Cable_step(cable); | |||||
+static void Port_step(Port* that, float deltaTime) { | |||||
+ // Set plug lights | |||||
+ if (that->channels == 0) { | |||||
+ that->plugLights[0].setBrightness(0.f); | |||||
+ that->plugLights[1].setBrightness(0.f); | |||||
+ that->plugLights[2].setBrightness(0.f); | |||||
+ } | |||||
+ else if (that->channels == 1) { | |||||
+ float v = that->getVoltage() / 10.f; | |||||
+ that->plugLights[0].setSmoothBrightness(-v, deltaTime); | |||||
+ that->plugLights[1].setSmoothBrightness(v, deltaTime); | |||||
+ that->plugLights[2].setBrightness(0.f); | |||||
+ } | |||||
+ else { | |||||
+ float v = that->getVoltageRMS() / 10.f; | |||||
+ that->plugLights[0].setBrightness(0.f); | |||||
+ that->plugLights[1].setBrightness(0.f); | |||||
+ that->plugLights[2].setSmoothBrightness(v, deltaTime); | |||||
+ } | |||||
+} | |||||
+ | |||||
+ | |||||
+static void TerminalModule__doProcess(TerminalModule* terminalModule, const Module::ProcessArgs& args, bool input) { | |||||
+ // Step module | |||||
+ if (input) { | |||||
+ terminalModule->processTerminalInput(args); | |||||
+ for (Output& output : terminalModule->outputs) { | |||||
+ for (Cable* cable : output.cables) | |||||
+ Cable_step(cable); | |||||
+ } | |||||
+ } else { | |||||
+ terminalModule->processTerminalOutput(args); | |||||
+ } | |||||
+ | |||||
+ // Iterate ports to step plug lights | |||||
+ if (args.frame % 7 /* PORT_DIVIDER */ == 0) { | |||||
+ float portTime = args.sampleTime * 7 /* PORT_DIVIDER */; | |||||
+ for (Input& input : terminalModule->inputs) { | |||||
+ Port_step(&input, portTime); | |||||
+ } | |||||
+ for (Output& output : terminalModule->outputs) { | |||||
+ Port_step(&output, portTime); | |||||
+ } | |||||
+ } | |||||
+} | |||||
+ | |||||
+ | |||||
/** Steps a single frame | |||||
*/ | |||||
static void Engine_stepFrame(Engine* that) { | |||||
@@ -372,13 +222,8 @@ | |||||
} | |||||
} | } | ||||
- // Step cables | |||||
- for (Cable* cable : that->internal->cables) { | |||||
- Cable_step(cable); | |||||
- } | |||||
- | |||||
// Flip messages for each module | // Flip messages for each module | ||||
- for (Module* module : that->internal->modules) { | - for (Module* module : that->internal->modules) { | ||||
+ for (Module* module : internal->modules) { | + for (Module* module : internal->modules) { | ||||
if (module->leftExpander.messageFlipRequested) { | if (module->leftExpander.messageFlipRequested) { | ||||
std::swap(module->leftExpander.producerMessage, module->leftExpander.consumerMessage); | std::swap(module->leftExpander.producerMessage, module->leftExpander.consumerMessage); | ||||
module->leftExpander.messageFlipRequested = false; | module->leftExpander.messageFlipRequested = false; | ||||
@@ -389,13 +182,18 @@ | |||||
@@ -389,13 +234,32 @@ | |||||
} | } | ||||
} | } | ||||
@@ -341,9 +426,23 @@ | |||||
+ processArgs.sampleTime = internal->sampleTime; | + processArgs.sampleTime = internal->sampleTime; | ||||
+ processArgs.frame = internal->frame; | + processArgs.frame = internal->frame; | ||||
+ | + | ||||
+ // Step each module | |||||
+ // Process terminal inputs first | |||||
+ for (TerminalModule* terminalModule : internal->terminalModules) { | |||||
+ TerminalModule__doProcess(terminalModule, processArgs, true); | |||||
+ } | |||||
+ | |||||
+ // Step each module and cables | |||||
+ for (Module* module : internal->modules) { | + for (Module* module : internal->modules) { | ||||
+ module->doProcess(processArgs); | + module->doProcess(processArgs); | ||||
+ for (Output& output : module->outputs) { | |||||
+ for (Cable* cable : output.cables) | |||||
+ Cable_step(cable); | |||||
+ } | |||||
+ } | |||||
+ | |||||
+ // Process terminal outputs last | |||||
+ for (TerminalModule* terminalModule : internal->terminalModules) { | |||||
+ TerminalModule__doProcess(terminalModule, processArgs, false); | |||||
+ } | + } | ||||
- internal->frame++; | - internal->frame++; | ||||
@@ -351,7 +450,30 @@ | |||||
} | } | ||||
@@ -460,37 +258,22 @@ | |||||
@@ -425,6 +289,14 @@ | |||||
disconnectedPorts.insert(&output); | |||||
} | |||||
} | |||||
+ for (TerminalModule* terminalModule : that->internal->terminalModules) { | |||||
+ for (Input& input : terminalModule->inputs) { | |||||
+ disconnectedPorts.insert(&input); | |||||
+ } | |||||
+ for (Output& output : terminalModule->outputs) { | |||||
+ disconnectedPorts.insert(&output); | |||||
+ } | |||||
+ } | |||||
for (Cable* cable : that->internal->cables) { | |||||
// Connect input | |||||
Input& input = cable->inputModule->inputs[cable->inputId]; | |||||
@@ -442,6 +314,7 @@ | |||||
// Disconnect ports that have no cable | |||||
for (Port* port : disconnectedPorts) { | |||||
Port_setDisconnected(port); | |||||
+ DISTRHO_SAFE_ASSERT(port->cables.empty()); | |||||
} | |||||
} | |||||
@@ -460,37 +333,23 @@ | |||||
Engine::Engine() { | Engine::Engine() { | ||||
internal = new Internal; | internal = new Internal; | ||||
@@ -388,6 +510,7 @@ | |||||
- assert(internal->paramHandlesCache.empty()); | - assert(internal->paramHandlesCache.empty()); | ||||
+ DISTRHO_SAFE_ASSERT(internal->cables.empty()); | + DISTRHO_SAFE_ASSERT(internal->cables.empty()); | ||||
+ DISTRHO_SAFE_ASSERT(internal->modules.empty()); | + DISTRHO_SAFE_ASSERT(internal->modules.empty()); | ||||
+ DISTRHO_SAFE_ASSERT(internal->terminalModules.empty()); | |||||
+ DISTRHO_SAFE_ASSERT(internal->paramHandles.empty()); | + DISTRHO_SAFE_ASSERT(internal->paramHandles.empty()); | ||||
+ | + | ||||
+ DISTRHO_SAFE_ASSERT(internal->modulesCache.empty()); | + DISTRHO_SAFE_ASSERT(internal->modulesCache.empty()); | ||||
@@ -396,9 +519,23 @@ | |||||
delete internal; | delete internal; | ||||
} | } | ||||
@@ -526,11 +309,8 @@ | |||||
@@ -519,18 +378,22 @@ | |||||
removeModule_NoLock(module); | |||||
delete module; | |||||
} | |||||
+ std::vector<TerminalModule*> terminalModules = internal->terminalModules; | |||||
+ for (TerminalModule* terminalModule : terminalModules) { | |||||
+ removeModule_NoLock(terminalModule); | |||||
+ delete terminalModule; | |||||
+ } | |||||
} | |||||
void Engine::stepBlock(int frames) { | |||||
+#ifndef HEADLESS | |||||
// Start timer before locking | // Start timer before locking | ||||
double startTime = system::getTime(); | double startTime = system::getTime(); | ||||
+#endif | |||||
- std::lock_guard<std::mutex> stepLock(internal->blockMutex); | - std::lock_guard<std::mutex> stepLock(internal->blockMutex); | ||||
SharedLock<SharedMutex> lock(internal->mutex); | SharedLock<SharedMutex> lock(internal->mutex); | ||||
@@ -408,7 +545,7 @@ | |||||
random::init(); | random::init(); | ||||
internal->blockFrame = internal->frame; | internal->blockFrame = internal->frame; | ||||
@@ -543,16 +323,11 @@ | |||||
@@ -543,18 +406,14 @@ | |||||
Engine_updateExpander_NoLock(this, module, true); | Engine_updateExpander_NoLock(this, module, true); | ||||
} | } | ||||
@@ -424,14 +561,18 @@ | |||||
- | - | ||||
internal->block++; | internal->block++; | ||||
+#ifndef HEADLESS | |||||
// Stop timer | // Stop timer | ||||
@@ -572,47 +347,19 @@ | |||||
double endTime = system::getTime(); | |||||
double meter = (endTime - startTime) / (frames * internal->sampleTime); | |||||
@@ -572,47 +431,20 @@ | |||||
internal->meterTotal = 0.0; | internal->meterTotal = 0.0; | ||||
internal->meterMax = 0.0; | internal->meterMax = 0.0; | ||||
} | } | ||||
- | - | ||||
- // Reset MXCSR back to original value | - // Reset MXCSR back to original value | ||||
- _mm_setcsr(csr); | - _mm_setcsr(csr); | ||||
+#endif | |||||
} | } | ||||
@@ -474,7 +615,14 @@ | |||||
} | } | ||||
@@ -639,16 +386,6 @@ | |||||
@@ -635,20 +467,13 @@ | |||||
for (Module* module : internal->modules) { | |||||
module->onSampleRateChange(e); | |||||
} | |||||
+ for (TerminalModule* terminalModule : internal->terminalModules) { | |||||
+ terminalModule->onSampleRateChange(e); | |||||
+ } | |||||
} | |||||
void Engine::setSuggestedSampleRate(float suggestedSampleRate) { | void Engine::setSuggestedSampleRate(float suggestedSampleRate) { | ||||
@@ -491,7 +639,7 @@ | |||||
} | } | ||||
@@ -658,7 +395,6 @@ | |||||
@@ -658,7 +483,6 @@ | |||||
void Engine::yieldWorkers() { | void Engine::yieldWorkers() { | ||||
@@ -499,29 +647,106 @@ | |||||
} | } | ||||
@@ -738,10 +474,10 @@ | |||||
@@ -698,17 +522,25 @@ | |||||
double Engine::getMeterAverage() { | |||||
+#ifndef HEADLESS | |||||
return internal->meterLastAverage; | |||||
+#else | |||||
+ return 0.0; | |||||
+#endif | |||||
} | |||||
double Engine::getMeterMax() { | |||||
+#ifndef HEADLESS | |||||
return internal->meterLastMax; | |||||
+#else | |||||
+ return 0.0; | |||||
+#endif | |||||
} | |||||
size_t Engine::getNumModules() { | |||||
- return internal->modules.size(); | |||||
+ return internal->modules.size() + internal->terminalModules.size(); | |||||
} | |||||
@@ -718,8 +550,12 @@ | |||||
for (Module* m : internal->modules) { | |||||
if (i >= len) | |||||
break; | |||||
- moduleIds[i] = m->id; | |||||
- i++; | |||||
+ moduleIds[i++] = m->id; | |||||
+ } | |||||
+ for (TerminalModule* m : internal->terminalModules) { | |||||
+ if (i >= len) | |||||
+ break; | |||||
+ moduleIds[i++] = m->id; | |||||
} | |||||
return i; | |||||
} | |||||
@@ -728,27 +564,43 @@ | |||||
std::vector<int64_t> Engine::getModuleIds() { | |||||
SharedLock<SharedMutex> lock(internal->mutex); | |||||
std::vector<int64_t> moduleIds; | |||||
- moduleIds.reserve(internal->modules.size()); | |||||
+ moduleIds.reserve(getNumModules()); | |||||
for (Module* m : internal->modules) { | |||||
moduleIds.push_back(m->id); | |||||
} | |||||
+ for (TerminalModule* tm : internal->terminalModules) { | |||||
+ moduleIds.push_back(tm->id); | |||||
+ } | |||||
return moduleIds; | |||||
} | |||||
+static TerminalModule* asTerminalModule(Module* const module) { | |||||
+ const plugin::Model* const model = module->model; | |||||
+ if (model == modelHostAudio2 || model == modelHostAudio8) | |||||
+ return static_cast<TerminalModule*>(module); | |||||
+ return nullptr; | |||||
+} | |||||
+ | |||||
+ | |||||
void Engine::addModule(Module* module) { | void Engine::addModule(Module* module) { | ||||
std::lock_guard<SharedMutex> lock(internal->mutex); | std::lock_guard<SharedMutex> lock(internal->mutex); | ||||
- assert(module); | - assert(module); | ||||
+ DISTRHO_SAFE_ASSERT_RETURN(module,); | |||||
+ DISTRHO_SAFE_ASSERT_RETURN(module != nullptr,); | |||||
// Check that the module is not already added | // Check that the module is not already added | ||||
auto it = std::find(internal->modules.begin(), internal->modules.end(), module); | auto it = std::find(internal->modules.begin(), internal->modules.end(), module); | ||||
- assert(it == internal->modules.end()); | - assert(it == internal->modules.end()); | ||||
+ DISTRHO_SAFE_ASSERT_RETURN(it == internal->modules.end(),); | + DISTRHO_SAFE_ASSERT_RETURN(it == internal->modules.end(),); | ||||
+ auto tit = std::find(internal->terminalModules.begin(), internal->terminalModules.end(), module); | |||||
+ DISTRHO_SAFE_ASSERT_RETURN(tit == internal->terminalModules.end(),); | |||||
// Set ID if unset or collides with an existing ID | // Set ID if unset or collides with an existing ID | ||||
while (module->id < 0 || internal->modulesCache.find(module->id) != internal->modulesCache.end()) { | while (module->id < 0 || internal->modulesCache.find(module->id) != internal->modulesCache.end()) { | ||||
// Randomly generate ID | // Randomly generate ID | ||||
@@ -773,10 +509,14 @@ | |||||
module->id = random::u64() % (1ull << 53); | |||||
} | |||||
// Add module | |||||
- internal->modules.push_back(module); | |||||
+ if (TerminalModule* const terminalModule = asTerminalModule(module)) | |||||
+ internal->terminalModules.push_back(terminalModule); | |||||
+ else | |||||
+ internal->modules.push_back(module); | |||||
internal->modulesCache[module->id] = module; | |||||
// Dispatch AddEvent | |||||
Module::AddEvent eAdd; | |||||
@@ -772,11 +624,11 @@ | |||||
} | |||||
void Engine::removeModule_NoLock(Module* module) { | |||||
-void Engine::removeModule_NoLock(Module* module) { | |||||
- assert(module); | - assert(module); | ||||
+ DISTRHO_SAFE_ASSERT_RETURN(module,); | |||||
// Check that the module actually exists | |||||
auto it = std::find(internal->modules.begin(), internal->modules.end(), module); | |||||
- // Check that the module actually exists | |||||
- auto it = std::find(internal->modules.begin(), internal->modules.end(), module); | |||||
- assert(it != internal->modules.end()); | - assert(it != internal->modules.end()); | ||||
+ DISTRHO_SAFE_ASSERT_RETURN(it != internal->modules.end(),); | |||||
+static void removeModule_NoLock_common(Engine::Internal* internal, Module* module) { | |||||
+ // Remove from widgets cache | + // Remove from widgets cache | ||||
+ CardinalPluginModelHelper* const helper = dynamic_cast<CardinalPluginModelHelper*>(module->model); | + CardinalPluginModelHelper* const helper = dynamic_cast<CardinalPluginModelHelper*>(module->model); | ||||
+ DISTRHO_SAFE_ASSERT_RETURN(helper != nullptr,); | + DISTRHO_SAFE_ASSERT_RETURN(helper != nullptr,); | ||||
@@ -529,7 +754,7 @@ | |||||
// Dispatch RemoveEvent | // Dispatch RemoveEvent | ||||
Module::RemoveEvent eRemove; | Module::RemoveEvent eRemove; | ||||
module->onRemove(eRemove); | module->onRemove(eRemove); | ||||
@@ -785,18 +525,14 @@ | |||||
@@ -785,18 +637,14 @@ | |||||
if (paramHandle->moduleId == module->id) | if (paramHandle->moduleId == module->id) | ||||
paramHandle->module = NULL; | paramHandle->module = NULL; | ||||
} | } | ||||
@@ -550,7 +775,52 @@ | |||||
} | } | ||||
// Update expanders of other modules | // Update expanders of other modules | ||||
for (Module* m : internal->modules) { | for (Module* m : internal->modules) { | ||||
@@ -844,7 +580,7 @@ | |||||
@@ -809,14 +657,31 @@ | |||||
m->rightExpander.module = NULL; | |||||
} | |||||
} | |||||
- // Remove module | |||||
- internal->modulesCache.erase(module->id); | |||||
- internal->modules.erase(it); | |||||
// Reset expanders | |||||
module->leftExpander.moduleId = -1; | |||||
module->leftExpander.module = NULL; | |||||
module->rightExpander.moduleId = -1; | |||||
module->rightExpander.module = NULL; | |||||
+ // Remove module | |||||
+ internal->modulesCache.erase(module->id); | |||||
+} | |||||
+ | |||||
+ | |||||
+void Engine::removeModule_NoLock(Module* module) { | |||||
+ DISTRHO_SAFE_ASSERT_RETURN(module,); | |||||
+ // Check that the module actually exists | |||||
+ if (TerminalModule* const terminalModule = asTerminalModule(module)) { | |||||
+ auto tit = std::find(internal->terminalModules.begin(), internal->terminalModules.end(), terminalModule); | |||||
+ DISTRHO_SAFE_ASSERT_RETURN(tit != internal->terminalModules.end(),); | |||||
+ removeModule_NoLock_common(internal, module); | |||||
+ internal->terminalModules.erase(tit); | |||||
+ } | |||||
+ else { | |||||
+ auto it = std::find(internal->modules.begin(), internal->modules.end(), module); | |||||
+ DISTRHO_SAFE_ASSERT_RETURN(it != internal->modules.end(),); | |||||
+ removeModule_NoLock_common(internal, module); | |||||
+ internal->modules.erase(it); | |||||
+ } | |||||
} | |||||
@@ -824,7 +689,8 @@ | |||||
SharedLock<SharedMutex> lock(internal->mutex); | |||||
// TODO Performance could be improved by searching modulesCache, but more testing would be needed to make sure it's always valid. | |||||
auto it = std::find(internal->modules.begin(), internal->modules.end(), module); | |||||
- return it != internal->modules.end(); | |||||
+ auto tit = std::find(internal->terminalModules.begin(), internal->terminalModules.end(), module); | |||||
+ return it != internal->modules.end() && tit != internal->terminalModules.end(); | |||||
} | |||||
@@ -844,7 +710,7 @@ | |||||
void Engine::resetModule(Module* module) { | void Engine::resetModule(Module* module) { | ||||
std::lock_guard<SharedMutex> lock(internal->mutex); | std::lock_guard<SharedMutex> lock(internal->mutex); | ||||
@@ -559,7 +829,7 @@ | |||||
Module::ResetEvent eReset; | Module::ResetEvent eReset; | ||||
module->onReset(eReset); | module->onReset(eReset); | ||||
@@ -853,7 +589,7 @@ | |||||
@@ -853,7 +719,7 @@ | |||||
void Engine::randomizeModule(Module* module) { | void Engine::randomizeModule(Module* module) { | ||||
std::lock_guard<SharedMutex> lock(internal->mutex); | std::lock_guard<SharedMutex> lock(internal->mutex); | ||||
@@ -568,7 +838,7 @@ | |||||
Module::RandomizeEvent eRandomize; | Module::RandomizeEvent eRandomize; | ||||
module->onRandomize(eRandomize); | module->onRandomize(eRandomize); | ||||
@@ -861,7 +597,7 @@ | |||||
@@ -861,7 +727,7 @@ | |||||
void Engine::bypassModule(Module* module, bool bypassed) { | void Engine::bypassModule(Module* module, bool bypassed) { | ||||
@@ -577,7 +847,18 @@ | |||||
if (module->isBypassed() == bypassed) | if (module->isBypassed() == bypassed) | ||||
return; | return; | ||||
@@ -946,16 +682,16 @@ | |||||
@@ -912,6 +778,10 @@ | |||||
Module::SaveEvent e; | |||||
module->onSave(e); | |||||
} | |||||
+ for (TerminalModule* terminalModule : internal->terminalModules) { | |||||
+ Module::SaveEvent e; | |||||
+ terminalModule->onSave(e); | |||||
+ } | |||||
} | |||||
@@ -946,16 +816,16 @@ | |||||
void Engine::addCable(Cable* cable) { | void Engine::addCable(Cable* cable) { | ||||
std::lock_guard<SharedMutex> lock(internal->mutex); | std::lock_guard<SharedMutex> lock(internal->mutex); | ||||
@@ -599,7 +880,16 @@ | |||||
// Get connected status of output, to decide whether we need to call a PortChangeEvent. | // Get connected status of output, to decide whether we need to call a PortChangeEvent. | ||||
// It's best to not trust `cable->outputModule->outputs[cable->outputId]->isConnected()` | // It's best to not trust `cable->outputModule->outputs[cable->outputId]->isConnected()` | ||||
if (cable2->outputModule == cable->outputModule && cable2->outputId == cable->outputId) | if (cable2->outputModule == cable->outputModule && cable2->outputId == cable->outputId) | ||||
@@ -996,10 +732,10 @@ | |||||
@@ -969,6 +839,8 @@ | |||||
// Add the cable | |||||
internal->cables.push_back(cable); | |||||
internal->cablesCache[cable->id] = cable; | |||||
+ // Add the cable's zero-latency shortcut | |||||
+ cable->outputModule->outputs[cable->outputId].cables.push_back(cable); | |||||
Engine_updateConnected(this); | |||||
// Dispatch input port event | |||||
{ | |||||
@@ -996,10 +868,12 @@ | |||||
void Engine::removeCable_NoLock(Cable* cable) { | void Engine::removeCable_NoLock(Cable* cable) { | ||||
@@ -609,10 +899,12 @@ | |||||
auto it = std::find(internal->cables.begin(), internal->cables.end(), cable); | auto it = std::find(internal->cables.begin(), internal->cables.end(), cable); | ||||
- assert(it != internal->cables.end()); | - assert(it != internal->cables.end()); | ||||
+ DISTRHO_SAFE_ASSERT_RETURN(it != internal->cables.end(),); | + DISTRHO_SAFE_ASSERT_RETURN(it != internal->cables.end(),); | ||||
+ // Remove the cable's zero-latency shortcut | |||||
+ cable->outputModule->outputs[cable->outputId].cables.remove(cable); | |||||
// Remove the cable | // Remove the cable | ||||
internal->cablesCache.erase(cable->id); | internal->cablesCache.erase(cable->id); | ||||
internal->cables.erase(it); | internal->cables.erase(it); | ||||
@@ -1085,11 +821,11 @@ | |||||
@@ -1085,11 +959,11 @@ | |||||
std::lock_guard<SharedMutex> lock(internal->mutex); | std::lock_guard<SharedMutex> lock(internal->mutex); | ||||
// New ParamHandles must be blank. | // New ParamHandles must be blank. | ||||
// This means we don't have to refresh the cache. | // This means we don't have to refresh the cache. | ||||
@@ -626,7 +918,7 @@ | |||||
// Add it | // Add it | ||||
internal->paramHandles.insert(paramHandle); | internal->paramHandles.insert(paramHandle); | ||||
@@ -1106,7 +842,7 @@ | |||||
@@ -1106,7 +980,7 @@ | |||||
void Engine::removeParamHandle_NoLock(ParamHandle* paramHandle) { | void Engine::removeParamHandle_NoLock(ParamHandle* paramHandle) { | ||||
// Check that the ParamHandle is already added | // Check that the ParamHandle is already added | ||||
auto it = internal->paramHandles.find(paramHandle); | auto it = internal->paramHandles.find(paramHandle); | ||||
@@ -635,7 +927,7 @@ | |||||
// Remove it | // Remove it | ||||
paramHandle->module = NULL; | paramHandle->module = NULL; | ||||
@@ -1143,7 +879,7 @@ | |||||
@@ -1143,7 +1017,7 @@ | |||||
void Engine::updateParamHandle_NoLock(ParamHandle* paramHandle, int64_t moduleId, int paramId, bool overwrite) { | void Engine::updateParamHandle_NoLock(ParamHandle* paramHandle, int64_t moduleId, int paramId, bool overwrite) { | ||||
// Check that it exists | // Check that it exists | ||||
auto it = internal->paramHandles.find(paramHandle); | auto it = internal->paramHandles.find(paramHandle); | ||||
@@ -644,7 +936,18 @@ | |||||
// Set IDs | // Set IDs | ||||
paramHandle->moduleId = moduleId; | paramHandle->moduleId = moduleId; | ||||
@@ -1197,11 +933,6 @@ | |||||
@@ -1187,6 +1061,10 @@ | |||||
json_t* moduleJ = module->toJson(); | |||||
json_array_append_new(modulesJ, moduleJ); | |||||
} | |||||
+ for (TerminalModule* terminalModule : internal->terminalModules) { | |||||
+ json_t* terminalModuleJ = terminalModule->toJson(); | |||||
+ json_array_append_new(modulesJ, terminalModuleJ); | |||||
+ } | |||||
json_object_set_new(rootJ, "modules", modulesJ); | |||||
// cables | |||||
@@ -1197,11 +1075,6 @@ | |||||
} | } | ||||
json_object_set_new(rootJ, "cables", cablesJ); | json_object_set_new(rootJ, "cables", cablesJ); | ||||
@@ -656,7 +959,7 @@ | |||||
return rootJ; | return rootJ; | ||||
} | } | ||||
@@ -1225,14 +956,20 @@ | |||||
@@ -1225,14 +1098,20 @@ | |||||
} | } | ||||
catch (Exception& e) { | catch (Exception& e) { | ||||
WARN("Cannot load model: %s", e.what()); | WARN("Cannot load model: %s", e.what()); | ||||
@@ -681,7 +984,7 @@ | |||||
try { | try { | ||||
// This doesn't need a lock because the Module is not added to the Engine yet. | // This doesn't need a lock because the Module is not added to the Engine yet. | ||||
@@ -1248,7 +985,8 @@ | |||||
@@ -1248,7 +1127,8 @@ | |||||
} | } | ||||
catch (Exception& e) { | catch (Exception& e) { | ||||
WARN("Cannot load module: %s", e.what()); | WARN("Cannot load module: %s", e.what()); | ||||
@@ -691,7 +994,7 @@ | |||||
delete module; | delete module; | ||||
continue; | continue; | ||||
} | } | ||||
@@ -1285,67 +1023,10 @@ | |||||
@@ -1285,67 +1165,10 @@ | |||||
continue; | continue; | ||||
} | } | ||||
} | } | ||||
@@ -1,5 +1,5 @@ | |||||
--- ../Rack/src/app/MenuBar.cpp 2022-01-15 14:44:46.391280963 +0000 | |||||
+++ MenuBar.cpp 2022-01-24 11:25:15.507061204 +0000 | |||||
--- ../Rack/src/app/MenuBar.cpp 2022-02-05 22:30:09.233392896 +0000 | |||||
+++ MenuBar.cpp 2022-02-05 18:08:00.272028714 +0000 | |||||
@@ -1,8 +1,33 @@ | @@ -1,8 +1,33 @@ | ||||
+/* | +/* | ||||
+ * DISTRHO Cardinal Plugin | + * DISTRHO Cardinal Plugin | ||||
@@ -48,7 +48,7 @@ | |||||
namespace rack { | namespace rack { | ||||
namespace app { | namespace app { | ||||
@@ -48,79 +78,75 @@ | |||||
@@ -48,79 +78,79 @@ | |||||
}; | }; | ||||
@@ -103,25 +103,34 @@ | |||||
- | - | ||||
menu->addChild(createMenuItem("Save", RACK_MOD_CTRL_NAME "+S", []() { | menu->addChild(createMenuItem("Save", RACK_MOD_CTRL_NAME "+S", []() { | ||||
- APP->patch->saveDialog(); | - APP->patch->saveDialog(); | ||||
- })); | |||||
+ // NOTE: will do nothing if path is empty, intentionally | + // NOTE: will do nothing if path is empty, intentionally | ||||
+ patchUtils::saveDialog(APP->patch->path); | + patchUtils::saveDialog(APP->patch->path); | ||||
+ }, APP->patch->path.empty())); | + }, APP->patch->path.empty())); | ||||
+ | |||||
- menu->addChild(createMenuItem("Save as", RACK_MOD_CTRL_NAME "+Shift+S", []() { | |||||
- APP->patch->saveAsDialog(); | |||||
+ menu->addChild(createMenuItem("Save as / Export...", RACK_MOD_CTRL_NAME "+Shift+S", []() { | + menu->addChild(createMenuItem("Save as / Export...", RACK_MOD_CTRL_NAME "+Shift+S", []() { | ||||
+ patchUtils::saveAsDialog(); | + patchUtils::saveAsDialog(); | ||||
})); | })); | ||||
- menu->addChild(createMenuItem("Save as", RACK_MOD_CTRL_NAME "+Shift+S", []() { | |||||
- APP->patch->saveAsDialog(); | |||||
- })); | |||||
- menu->addChild(createMenuItem("Save a copy", "", []() { | |||||
- APP->patch->saveAsDialog(false); | |||||
+ menu->addChild(createMenuItem("Export uncompressed json...", "", []() { | |||||
+ patchUtils::saveAsDialogUncompressed(); | |||||
})); | |||||
- menu->addChild(createMenuItem("Revert", RACK_MOD_CTRL_NAME "+" RACK_MOD_SHIFT_NAME "+O", []() { | |||||
- APP->patch->revertDialog(); | |||||
- }, APP->patch->path == "")); | |||||
+#ifdef HAVE_LIBLO | +#ifdef HAVE_LIBLO | ||||
+ if (patchUtils::isRemoteConnected()) { | + if (patchUtils::isRemoteConnected()) { | ||||
+ menu->addChild(createMenuItem("Deploy to MOD", "F7", []() { | + menu->addChild(createMenuItem("Deploy to MOD", "F7", []() { | ||||
+ patchUtils::deployToRemote(); | + patchUtils::deployToRemote(); | ||||
+ })); | + })); | ||||
- menu->addChild(createMenuItem("Save a copy", "", []() { | |||||
- APP->patch->saveAsDialog(false); | |||||
- menu->addChild(createMenuItem("Overwrite template", "", []() { | |||||
- APP->patch->saveTemplateDialog(); | |||||
- })); | - })); | ||||
+ const bool autoDeploy = patchUtils::isRemoteAutoDeployed(); | + const bool autoDeploy = patchUtils::isRemoteAutoDeployed(); | ||||
+ menu->addChild(createCheckMenuItem("Auto deploy to MOD", "", | + menu->addChild(createCheckMenuItem("Auto deploy to MOD", "", | ||||
@@ -129,19 +138,13 @@ | |||||
+ [=]() {patchUtils::setRemoteAutoDeploy(!autoDeploy);} | + [=]() {patchUtils::setRemoteAutoDeploy(!autoDeploy);} | ||||
+ )); | + )); | ||||
+ } else { | + } else { | ||||
+ menu->addChild(createMenuItem("Connect to MOD", "", [this]() { | |||||
+ menu->addChild(createMenuItem("Connect to MOD", "", []() { | |||||
+ patchUtils::connectToRemote(); | + patchUtils::connectToRemote(); | ||||
+ })); | + })); | ||||
+ } | + } | ||||
+#endif | +#endif | ||||
menu->addChild(createMenuItem("Revert", RACK_MOD_CTRL_NAME "+" RACK_MOD_SHIFT_NAME "+O", []() { | |||||
- APP->patch->revertDialog(); | |||||
- }, APP->patch->path == "")); | |||||
- | |||||
- menu->addChild(createMenuItem("Overwrite template", "", []() { | |||||
- APP->patch->saveTemplateDialog(); | |||||
- })); | |||||
+ | |||||
+ menu->addChild(createMenuItem("Revert", RACK_MOD_CTRL_NAME "+" RACK_MOD_SHIFT_NAME "+O", []() { | |||||
+ patchUtils::revertDialog(); | + patchUtils::revertDialog(); | ||||
+ }, APP->patch->path.empty())); | + }, APP->patch->path.empty())); | ||||
@@ -167,7 +170,7 @@ | |||||
} | } | ||||
}; | }; | ||||
@@ -166,7 +192,7 @@ | |||||
@@ -166,7 +196,7 @@ | |||||
menu->addChild(new ui::MenuSeparator); | menu->addChild(new ui::MenuSeparator); | ||||
@@ -176,7 +179,7 @@ | |||||
} | } | ||||
}; | }; | ||||
@@ -256,7 +282,7 @@ | |||||
@@ -256,7 +286,7 @@ | |||||
return settings::cableTension; | return settings::cableTension; | ||||
} | } | ||||
float getDefaultValue() override { | float getDefaultValue() override { | ||||
@@ -185,7 +188,7 @@ | |||||
} | } | ||||
float getDisplayValue() override { | float getDisplayValue() override { | ||||
return getValue() * 100; | return getValue() * 100; | ||||
@@ -421,28 +447,9 @@ | |||||
@@ -421,28 +451,9 @@ | |||||
haloBrightnessSlider->box.size.x = 250.0; | haloBrightnessSlider->box.size.x = 250.0; | ||||
menu->addChild(haloBrightnessSlider); | menu->addChild(haloBrightnessSlider); | ||||
@@ -215,7 +218,7 @@ | |||||
static const std::vector<std::string> knobModeLabels = { | static const std::vector<std::string> knobModeLabels = { | ||||
"Linear", | "Linear", | ||||
@@ -467,6 +474,21 @@ | |||||
@@ -467,6 +478,21 @@ | |||||
menu->addChild(knobScrollSensitivitySlider); | menu->addChild(knobScrollSensitivitySlider); | ||||
menu->addChild(createBoolPtrMenuItem("Lock module positions", "", &settings::lockModules)); | menu->addChild(createBoolPtrMenuItem("Lock module positions", "", &settings::lockModules)); | ||||
@@ -237,7 +240,7 @@ | |||||
} | } | ||||
}; | }; | ||||
@@ -476,47 +498,6 @@ | |||||
@@ -476,47 +502,6 @@ | |||||
//////////////////// | //////////////////// | ||||
@@ -285,7 +288,7 @@ | |||||
struct EngineButton : MenuButton { | struct EngineButton : MenuButton { | ||||
void onAction(const ActionEvent& e) override { | void onAction(const ActionEvent& e) override { | ||||
ui::Menu* menu = createMenu(); | ui::Menu* menu = createMenu(); | ||||
@@ -529,269 +510,6 @@ | |||||
@@ -529,269 +514,6 @@ | |||||
menu->addChild(createMenuItem("Performance meters", cpuMeterText, [=]() { | menu->addChild(createMenuItem("Performance meters", cpuMeterText, [=]() { | ||||
settings::cpuMeter ^= true; | settings::cpuMeter ^= true; | ||||
})); | })); | ||||
@@ -555,7 +558,7 @@ | |||||
} | } | ||||
}; | }; | ||||
@@ -802,63 +520,24 @@ | |||||
@@ -802,63 +524,23 @@ | |||||
struct HelpButton : MenuButton { | struct HelpButton : MenuButton { | ||||
@@ -614,17 +617,17 @@ | |||||
- menu->addChild(createMenuLabel(APP_NAME + " " + APP_EDITION_NAME + " " + APP_VERSION)); | - menu->addChild(createMenuLabel(APP_NAME + " " + APP_EDITION_NAME + " " + APP_VERSION)); | ||||
- } | - } | ||||
+ menu->addChild(createMenuLabel(APP_EDITION + " " + APP_EDITION_NAME)); | |||||
- | |||||
- void step() override { | - void step() override { | ||||
- notification->box.pos = math::Vec(0, 0); | - notification->box.pos = math::Vec(0, 0); | ||||
- notification->visible = library::isAppUpdateAvailable(); | - notification->visible = library::isAppUpdateAvailable(); | ||||
- MenuButton::step(); | - MenuButton::step(); | ||||
+ menu->addChild(createMenuLabel("Cardinal " + APP_EDITION + " " + CARDINAL_VERSION)); | |||||
+ menu->addChild(createMenuLabel("Rack " + APP_VERSION + " Compatible")); | + menu->addChild(createMenuLabel("Rack " + APP_VERSION + " Compatible")); | ||||
} | } | ||||
}; | }; | ||||
@@ -908,7 +587,9 @@ | |||||
@@ -908,7 +590,9 @@ | |||||
struct MenuBar : widget::OpaqueWidget { | struct MenuBar : widget::OpaqueWidget { | ||||
MeterLabel* meterLabel; | MeterLabel* meterLabel; | ||||
@@ -635,7 +638,7 @@ | |||||
const float margin = 5; | const float margin = 5; | ||||
box.size.y = BND_WIDGET_HEIGHT + 2 * margin; | box.size.y = BND_WIDGET_HEIGHT + 2 * margin; | ||||
@@ -917,7 +598,7 @@ | |||||
@@ -917,7 +601,7 @@ | |||||
layout->spacing = math::Vec(0, 0); | layout->spacing = math::Vec(0, 0); | ||||
addChild(layout); | addChild(layout); | ||||
@@ -644,7 +647,7 @@ | |||||
fileButton->text = "File"; | fileButton->text = "File"; | ||||
layout->addChild(fileButton); | layout->addChild(fileButton); | ||||
@@ -933,10 +614,6 @@ | |||||
@@ -933,10 +617,6 @@ | |||||
engineButton->text = "Engine"; | engineButton->text = "Engine"; | ||||
layout->addChild(engineButton); | layout->addChild(engineButton); | ||||
@@ -655,7 +658,7 @@ | |||||
HelpButton* helpButton = new HelpButton; | HelpButton* helpButton = new HelpButton; | ||||
helpButton->text = "Help"; | helpButton->text = "Help"; | ||||
layout->addChild(helpButton); | layout->addChild(helpButton); | ||||
@@ -971,7 +648,11 @@ | |||||
@@ -971,7 +651,11 @@ | |||||
widget::Widget* createMenuBar() { | widget::Widget* createMenuBar() { | ||||
@@ -1,5 +1,5 @@ | |||||
--- ../Rack/src/app/Scene.cpp 2021-12-14 21:35:44.414568198 +0000 | --- ../Rack/src/app/Scene.cpp 2021-12-14 21:35:44.414568198 +0000 | ||||
+++ Scene.cpp 2022-01-26 18:47:48.006168325 +0000 | |||||
+++ Scene.cpp 2022-02-06 14:11:59.259830276 +0000 | |||||
@@ -1,3 +1,30 @@ | @@ -1,3 +1,30 @@ | ||||
+/* | +/* | ||||
+ * DISTRHO Cardinal Plugin | + * DISTRHO Cardinal Plugin | ||||
@@ -108,13 +108,13 @@ | |||||
+ | + | ||||
+ void onEnter(const EnterEvent& e) override { | + void onEnter(const EnterEvent& e) override { | ||||
+ glfwSetCursor(nullptr, (GLFWcursor*)0x1); | + glfwSetCursor(nullptr, (GLFWcursor*)0x1); | ||||
+ } | |||||
+ | |||||
+ void onLeave(const LeaveEvent& e) override { | |||||
+ glfwSetCursor(nullptr, nullptr); | |||||
} | } | ||||
- void onDragStart(const DragStartEvent& e) override { | - void onDragStart(const DragStartEvent& e) override { | ||||
+ void onLeave(const LeaveEvent& e) override { | |||||
+ glfwSetCursor(nullptr, nullptr); | |||||
+ } | |||||
+ | |||||
+ void onDragStart(const DragStartEvent&) override { | + void onDragStart(const DragStartEvent&) override { | ||||
size = APP->window->getSize(); | size = APP->window->getSize(); | ||||
} | } | ||||
@@ -260,7 +260,23 @@ | |||||
e.consume(this); | e.consume(this); | ||||
} | } | ||||
if (e.keyName == "z" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) { | if (e.keyName == "z" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) { | ||||
@@ -232,10 +326,8 @@ | |||||
@@ -220,10 +314,14 @@ | |||||
APP->scene->rackScroll->setZoom(std::pow(2.f, zoom)); | |||||
e.consume(this); | |||||
} | |||||
- if ((e.keyName == "0") && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) { | |||||
+ if ((e.keyName == "0" || e.keyName == "1") && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) { | |||||
APP->scene->rackScroll->setZoom(1.f); | |||||
e.consume(this); | |||||
} | |||||
+ if (e.keyName == "2" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) { | |||||
+ APP->scene->rackScroll->setZoom(2.f); | |||||
+ e.consume(this); | |||||
+ } | |||||
if (e.key == GLFW_KEY_F1 && (e.mods & RACK_MOD_MASK) == 0) { | |||||
system::openBrowser("https://vcvrack.com/manual/"); | |||||
e.consume(this); | |||||
@@ -232,10 +330,8 @@ | |||||
settings::cpuMeter ^= true; | settings::cpuMeter ^= true; | ||||
e.consume(this); | e.consume(this); | ||||
} | } | ||||
@@ -273,7 +289,7 @@ | |||||
e.consume(this); | e.consume(this); | ||||
} | } | ||||
@@ -326,13 +418,6 @@ | |||||
@@ -326,13 +422,6 @@ | |||||
// Key commands that can be overridden by children | // Key commands that can be overridden by children | ||||
if (e.action == GLFW_PRESS || e.action == GLFW_REPEAT) { | if (e.action == GLFW_PRESS || e.action == GLFW_REPEAT) { | ||||
@@ -287,7 +303,7 @@ | |||||
if (e.keyName == "v" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) { | if (e.keyName == "v" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) { | ||||
rack->pasteClipboardAction(); | rack->pasteClipboardAction(); | ||||
e.consume(this); | e.consume(this); | ||||
@@ -351,7 +436,7 @@ | |||||
@@ -351,7 +440,7 @@ | |||||
std::string extension = system::getExtension(path); | std::string extension = system::getExtension(path); | ||||
if (extension == ".vcv") { | if (extension == ".vcv") { | ||||
@@ -296,7 +312,7 @@ | |||||
e.consume(this); | e.consume(this); | ||||
return; | return; | ||||
} | } | ||||
@@ -368,3 +453,73 @@ | |||||
@@ -368,3 +457,77 @@ | |||||
} // namespace app | } // namespace app | ||||
} // namespace rack | } // namespace rack | ||||
@@ -306,6 +322,7 @@ | |||||
+ | + | ||||
+ | + | ||||
+bool connectToRemote() { | +bool connectToRemote() { | ||||
+#ifdef HAVE_LIBLO | |||||
+ rack::app::Scene::Internal* const internal = APP->scene->internal; | + rack::app::Scene::Internal* const internal = APP->scene->internal; | ||||
+ | + | ||||
+ if (internal->oscServer == nullptr) { | + if (internal->oscServer == nullptr) { | ||||
@@ -321,6 +338,9 @@ | |||||
+ lo_address_free(addr); | + lo_address_free(addr); | ||||
+ | + | ||||
+ return true; | + return true; | ||||
+#else | |||||
+ return false; | |||||
+#endif | |||||
+} | +} | ||||
+ | + | ||||
+ | + | ||||
@@ -1,5 +1,5 @@ | |||||
--- ../Rack/src/common.cpp 2021-11-23 19:57:23.719015894 +0000 | --- ../Rack/src/common.cpp 2021-11-23 19:57:23.719015894 +0000 | ||||
+++ common.cpp 2022-01-23 17:13:08.824652617 +0000 | |||||
+++ common.cpp 2022-01-31 13:24:14.558807713 +0000 | |||||
@@ -1,6 +1,38 @@ | @@ -1,6 +1,38 @@ | ||||
+/* | +/* | ||||
+ * DISTRHO Cardinal Plugin | + * DISTRHO Cardinal Plugin | ||||
@@ -1,4 +1,4 @@ | |||||
--- ../Rack/src/context.cpp 2022-01-15 14:44:46.391280963 +0000 | |||||
--- ../Rack/src/context.cpp 2022-02-05 22:30:09.253393116 +0000 | |||||
+++ context.cpp 2022-01-23 17:13:11.652514338 +0000 | +++ context.cpp 2022-01-23 17:13:11.652514338 +0000 | ||||
@@ -1,3 +1,30 @@ | @@ -1,3 +1,30 @@ | ||||
+/* | +/* | ||||
@@ -1,5 +1,5 @@ | |||||
--- ../Rack/src/plugin.cpp 2022-01-15 14:44:46.395281005 +0000 | |||||
+++ plugin.cpp 2022-01-24 20:38:11.436099651 +0000 | |||||
--- ../Rack/src/plugin.cpp 2022-02-05 22:30:09.265393248 +0000 | |||||
+++ plugin.cpp 2022-01-30 00:24:49.375329910 +0000 | |||||
@@ -1,308 +1,40 @@ | @@ -1,308 +1,40 @@ | ||||
-#include <thread> | -#include <thread> | ||||
-#include <map> | -#include <map> | ||||
@@ -337,7 +337,7 @@ | |||||
/** Given slug => fallback slug. | /** Given slug => fallback slug. | ||||
Correctly handles bidirectional fallbacks. | Correctly handles bidirectional fallbacks. | ||||
To request fallback slugs to be added to this list, open a GitHub issue. | To request fallback slugs to be added to this list, open a GitHub issue. | ||||
@@ -352,6 +84,12 @@ | |||||
@@ -352,8 +84,19 @@ | |||||
*/ | */ | ||||
using PluginModuleSlug = std::tuple<std::string, std::string>; | using PluginModuleSlug = std::tuple<std::string, std::string>; | ||||
static const std::map<PluginModuleSlug, PluginModuleSlug> moduleSlugFallbacks = { | static const std::map<PluginModuleSlug, PluginModuleSlug> moduleSlugFallbacks = { | ||||
@@ -345,12 +345,20 @@ | |||||
+ {{"Core", "AudioInterface"}, {"Cardinal", "HostAudio8"}}, | + {{"Core", "AudioInterface"}, {"Cardinal", "HostAudio8"}}, | ||||
+ {{"Core", "AudioInterface16"}, {"Cardinal", "HostAudio8"}}, | + {{"Core", "AudioInterface16"}, {"Cardinal", "HostAudio8"}}, | ||||
+ {{"Core", "MIDIToCVInterface"}, {"Cardinal", "HostMIDI"}}, | + {{"Core", "MIDIToCVInterface"}, {"Cardinal", "HostMIDI"}}, | ||||
+ {{"Core", "MIDICCToCVInterface"}, {"Cardinal", "HostMIDICC"}}, | |||||
+ {{"Core", "MIDITriggerToCVInterface"}, {"Cardinal", "HostMIDIGate"}}, | |||||
+ {{"Core", "CV-MIDI"}, {"Cardinal", "HostMIDI"}}, | + {{"Core", "CV-MIDI"}, {"Cardinal", "HostMIDI"}}, | ||||
+ {{"Core", "CV-CC"}, {"Cardinal", "HostMIDICC"}}, | |||||
+ {{"Core", "CV-Gate"}, {"Cardinal", "HostMIDIGate"}}, | |||||
+ {{"Core", "MIDI-Map"}, {"Cardinal", "HostMIDIMap"}}, | |||||
+ {{"Core", "Notes"}, {"Cardinal", "TextEditor"}}, | + {{"Core", "Notes"}, {"Cardinal", "TextEditor"}}, | ||||
+ {{"Core", "Blank"}, {"Cardinal", "Blank"}}, | |||||
{{"MindMeld-ShapeMasterPro", "ShapeMasterPro"}, {"MindMeldModular", "ShapeMaster"}}, | {{"MindMeld-ShapeMasterPro", "ShapeMasterPro"}, {"MindMeldModular", "ShapeMaster"}}, | ||||
{{"MindMeldModular", "ShapeMaster"}, {"MindMeld-ShapeMasterPro", "ShapeMasterPro"}}, | |||||
- {{"MindMeldModular", "ShapeMaster"}, {"MindMeld-ShapeMasterPro", "ShapeMasterPro"}}, | |||||
// {{"", ""}, {"", ""}}, | // {{"", ""}, {"", ""}}, | ||||
@@ -441,7 +179,6 @@ | |||||
}; | |||||
@@ -441,7 +184,6 @@ | |||||
} | } | ||||