Browse Source

Alternative approach to zero-latency cables; Update patches

Signed-off-by: falkTX <falktx@falktx.com>
tags/22.02
falkTX 3 years ago
parent
commit
0000456cf5
Signed by: falkTX <falktx@falktx.com> GPG Key ID: CDBAA37ABC74FBA0
12 changed files with 659 additions and 189 deletions
  1. +15
    -39
      include/engine/Port.hpp
  2. +31
    -0
      include/engine/TerminalModule.hpp
  3. +1
    -1
      plugins/Bidoo
  4. +36
    -16
      plugins/Cardinal/src/HostAudio.cpp
  5. +1
    -0
      plugins/Cardinal/src/plugin.hpp
  6. +141
    -33
      src/override/Engine.cpp
  7. +356
    -53
      src/override/diffs/Engine.cpp.diff
  8. +34
    -31
      src/override/diffs/MenuBar.cpp.diff
  9. +29
    -9
      src/override/diffs/Scene.cpp.diff
  10. +1
    -1
      src/override/diffs/common.cpp.diff
  11. +1
    -1
      src/override/diffs/context.cpp.diff
  12. +13
    -5
      src/override/diffs/plugin.cpp.diff

+ 15
- 39
include/engine/Port.hpp View File

@@ -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) {


+ 31
- 0
include/engine/TerminalModule.hpp View File

@@ -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
plugins/Bidoo

@@ -1 +1 @@
Subproject commit 988c2372a95d163b71d04b217080e612b767c539
Subproject commit e55fcd2e1d7c0fef69d4919baac6f791172c89ca

+ 36
- 16
plugins/Cardinal/src/HostAudio.cpp View File

@@ -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)


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

@@ -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


+ 141
- 33
src/override/Engine.cpp View File

@@ -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


+ 356
- 53
src/override/diffs/Engine.cpp.diff View File

@@ -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;
} }
} }


+ 34
- 31
src/override/diffs/MenuBar.cpp.diff View File

@@ -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() {


+ 29
- 9
src/override/diffs/Scene.cpp.diff View File

@@ -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
- 1
src/override/diffs/common.cpp.diff View File

@@ -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
- 1
src/override/diffs/context.cpp.diff View File

@@ -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 @@
+/* +/*


+ 13
- 5
src/override/diffs/plugin.cpp.diff View File

@@ -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 @@
} }


Loading…
Cancel
Save