diff --git a/source/backend/CarlaEngine.hpp b/source/backend/CarlaEngine.hpp
index 4a1256db8..c1f10679c 100644
--- a/source/backend/CarlaEngine.hpp
+++ b/source/backend/CarlaEngine.hpp
@@ -1150,7 +1150,7 @@ protected:
* Do not free returned data.
*/
virtual const char* const* getPatchbayConnections(const bool external) const;
- virtual void restorePatchbayConnection(const bool external, const char* const sourcePort, const char* const targetPort);
+ virtual void restorePatchbayConnection(const bool external, const char* const sourcePort, const char* const targetPort, const bool sendCallback);
#endif
// -------------------------------------------------------------------
diff --git a/source/backend/engine/CarlaEngine.cpp b/source/backend/engine/CarlaEngine.cpp
index 2d34bc177..20389253e 100644
--- a/source/backend/engine/CarlaEngine.cpp
+++ b/source/backend/engine/CarlaEngine.cpp
@@ -604,6 +604,19 @@ bool CarlaEngine::addPlugin(const BinaryType btype, const PluginType ptype,
canRun = false;
}
}
+ else if (pData->options.processMode == ENGINE_PROCESS_MODE_PATCHBAY)
+ {
+ /**/ if (plugin->getMidiInCount() > 1 || plugin->getMidiOutCount() > 1)
+ {
+ setLastError("Carla's patchbay mode cannot work with plugins that have multiple MIDI ports, sorry!");
+ canRun = false;
+ }
+ else if (plugin->getCVInCount() > 0 || plugin->getCVInCount() > 0)
+ {
+ setLastError("CV ports in patchbay mode is still TODO");
+ canRun = false;
+ }
+ }
if (! canRun)
{
@@ -630,6 +643,9 @@ bool CarlaEngine::addPlugin(const BinaryType btype, const PluginType ptype,
const ScopedThreadStopper sts(this);
+ if (pData->options.processMode == ENGINE_PROCESS_MODE_PATCHBAY)
+ pData->graph.replacePlugin(oldPlugin, plugin);
+
const bool wasActive = oldPlugin->getInternalParameterValue(PARAMETER_ACTIVE) >= 0.5f;
const float oldDryWet = oldPlugin->getInternalParameterValue(PARAMETER_DRYWET);
const float oldVolume = oldPlugin->getInternalParameterValue(PARAMETER_VOLUME);
@@ -655,6 +671,11 @@ bool CarlaEngine::addPlugin(const BinaryType btype, const PluginType ptype,
++pData->curPluginCount;
callback(ENGINE_CALLBACK_PLUGIN_ADDED, id, 0, 0, 0.0f, plugin->getName());
+
+#ifndef BUILD_BRIDGE
+ if (pData->options.processMode == ENGINE_PROCESS_MODE_PATCHBAY)
+ pData->graph.addPlugin(plugin);
+#endif
}
return true;
@@ -684,6 +705,9 @@ bool CarlaEngine::removePlugin(const uint id)
const ScopedThreadStopper sts(this);
#ifndef BUILD_BRIDGE
+ if (pData->options.processMode == ENGINE_PROCESS_MODE_PATCHBAY)
+ pData->graph.removePlugin(plugin);
+
const bool lockWait(isRunning() /*&& pData->options.processMode != ENGINE_PROCESS_MODE_MULTIPLE_CLIENTS*/);
const ScopedActionLock sal(this, kEnginePostActionRemovePlugin, id, 0, lockWait);
@@ -728,12 +752,17 @@ bool CarlaEngine::removeAllPlugins()
const uint curPluginCount(pData->curPluginCount);
-#if defined(HAVE_LIBLO) && ! defined(BUILD_BRIDGE)
+#ifndef BUILD_BRIDGE
+ if (pData->options.processMode == ENGINE_PROCESS_MODE_PATCHBAY)
+ pData->graph.removeAllPlugins();
+
+# ifdef HAVE_LIBLO
if (isOscControlRegistered())
{
for (uint i=0; i < curPluginCount; ++i)
oscSend_control_remove_plugin(curPluginCount-i-1);
}
+# endif
#endif
const bool lockWait(isRunning());
@@ -782,6 +811,9 @@ const char* CarlaEngine::renamePlugin(const uint id, const char* const newName)
plugin->setName(uniqueName);
+ if (pData->options.processMode == ENGINE_PROCESS_MODE_PATCHBAY)
+ pData->graph.renamePlugin(plugin, uniqueName);
+
delete[] uniqueName;
return plugin->getName();
}
@@ -867,6 +899,9 @@ bool CarlaEngine::switchPlugins(const uint idA, const uint idB) noexcept
const ScopedThreadStopper sts(this);
+ if (pData->options.processMode == ENGINE_PROCESS_MODE_PATCHBAY)
+ pData->graph.replacePlugin(pluginA, pluginB);
+
const bool lockWait(isRunning() /*&& pData->options.processMode != ENGINE_PROCESS_MODE_MULTIPLE_CLIENTS*/);
const ScopedActionLock sal(this, kEnginePostActionSwitchPlugins, idA, idB, lockWait);
@@ -1610,8 +1645,11 @@ void CarlaEngine::bufferSizeChanged(const uint32_t newBufferSize)
carla_debug("CarlaEngine::bufferSizeChanged(%i)", newBufferSize);
#ifndef BUILD_BRIDGE
- if (pData->options.processMode == ENGINE_PROCESS_MODE_CONTINUOUS_RACK)
+ if (pData->options.processMode == ENGINE_PROCESS_MODE_CONTINUOUS_RACK ||
+ pData->options.processMode == ENGINE_PROCESS_MODE_PATCHBAY)
+ {
pData->graph.setBufferSize(newBufferSize);
+ }
#endif
pData->time.updateAudioValues(newBufferSize, pData->sampleRate);
@@ -1631,6 +1669,14 @@ void CarlaEngine::sampleRateChanged(const double newSampleRate)
{
carla_debug("CarlaEngine::sampleRateChanged(%g)", newSampleRate);
+#ifndef BUILD_BRIDGE
+ if (pData->options.processMode == ENGINE_PROCESS_MODE_CONTINUOUS_RACK ||
+ pData->options.processMode == ENGINE_PROCESS_MODE_PATCHBAY)
+ {
+ pData->graph.setSampleRate(newSampleRate);
+ }
+#endif
+
pData->time.updateAudioValues(pData->bufferSize, newSampleRate);
for (uint i=0; i < pData->curPluginCount; ++i)
@@ -1649,8 +1695,11 @@ void CarlaEngine::offlineModeChanged(const bool isOfflineNow)
carla_debug("CarlaEngine::offlineModeChanged(%s)", bool2str(isOfflineNow));
#ifndef BUILD_BRIDGE
- if (pData->options.processMode == ENGINE_PROCESS_MODE_CONTINUOUS_RACK)
+ if (pData->options.processMode == ENGINE_PROCESS_MODE_CONTINUOUS_RACK ||
+ pData->options.processMode == ENGINE_PROCESS_MODE_PATCHBAY)
+ {
pData->graph.setOffline(isOfflineNow);
+ }
#endif
for (uint i=0; i < pData->curPluginCount; ++i)
@@ -1763,29 +1812,57 @@ void CarlaEngine::saveProjectInternal(water::MemoryOutputStream& outStream) cons
plugin->setCustomData(CUSTOM_DATA_TYPE_STRING, "__CarlaPingOnOff__", "true", false);
}
+ // save internal connections
+ if (pData->options.processMode == ENGINE_PROCESS_MODE_PATCHBAY)
+ {
+ if (const char* const* const patchbayConns = getPatchbayConnections(false))
+ {
+ MemoryOutputStream outPatchbay(2048);
+
+ outPatchbay << "\n \n";
+
+ for (int i=0; patchbayConns[i] != nullptr && patchbayConns[i+1] != nullptr; ++i, ++i )
+ {
+ const char* const connSource(patchbayConns[i]);
+ const char* const connTarget(patchbayConns[i+1]);
+
+ CARLA_SAFE_ASSERT_CONTINUE(connSource != nullptr && connSource[0] != '\0');
+ CARLA_SAFE_ASSERT_CONTINUE(connTarget != nullptr && connTarget[0] != '\0');
+
+ outPatchbay << " \n";
+ outPatchbay << " \n";
+ outPatchbay << " " << xmlSafeString(connTarget, true) << "\n";
+ outPatchbay << " \n";
+ }
+
+ outPatchbay << " \n";
+ outStream << outPatchbay;
+ }
+ }
+
// if we're running inside some session-manager (and using JACK), let them handle the connections
- bool saveConnections;
+ bool saveExternalConnections;
/**/ if (isPlugin)
- saveConnections = false;
+ saveExternalConnections = false;
else if (std::strcmp(getCurrentDriverName(), "JACK") != 0)
- saveConnections = true;
+ saveExternalConnections = true;
else if (std::getenv("CARLA_DONT_MANAGE_CONNECTIONS") != nullptr)
- saveConnections = false;
+ saveExternalConnections = false;
else if (std::getenv("LADISH_APP_NAME") != nullptr)
- saveConnections = false;
+ saveExternalConnections = false;
else if (std::getenv("NSM_URL") != nullptr)
- saveConnections = false;
+ saveExternalConnections = false;
else
- saveConnections = true;
+ saveExternalConnections = true;
- if (saveConnections)
+ if (saveExternalConnections)
{
- if (const char* const* const patchbayConns = getPatchbayConnections())
+ if (const char* const* const patchbayConns = getPatchbayConnections(true))
{
MemoryOutputStream outPatchbay(2048);
- outPatchbay << "\n \n";
+ outPatchbay << "\n \n";
for (int i=0; patchbayConns[i] != nullptr && patchbayConns[i+1] != nullptr; ++i, ++i )
{
@@ -1801,7 +1878,7 @@ void CarlaEngine::saveProjectInternal(water::MemoryOutputStream& outStream) cons
outPatchbay << " \n";
}
- outPatchbay << " \n";
+ outPatchbay << " \n";
outStream << outPatchbay;
}
}
@@ -2132,6 +2209,11 @@ bool CarlaEngine::loadProjectInternal(water::XmlDocument& xmlDoc)
plugin->setEnabled(true);
callback(ENGINE_CALLBACK_PLUGIN_ADDED, pluginId, 0, 0, 0.0f, plugin->getName());
+
+#ifndef BUILD_BRIDGE
+ if (pData->options.processMode == ENGINE_PROCESS_MODE_PATCHBAY)
+ pData->graph.addPlugin(plugin);
+#endif
}
else
{
@@ -2163,30 +2245,79 @@ bool CarlaEngine::loadProjectInternal(water::XmlDocument& xmlDoc)
if (pData->aboutToClose)
return true;
- // if we're running inside some session-manager (and using JACK), let them handle the connections
- bool loadConnections;
+ // handle connections (internal)
+ if (pData->options.processMode == ENGINE_PROCESS_MODE_PATCHBAY)
+ {
+ const bool isUsingExternal(pData->graph.isUsingExternal());
+
+ for (XmlElement* elem = xmlElement->getFirstChildElement(); elem != nullptr; elem = elem->getNextElement())
+ {
+ const String& tagName(elem->getTagName());
+
+ if (! tagName.equalsIgnoreCase("patchbay"))
+ continue;
+
+ CarlaString sourcePort, targetPort;
+
+ for (XmlElement* patchElem = elem->getFirstChildElement(); patchElem != nullptr; patchElem = patchElem->getNextElement())
+ {
+ const String& patchTag(patchElem->getTagName());
+
+ sourcePort.clear();
+ targetPort.clear();
+
+ if (! patchTag.equalsIgnoreCase("connection"))
+ continue;
+
+ for (XmlElement* connElem = patchElem->getFirstChildElement(); connElem != nullptr; connElem = connElem->getNextElement())
+ {
+ const String& tag(connElem->getTagName());
+ const String text(connElem->getAllSubText().trim());
+
+ /**/ if (tag.equalsIgnoreCase("source"))
+ sourcePort = xmlSafeString(text, false).toRawUTF8();
+ else if (tag.equalsIgnoreCase("target"))
+ targetPort = xmlSafeString(text, false).toRawUTF8();
+ }
+
+ if (sourcePort.isNotEmpty() && targetPort.isNotEmpty())
+ restorePatchbayConnection(false, sourcePort, targetPort, !isUsingExternal);
+ }
+ break;
+ }
+
+ callback(ENGINE_CALLBACK_IDLE, 0, 0, 0, 0.0f, nullptr);
+
+ if (pData->aboutToClose)
+ return true;
+ }
+
+ // if we're running inside some session-manager (and using JACK), let them handle the external connections
+ bool loadExternalConnections;
/**/ if (isPlugin)
- loadConnections = false;
+ loadExternalConnections = false;
else if (std::strcmp(getCurrentDriverName(), "JACK") != 0)
- loadConnections = true;
+ loadExternalConnections = true;
else if (std::getenv("CARLA_DONT_MANAGE_CONNECTIONS") != nullptr)
- loadConnections = false;
+ loadExternalConnections = false;
else if (std::getenv("LADISH_APP_NAME") != nullptr)
- loadConnections = false;
+ loadExternalConnections = false;
else if (std::getenv("NSM_URL") != nullptr)
- loadConnections = false;
+ loadExternalConnections = false;
else
- loadConnections = true;
+ loadExternalConnections = true;
// handle connections
- if (loadConnections)
+ if (loadExternalConnections)
{
+ const bool isUsingExternal(pData->graph.isUsingExternal());
+
for (XmlElement* elem = xmlElement->getFirstChildElement(); elem != nullptr; elem = elem->getNextElement())
{
const String& tagName(elem->getTagName());
- if (! tagName.equalsIgnoreCase("patchbay") && ! tagName.equalsIgnoreCase("externalpatchbay"))
+ if (! tagName.equalsIgnoreCase("externalpatchbay"))
continue;
CarlaString sourcePort, targetPort;
@@ -2213,7 +2344,7 @@ bool CarlaEngine::loadProjectInternal(water::XmlDocument& xmlDoc)
}
if (sourcePort.isNotEmpty() && targetPort.isNotEmpty())
- restorePatchbayConnection(sourcePort, targetPort);
+ restorePatchbayConnection(true, sourcePort, targetPort, isUsingExternal);
}
break;
}
diff --git a/source/backend/engine/CarlaEngineData.cpp b/source/backend/engine/CarlaEngineData.cpp
index bdcdfe226..8e6d54d6c 100644
--- a/source/backend/engine/CarlaEngineData.cpp
+++ b/source/backend/engine/CarlaEngineData.cpp
@@ -187,7 +187,7 @@ EngineOptions::EngineOptions() noexcept
: processMode(ENGINE_PROCESS_MODE_MULTIPLE_CLIENTS),
transportMode(ENGINE_TRANSPORT_MODE_JACK),
#else
- : processMode(ENGINE_PROCESS_MODE_CONTINUOUS_RACK),
+ : processMode(ENGINE_PROCESS_MODE_PATCHBAY),
transportMode(ENGINE_TRANSPORT_MODE_INTERNAL),
#endif
transportExtra(nullptr),
diff --git a/source/backend/engine/CarlaEngineGraph.cpp b/source/backend/engine/CarlaEngineGraph.cpp
index 28534be60..6d1d2c3da 100644
--- a/source/backend/engine/CarlaEngineGraph.cpp
+++ b/source/backend/engine/CarlaEngineGraph.cpp
@@ -20,8 +20,16 @@
#include "CarlaPlugin.hpp"
#include "CarlaMathUtils.hpp"
+#include "CarlaJuceUtils.hpp"
+
#include "CarlaMIDI.h"
+using water::jmax;
+using water::AudioProcessor;
+using water::MidiBuffer;
+using water::String;
+using water::StringArray;
+
CARLA_BACKEND_START_NAMESPACE
// -----------------------------------------------------------------------
@@ -971,147 +979,1207 @@ void RackGraph::processHelper(CarlaEngine::ProtectedData* const data, const floa
}
// -----------------------------------------------------------------------
-// InternalGraph
+// Patchbay Graph stuff
-EngineInternalGraph::EngineInternalGraph(CarlaEngine* const engine) noexcept
- : fIsReady(false),
- fRack(nullptr),
- kEngine(engine) {}
+static const uint32_t kAudioInputPortOffset = MAX_PATCHBAY_PLUGINS*1;
+static const uint32_t kAudioOutputPortOffset = MAX_PATCHBAY_PLUGINS*2;
+static const uint32_t kMidiInputPortOffset = MAX_PATCHBAY_PLUGINS*3;
+static const uint32_t kMidiOutputPortOffset = MAX_PATCHBAY_PLUGINS*3+1;
-EngineInternalGraph::~EngineInternalGraph() noexcept
+static const uint kMidiChannelIndex = static_cast(AudioProcessorGraph::midiChannelIndex);
+
+static inline
+bool adjustPatchbayPortIdForJuce(uint& portId)
{
- CARLA_SAFE_ASSERT(! fIsReady);
- CARLA_SAFE_ASSERT(fRack == nullptr);
+ CARLA_SAFE_ASSERT_RETURN(portId >= kAudioInputPortOffset, false);
+ CARLA_SAFE_ASSERT_RETURN(portId <= kMidiOutputPortOffset, false);
+
+ if (portId == kMidiInputPortOffset)
+ {
+ portId = kMidiChannelIndex;
+ return true;
+ }
+ if (portId == kMidiOutputPortOffset)
+ {
+ portId = kMidiChannelIndex;
+ return true;
+ }
+ if (portId >= kAudioOutputPortOffset)
+ {
+ portId -= kAudioOutputPortOffset;
+ return true;
+ }
+ if (portId >= kAudioInputPortOffset)
+ {
+ portId -= kAudioInputPortOffset;
+ return true;
+ }
+
+ return false;
}
-void EngineInternalGraph::create(const uint32_t inputs, const uint32_t outputs)
+static inline
+const String getProcessorFullPortName(AudioProcessor* const proc, const uint32_t portId)
{
- CARLA_SAFE_ASSERT_RETURN(fRack == nullptr,);
- fRack = new RackGraph(kEngine, inputs, outputs);
- fIsReady = true;
+ CARLA_SAFE_ASSERT_RETURN(proc != nullptr, String());
+ CARLA_SAFE_ASSERT_RETURN(portId >= kAudioInputPortOffset, String());
+ CARLA_SAFE_ASSERT_RETURN(portId <= kMidiOutputPortOffset, String());
+
+ String fullPortName(proc->getName());
+
+ if (portId == kMidiOutputPortOffset)
+ {
+ fullPortName += ":events-out";
+ }
+ else if (portId == kMidiInputPortOffset)
+ {
+ fullPortName += ":events-in";
+ }
+ else if (portId >= kAudioOutputPortOffset)
+ {
+ CARLA_SAFE_ASSERT_RETURN(proc->getTotalNumOutputChannels() > 0, String());
+ fullPortName += ":" + proc->getOutputChannelName(static_cast(portId-kAudioOutputPortOffset));
+ }
+ else if (portId >= kAudioInputPortOffset)
+ {
+ CARLA_SAFE_ASSERT_RETURN(proc->getTotalNumInputChannels() > 0, String());
+ fullPortName += ":" + proc->getInputChannelName(static_cast(portId-kAudioInputPortOffset));
+ }
+ else
+ {
+ return String();
+ }
+
+ return fullPortName;
}
-void EngineInternalGraph::destroy() noexcept
+static inline
+void addNodeToPatchbay(CarlaEngine* const engine, const uint32_t groupId, const int clientId, const AudioProcessor* const proc)
{
- if (! fIsReady)
+ CARLA_SAFE_ASSERT_RETURN(engine != nullptr,);
+ CARLA_SAFE_ASSERT_RETURN(proc != nullptr,);
+
+ const int icon((clientId >= 0) ? PATCHBAY_ICON_PLUGIN : PATCHBAY_ICON_HARDWARE);
+ engine->callback(ENGINE_CALLBACK_PATCHBAY_CLIENT_ADDED, groupId, icon, clientId, 0.0f, proc->getName().toRawUTF8());
+
+ for (int i=0, numInputs=proc->getTotalNumInputChannels(); icallback(ENGINE_CALLBACK_PATCHBAY_PORT_ADDED, groupId, static_cast(kAudioInputPortOffset)+i,
+ PATCHBAY_PORT_TYPE_AUDIO|PATCHBAY_PORT_IS_INPUT, 0.0f, proc->getInputChannelName(i).toRawUTF8());
}
- CARLA_SAFE_ASSERT_RETURN(fRack != nullptr,);
- delete fRack;
- fRack = nullptr;
+ for (int i=0, numOutputs=proc->getTotalNumOutputChannels(); icallback(ENGINE_CALLBACK_PATCHBAY_PORT_ADDED, groupId, static_cast(kAudioOutputPortOffset)+i,
+ PATCHBAY_PORT_TYPE_AUDIO, 0.0f, proc->getOutputChannelName(i).toRawUTF8());
+ }
- fIsReady = false;
+ if (proc->acceptsMidi())
+ {
+ engine->callback(ENGINE_CALLBACK_PATCHBAY_PORT_ADDED, groupId, static_cast(kMidiInputPortOffset),
+ PATCHBAY_PORT_TYPE_MIDI|PATCHBAY_PORT_IS_INPUT, 0.0f, "events-in");
+ }
+
+ if (proc->producesMidi())
+ {
+ engine->callback(ENGINE_CALLBACK_PATCHBAY_PORT_ADDED, groupId, static_cast(kMidiOutputPortOffset),
+ PATCHBAY_PORT_TYPE_MIDI, 0.0f, "events-out");
+ }
}
-void EngineInternalGraph::setBufferSize(const uint32_t bufferSize)
+static inline
+void removeNodeFromPatchbay(CarlaEngine* const engine, const uint32_t groupId, const AudioProcessor* const proc)
{
- ScopedValueSetter svs(fIsReady, false, true);
+ CARLA_SAFE_ASSERT_RETURN(engine != nullptr,);
+ CARLA_SAFE_ASSERT_RETURN(proc != nullptr,);
- CARLA_SAFE_ASSERT_RETURN(fRack != nullptr,);
- fRack->setBufferSize(bufferSize);
+ for (int i=0, numInputs=proc->getTotalNumInputChannels(); icallback(ENGINE_CALLBACK_PATCHBAY_PORT_REMOVED, groupId, static_cast(kAudioInputPortOffset)+i,
+ 0, 0.0f, nullptr);
+ }
+
+ for (int i=0, numOutputs=proc->getTotalNumOutputChannels(); icallback(ENGINE_CALLBACK_PATCHBAY_PORT_REMOVED, groupId, static_cast(kAudioOutputPortOffset)+i,
+ 0, 0.0f, nullptr);
+ }
+
+ if (proc->acceptsMidi())
+ {
+ engine->callback(ENGINE_CALLBACK_PATCHBAY_PORT_REMOVED, groupId, static_cast(kMidiInputPortOffset),
+ 0, 0.0f, nullptr);
+ }
+
+ if (proc->producesMidi())
+ {
+ engine->callback(ENGINE_CALLBACK_PATCHBAY_PORT_REMOVED, groupId, static_cast(kMidiOutputPortOffset),
+ 0, 0.0f, nullptr);
+ }
+
+ engine->callback(ENGINE_CALLBACK_PATCHBAY_CLIENT_REMOVED, groupId, 0, 0, 0.0f, nullptr);
}
-void EngineInternalGraph::setOffline(const bool offline)
+// -----------------------------------------------------------------------
+
+class CarlaPluginInstance : public AudioProcessor
{
- ScopedValueSetter svs(fIsReady, false, true);
+public:
+ CarlaPluginInstance(CarlaEngine* const engine, CarlaPlugin* const plugin)
+ : kEngine(engine),
+ fPlugin(plugin)
+ {
+ setPlayConfigDetails(static_cast(fPlugin->getAudioInCount()),
+ static_cast(fPlugin->getAudioOutCount()),
+ getSampleRate(), getBlockSize());
+ }
- CARLA_SAFE_ASSERT_RETURN(fRack != nullptr,);
- fRack->setOffline(offline);
-}
+ ~CarlaPluginInstance() override
+ {
+ }
-bool EngineInternalGraph::isReady() const noexcept
+ void invalidatePlugin() noexcept
+ {
+ fPlugin = nullptr;
+ }
+
+ // -------------------------------------------------------------------
+
+ const String getName() const override
+ {
+ return fPlugin->getName();
+ }
+
+ void processBlock(AudioSampleBuffer& audio, MidiBuffer& midi) override
+ {
+ if (fPlugin == nullptr || ! fPlugin->isEnabled())
+ {
+ audio.clear();
+ midi.clear();
+ return;
+ }
+
+ if (! fPlugin->tryLock(kEngine->isOffline()))
+ {
+ audio.clear();
+ midi.clear();
+ return;
+ }
+
+ fPlugin->initBuffers();
+
+ if (CarlaEngineEventPort* const port = fPlugin->getDefaultEventInPort())
+ {
+ EngineEvent* const engineEvents(port->fBuffer);
+ CARLA_SAFE_ASSERT_RETURN(engineEvents != nullptr,);
+
+ carla_zeroStructs(engineEvents, kMaxEngineEventInternalCount);
+ fillEngineEventsFromWaterMidiBuffer(engineEvents, midi);
+ }
+
+ midi.clear();
+
+ // TODO - CV support
+
+ const int numSamples(audio.getNumSamples());
+
+ if (const int numChan = audio.getNumChannels())
+ {
+ if (fPlugin->getAudioInCount() == 0)
+ audio.clear();
+
+ float* audioBuffers[numChan];
+
+ for (int i=0; i range;
+
+ for (int i=static_cast(jmin(fPlugin->getAudioInCount(), 2U)); --i>=0;)
+ {
+ range = FloatVectorOperations::findMinAndMax(audioBuffers[i], numSamples);
+ inPeaks[i] = carla_maxLimited(std::abs(range.getStart()), std::abs(range.getEnd()), 1.0f);
+ }
+#endif
+
+ fPlugin->process(const_cast(audioBuffers), audioBuffers, nullptr, nullptr, static_cast(numSamples));
+
+#if 0
+ for (int i=static_cast(jmin(fPlugin->getAudioOutCount(), 2U)); --i>=0;)
+ {
+ range = FloatVectorOperations::findMinAndMax(audioBuffers[i], numSamples);
+ outPeaks[i] = carla_maxLimited(std::abs(range.getStart()), std::abs(range.getEnd()), 1.0f);
+ }
+
+ kEngine->setPluginPeaks(fPlugin->getId(), inPeaks, outPeaks);
+#endif
+ }
+ else
+ {
+ fPlugin->process(nullptr, nullptr, nullptr, nullptr, static_cast(numSamples));
+ }
+
+ midi.clear();
+
+ if (CarlaEngineEventPort* const port = fPlugin->getDefaultEventOutPort())
+ {
+ /*const*/ EngineEvent* const engineEvents(port->fBuffer);
+ CARLA_SAFE_ASSERT_RETURN(engineEvents != nullptr,);
+
+ fillWaterMidiBufferFromEngineEvents(midi, engineEvents);
+ carla_zeroStructs(engineEvents, kMaxEngineEventInternalCount);
+ }
+
+ fPlugin->unlock();
+ }
+
+ const String getInputChannelName(int i) const override
+ {
+ CARLA_SAFE_ASSERT_RETURN(i >= 0, String());
+ CarlaEngineClient* const client(fPlugin->getEngineClient());
+ return client->getAudioPortName(true, static_cast(i));
+ }
+
+ const String getOutputChannelName(int i) const override
+ {
+ CARLA_SAFE_ASSERT_RETURN(i >= 0, String());
+ CarlaEngineClient* const client(fPlugin->getEngineClient());
+ return client->getAudioPortName(false, static_cast(i));
+ }
+
+ void prepareToPlay(double, int) override {}
+ void releaseResources() override {}
+
+ bool acceptsMidi() const override { return fPlugin->getDefaultEventInPort() != nullptr; }
+ bool producesMidi() const override { return fPlugin->getDefaultEventOutPort() != nullptr; }
+
+private:
+ CarlaEngine* const kEngine;
+ CarlaPlugin* fPlugin;
+
+ CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CarlaPluginInstance)
+};
+
+// -----------------------------------------------------------------------
+// Patchbay Graph
+
+class NamedAudioGraphIOProcessor : public AudioProcessorGraph::AudioGraphIOProcessor
{
- return fIsReady;
+public:
+ NamedAudioGraphIOProcessor(const IODeviceType iotype)
+ : AudioProcessorGraph::AudioGraphIOProcessor(iotype),
+ inputNames(),
+ outputNames() {}
+
+ const String getInputChannelName (int index) const override
+ {
+ if (index < inputNames.size())
+ return inputNames[index];
+ return String("Playback ") + String(index+1);
+ }
+
+ const String getOutputChannelName (int index) const override
+ {
+ if (index < outputNames.size())
+ return outputNames[index];
+ return String("Capture ") + String(index+1);
+ }
+
+ void setNames(const bool setInputNames, const StringArray& names)
+ {
+ if (setInputNames)
+ inputNames = names;
+ else
+ outputNames = names;
+ }
+
+private:
+ StringArray inputNames;
+ StringArray outputNames;
+};
+
+PatchbayGraph::PatchbayGraph(CarlaEngine* const engine, const uint32_t ins, const uint32_t outs)
+ : connections(),
+ graph(),
+ audioBuffer(),
+ midiBuffer(),
+ inputs(carla_fixedValue(0U, 32U, ins)),
+ outputs(carla_fixedValue(0U, 32U, outs)),
+ retCon(),
+ usingExternal(false),
+ extGraph(engine),
+ kEngine(engine)
+{
+ const int bufferSize(static_cast(engine->getBufferSize()));
+ const double sampleRate(engine->getSampleRate());
+
+ graph.setPlayConfigDetails(static_cast(inputs), static_cast(outputs), sampleRate, bufferSize);
+ graph.prepareToPlay(sampleRate, bufferSize);
+
+ audioBuffer.setSize(static_cast(jmax(inputs, outputs)), bufferSize);
+
+ midiBuffer.ensureSize(kMaxEngineEventInternalCount*2);
+ midiBuffer.clear();
+
+ StringArray channelNames;
+
+ switch (inputs)
+ {
+ case 2:
+ channelNames.add("Left");
+ channelNames.add("Right");
+ break;
+ case 3:
+ channelNames.add("Left");
+ channelNames.add("Right");
+ channelNames.add("Sidechain");
+ break;
+ }
+
+ {
+ NamedAudioGraphIOProcessor* const proc(
+ new NamedAudioGraphIOProcessor(NamedAudioGraphIOProcessor::audioInputNode));
+ proc->setNames(false, channelNames);
+
+ AudioProcessorGraph::Node* const node(graph.addNode(proc));
+ node->properties.set("isPlugin", false);
+ node->properties.set("isOutput", false);
+ node->properties.set("isAudio", true);
+ node->properties.set("isCV", false);
+ node->properties.set("isMIDI", false);
+ node->properties.set("isOSC", false);
+ }
+
+ {
+ NamedAudioGraphIOProcessor* const proc(
+ new NamedAudioGraphIOProcessor(NamedAudioGraphIOProcessor::audioOutputNode));
+ proc->setNames(true, channelNames);
+
+ AudioProcessorGraph::Node* const node(graph.addNode(proc));
+ node->properties.set("isPlugin", false);
+ node->properties.set("isOutput", false);
+ node->properties.set("isAudio", true);
+ node->properties.set("isCV", false);
+ node->properties.set("isMIDI", false);
+ node->properties.set("isOSC", false);
+ }
+
+ {
+ NamedAudioGraphIOProcessor* const proc(
+ new NamedAudioGraphIOProcessor(NamedAudioGraphIOProcessor::midiInputNode));
+ AudioProcessorGraph::Node* const node(graph.addNode(proc));
+ node->properties.set("isPlugin", false);
+ node->properties.set("isOutput", false);
+ node->properties.set("isAudio", false);
+ node->properties.set("isCV", false);
+ node->properties.set("isMIDI", true);
+ node->properties.set("isOSC", false);
+ }
+
+ {
+ NamedAudioGraphIOProcessor* const proc(
+ new NamedAudioGraphIOProcessor(NamedAudioGraphIOProcessor::midiOutputNode));
+ AudioProcessorGraph::Node* const node(graph.addNode(proc));
+ node->properties.set("isPlugin", false);
+ node->properties.set("isOutput", true);
+ node->properties.set("isAudio", false);
+ node->properties.set("isCV", false);
+ node->properties.set("isMIDI", true);
+ node->properties.set("isOSC", false);
+ }
}
-RackGraph* EngineInternalGraph::getGraph() const noexcept
+PatchbayGraph::~PatchbayGraph()
{
- return fRack;
+ connections.clear();
+ extGraph.clear();
+
+ graph.releaseResources();
+ graph.clear();
+ audioBuffer.clear();
}
-void EngineInternalGraph::process(CarlaEngine::ProtectedData* const data, const float* const* const inBuf, float* const* const outBuf, const uint32_t frames)
+void PatchbayGraph::setBufferSize(const uint32_t bufferSize)
{
- CARLA_SAFE_ASSERT_RETURN(fRack != nullptr,);
- fRack->processHelper(data, inBuf, outBuf, frames);
+ const int bufferSizei(static_cast(bufferSize));
+
+ graph.releaseResources();
+ graph.prepareToPlay(kEngine->getSampleRate(), bufferSizei);
+ audioBuffer.setSize(audioBuffer.getNumChannels(), bufferSizei);
}
-void EngineInternalGraph::processRack(CarlaEngine::ProtectedData* const data, const float* inBuf[2], float* outBuf[2], const uint32_t frames)
+void PatchbayGraph::setSampleRate(const double sampleRate)
{
- CARLA_SAFE_ASSERT_RETURN(fRack != nullptr,);
- fRack->process(data, inBuf, outBuf, frames);
+ graph.releaseResources();
+ graph.prepareToPlay(sampleRate, static_cast(kEngine->getBufferSize()));
}
-// -----------------------------------------------------------------------
-// CarlaEngine Patchbay stuff
+void PatchbayGraph::setOffline(const bool offline)
+{
+ graph.setNonRealtime(offline);
+}
-bool CarlaEngine::patchbayConnect(const uint groupA, const uint portA, const uint groupB, const uint portB)
+void PatchbayGraph::addPlugin(CarlaPlugin* const plugin)
{
- CARLA_SAFE_ASSERT_RETURN(pData->options.processMode == ENGINE_PROCESS_MODE_CONTINUOUS_RACK, false);
- CARLA_SAFE_ASSERT_RETURN(pData->graph.isReady(), false);
- carla_debug("CarlaEngine::patchbayConnect(%u, %u, %u, %u)", groupA, portA, groupB, portB);
+ CARLA_SAFE_ASSERT_RETURN(plugin != nullptr,);
+ carla_debug("PatchbayGraph::addPlugin(%p)", plugin);
- RackGraph* const graph = pData->graph.getGraph();
- CARLA_SAFE_ASSERT_RETURN(graph != nullptr, false);
+ CarlaPluginInstance* const instance(new CarlaPluginInstance(kEngine, plugin));
+ AudioProcessorGraph::Node* const node(graph.addNode(instance));
+ CARLA_SAFE_ASSERT_RETURN(node != nullptr,);
- return graph->connect(groupA, portA, groupB, portB);
+ plugin->setPatchbayNodeId(node->nodeId);
+
+ node->properties.set("isPlugin", true);
+ node->properties.set("pluginId", static_cast(plugin->getId()));
+
+ if (! usingExternal)
+ addNodeToPatchbay(plugin->getEngine(), node->nodeId, static_cast(plugin->getId()), instance);
}
-bool CarlaEngine::patchbayDisconnect(const uint connectionId)
+void PatchbayGraph::replacePlugin(CarlaPlugin* const oldPlugin, CarlaPlugin* const newPlugin)
{
- CARLA_SAFE_ASSERT_RETURN(pData->options.processMode == ENGINE_PROCESS_MODE_CONTINUOUS_RACK, false);
- CARLA_SAFE_ASSERT_RETURN(pData->graph.isReady(), false);
- carla_debug("CarlaEngine::patchbayDisconnect(%u)", connectionId);
+ CARLA_SAFE_ASSERT_RETURN(oldPlugin != nullptr,);
+ CARLA_SAFE_ASSERT_RETURN(newPlugin != nullptr,);
+ CARLA_SAFE_ASSERT_RETURN(oldPlugin != newPlugin,);
+ CARLA_SAFE_ASSERT_RETURN(oldPlugin->getId() == newPlugin->getId(),);
- RackGraph* const graph = pData->graph.getGraph();
- CARLA_SAFE_ASSERT_RETURN(graph != nullptr, false);
+ AudioProcessorGraph::Node* const oldNode(graph.getNodeForId(oldPlugin->getPatchbayNodeId()));
+ CARLA_SAFE_ASSERT_RETURN(oldNode != nullptr,);
+
+ if (! usingExternal)
+ {
+ disconnectInternalGroup(oldNode->nodeId);
+ removeNodeFromPatchbay(kEngine, oldNode->nodeId, oldNode->getProcessor());
+ }
+
+ ((CarlaPluginInstance*)oldNode->getProcessor())->invalidatePlugin();
+
+ graph.removeNode(oldNode->nodeId);
- return graph->disconnect(connectionId);
+ CarlaPluginInstance* const instance(new CarlaPluginInstance(kEngine, newPlugin));
+ AudioProcessorGraph::Node* const node(graph.addNode(instance));
+ CARLA_SAFE_ASSERT_RETURN(node != nullptr,);
+
+ newPlugin->setPatchbayNodeId(node->nodeId);
+
+ node->properties.set("isPlugin", true);
+ node->properties.set("pluginId", static_cast(newPlugin->getId()));
+
+ if (! usingExternal)
+ addNodeToPatchbay(newPlugin->getEngine(), node->nodeId, static_cast(newPlugin->getId()), instance);
}
-bool CarlaEngine::patchbayRefresh()
+void PatchbayGraph::renamePlugin(CarlaPlugin* const plugin, const char* const newName)
{
- // This is implemented in engine subclasses
- setLastError("Unsupported operation");
- return false;
-}
+ CARLA_SAFE_ASSERT_RETURN(plugin != nullptr,);
+ carla_debug("PatchbayGraph::renamePlugin(%p)", plugin, newName);
-// -----------------------------------------------------------------------
+ AudioProcessorGraph::Node* const node(graph.getNodeForId(plugin->getPatchbayNodeId()));
+ CARLA_SAFE_ASSERT_RETURN(node != nullptr,);
+
+ if (! usingExternal)
+ kEngine->callback(ENGINE_CALLBACK_PATCHBAY_CLIENT_RENAMED, node->nodeId, 0, 0, 0.0f, newName);
+}
-const char* const* CarlaEngine::getPatchbayConnections() const
+void PatchbayGraph::removePlugin(CarlaPlugin* const plugin)
{
- CARLA_SAFE_ASSERT_RETURN(pData->options.processMode == ENGINE_PROCESS_MODE_CONTINUOUS_RACK, nullptr);
- CARLA_SAFE_ASSERT_RETURN(pData->graph.isReady(), nullptr);
- carla_debug("CarlaEngine::getPatchbayConnections(%s)", bool2str(external));
+ CARLA_SAFE_ASSERT_RETURN(plugin != nullptr,);
+ carla_debug("PatchbayGraph::removePlugin(%p)", plugin);
+
+ AudioProcessorGraph::Node* const node(graph.getNodeForId(plugin->getPatchbayNodeId()));
+ CARLA_SAFE_ASSERT_RETURN(node != nullptr,);
+
+ if (! usingExternal)
+ {
+ disconnectInternalGroup(node->nodeId);
+ removeNodeFromPatchbay(kEngine, node->nodeId, node->getProcessor());
+ }
+
+ ((CarlaPluginInstance*)node->getProcessor())->invalidatePlugin();
- RackGraph* const graph = pData->graph.getGraph();
- CARLA_SAFE_ASSERT_RETURN(graph != nullptr, nullptr);
+ // Fix plugin Ids properties
+ for (uint i=plugin->getId()+1, count=kEngine->getCurrentPluginCount(); igetPlugin(i));
+ CARLA_SAFE_ASSERT_BREAK(plugin2 != nullptr);
+
+ if (AudioProcessorGraph::Node* const node2 = graph.getNodeForId(plugin2->getPatchbayNodeId()))
+ {
+ CARLA_SAFE_ASSERT_CONTINUE(node2->properties.getWithDefault("pluginId", -1) != water::var(-1));
+ node2->properties.set("pluginId", static_cast(i-1));
+ }
+ }
- return graph->getConnections();
+ CARLA_SAFE_ASSERT_RETURN(graph.removeNode(node->nodeId),);
}
-void CarlaEngine::restorePatchbayConnection(const char* const sourcePort, const char* const targetPort)
+void PatchbayGraph::removeAllPlugins()
{
- CARLA_SAFE_ASSERT_RETURN(pData->options.processMode == ENGINE_PROCESS_MODE_CONTINUOUS_RACK,);
- CARLA_SAFE_ASSERT_RETURN(pData->graph.isReady(),);
- CARLA_SAFE_ASSERT_RETURN(sourcePort != nullptr && sourcePort[0] != '\0',);
- CARLA_SAFE_ASSERT_RETURN(targetPort != nullptr && targetPort[0] != '\0',);
- carla_debug("CarlaEngine::restorePatchbayConnection(\"%s\", \"%s\")", sourcePort, targetPort);
+ carla_debug("PatchbayGraph::removeAllPlugins()");
- uint groupA, portA;
- uint groupB, portB;
+ for (uint i=0, count=kEngine->getCurrentPluginCount(); igetPlugin(i));
+ CARLA_SAFE_ASSERT_CONTINUE(plugin != nullptr);
- RackGraph* const graph = pData->graph.getGraph();
- CARLA_SAFE_ASSERT_RETURN(graph != nullptr,);
+ AudioProcessorGraph::Node* const node(graph.getNodeForId(plugin->getPatchbayNodeId()));
+ CARLA_SAFE_ASSERT_CONTINUE(node != nullptr);
- if (! graph->getGroupAndPortIdFromFullName(sourcePort, groupA, portA))
- return;
- if (! graph->getGroupAndPortIdFromFullName(targetPort, groupB, portB))
- return;
+ if (! usingExternal)
+ {
+ disconnectInternalGroup(node->nodeId);
+ removeNodeFromPatchbay(kEngine, node->nodeId, node->getProcessor());
+ }
+
+ ((CarlaPluginInstance*)node->getProcessor())->invalidatePlugin();
- graph->connect(groupA, portA, groupB, portB);
+ graph.removeNode(node->nodeId);
+ }
+}
+
+bool PatchbayGraph::connect(const bool external, const uint groupA, const uint portA, const uint groupB, const uint portB, const bool sendCallback)
+{
+ if (external)
+ return extGraph.connect(groupA, portA, groupB, portB, sendCallback);
+
+ uint adjustedPortA = portA;
+ uint adjustedPortB = portB;
+
+ if (! adjustPatchbayPortIdForJuce(adjustedPortA))
+ return false;
+ if (! adjustPatchbayPortIdForJuce(adjustedPortB))
+ return false;
+
+ if (! graph.addConnection(groupA, static_cast(adjustedPortA), groupB, static_cast(adjustedPortB)))
+ {
+ kEngine->setLastError("Failed from juce");
+ return false;
+ }
+
+ ConnectionToId connectionToId;
+ connectionToId.setData(++connections.lastId, groupA, portA, groupB, portB);
+
+ char strBuf[STR_MAX+1];
+ strBuf[STR_MAX] = '\0';
+ std::snprintf(strBuf, STR_MAX, "%u:%u:%u:%u", groupA, portA, groupB, portB);
+
+ if (sendCallback)
+ kEngine->callback(ENGINE_CALLBACK_PATCHBAY_CONNECTION_ADDED, connectionToId.id, 0, 0, 0.0f, strBuf);
+
+ connections.list.append(connectionToId);
+ return true;
+}
+
+bool PatchbayGraph::disconnect(const uint connectionId)
+{
+ if (usingExternal)
+ return extGraph.disconnect(connectionId);
+
+ for (LinkedList::Itenerator it=connections.list.begin2(); it.valid(); it.next())
+ {
+ static const ConnectionToId fallback = { 0, 0, 0, 0, 0 };
+
+ const ConnectionToId& connectionToId(it.getValue(fallback));
+ CARLA_SAFE_ASSERT_CONTINUE(connectionToId.id > 0);
+
+ if (connectionToId.id != connectionId)
+ continue;
+
+ uint adjustedPortA = connectionToId.portA;
+ uint adjustedPortB = connectionToId.portB;
+
+ if (! adjustPatchbayPortIdForJuce(adjustedPortA))
+ return false;
+ if (! adjustPatchbayPortIdForJuce(adjustedPortB))
+ return false;
+
+ if (! graph.removeConnection(connectionToId.groupA, static_cast(adjustedPortA),
+ connectionToId.groupB, static_cast(adjustedPortB)))
+ return false;
+
+ kEngine->callback(ENGINE_CALLBACK_PATCHBAY_CONNECTION_REMOVED, connectionToId.id, 0, 0, 0.0f, nullptr);
+
+ connections.list.remove(it);
+ return true;
+ }
+
+ kEngine->setLastError("Failed to find connection");
+ return false;
+}
+
+void PatchbayGraph::disconnectInternalGroup(const uint groupId) noexcept
+{
+ CARLA_SAFE_ASSERT(! usingExternal);
+
+ for (LinkedList::Itenerator it=connections.list.begin2(); it.valid(); it.next())
+ {
+ static const ConnectionToId fallback = { 0, 0, 0, 0, 0 };
+
+ const ConnectionToId& connectionToId(it.getValue(fallback));
+ CARLA_SAFE_ASSERT_CONTINUE(connectionToId.id > 0);
+
+ if (connectionToId.groupA != groupId && connectionToId.groupB != groupId)
+ continue;
+
+ /*
+ uint adjustedPortA = connectionToId.portA;
+ uint adjustedPortB = connectionToId.portB;
+
+ if (! adjustPatchbayPortIdForJuce(adjustedPortA))
+ return false;
+ if (! adjustPatchbayPortIdForJuce(adjustedPortB))
+ return false;
+
+ graph.removeConnection(connectionToId.groupA, static_cast(adjustedPortA),
+ connectionToId.groupB, static_cast(adjustedPortB));
+ */
+
+ if (! usingExternal)
+ kEngine->callback(ENGINE_CALLBACK_PATCHBAY_CONNECTION_REMOVED, connectionToId.id, 0, 0, 0.0f, nullptr);
+
+ connections.list.remove(it);
+ }
+}
+
+void PatchbayGraph::refresh(const char* const deviceName)
+{
+ if (usingExternal)
+ return extGraph.refresh(deviceName);
+
+ CARLA_SAFE_ASSERT_RETURN(deviceName != nullptr,);
+
+ connections.clear();
+ graph.removeIllegalConnections();
+
+ for (int i=0, count=graph.getNumNodes(); igetProcessor());
+ CARLA_SAFE_ASSERT_CONTINUE(proc != nullptr);
+
+ int clientId = -1;
+
+ // plugin node
+ if (node->properties.getWithDefault("isPlugin", false) == water::var(true))
+ clientId = node->properties.getWithDefault("pluginId", -1);
+
+ addNodeToPatchbay(kEngine, node->nodeId, clientId, proc);
+ }
+
+ char strBuf[STR_MAX+1];
+ strBuf[STR_MAX] = '\0';
+
+ for (int i=0, count=graph.getNumConnections(); isourceChannelIndex >= 0);
+ CARLA_SAFE_ASSERT_CONTINUE(conn->destChannelIndex >= 0);
+
+ const uint groupA = conn->sourceNodeId;
+ const uint groupB = conn->destNodeId;
+
+ uint portA = static_cast(conn->sourceChannelIndex);
+ uint portB = static_cast(conn->destChannelIndex);
+
+ if (portA == kMidiChannelIndex)
+ portA = kMidiOutputPortOffset;
+ else
+ portA += kAudioOutputPortOffset;
+
+ if (portB == kMidiChannelIndex)
+ portB = kMidiInputPortOffset;
+ else
+ portB += kAudioInputPortOffset;
+
+ ConnectionToId connectionToId;
+ connectionToId.setData(++connections.lastId, groupA, portA, groupB, portB);
+
+ std::snprintf(strBuf, STR_MAX, "%i:%i:%i:%i", groupA, portA, groupB, portB);
+
+ kEngine->callback(ENGINE_CALLBACK_PATCHBAY_CONNECTION_ADDED, connectionToId.id, 0, 0, 0.0f, strBuf);
+
+ connections.list.append(connectionToId);
+ }
+}
+
+const char* const* PatchbayGraph::getConnections(const bool external) const
+{
+ if (external)
+ return extGraph.getConnections();
+
+ if (connections.list.count() == 0)
+ return nullptr;
+
+ CarlaStringList connList;
+
+ for (LinkedList::Itenerator it=connections.list.begin2(); it.valid(); it.next())
+ {
+ static const ConnectionToId fallback = { 0, 0, 0, 0, 0 };
+
+ const ConnectionToId& connectionToId(it.getValue(fallback));
+ CARLA_SAFE_ASSERT_CONTINUE(connectionToId.id > 0);
+
+ AudioProcessorGraph::Node* const nodeA(graph.getNodeForId(connectionToId.groupA));
+ CARLA_SAFE_ASSERT_CONTINUE(nodeA != nullptr);
+
+ AudioProcessorGraph::Node* const nodeB(graph.getNodeForId(connectionToId.groupB));
+ CARLA_SAFE_ASSERT_CONTINUE(nodeB != nullptr);
+
+ AudioProcessor* const procA(nodeA->getProcessor());
+ CARLA_SAFE_ASSERT_CONTINUE(procA != nullptr);
+
+ AudioProcessor* const procB(nodeB->getProcessor());
+ CARLA_SAFE_ASSERT_CONTINUE(procB != nullptr);
+
+ String fullPortNameA(getProcessorFullPortName(procA, connectionToId.portA));
+ CARLA_SAFE_ASSERT_CONTINUE(fullPortNameA.isNotEmpty());
+
+ String fullPortNameB(getProcessorFullPortName(procB, connectionToId.portB));
+ CARLA_SAFE_ASSERT_CONTINUE(fullPortNameB.isNotEmpty());
+
+ connList.append(fullPortNameA.toRawUTF8());
+ connList.append(fullPortNameB.toRawUTF8());
+ }
+
+ if (connList.count() == 0)
+ return nullptr;
+
+ retCon = connList.toCharStringListPtr();
+
+ return retCon;
+}
+
+bool PatchbayGraph::getGroupAndPortIdFromFullName(const bool external, const char* const fullPortName, uint& groupId, uint& portId) const
+{
+ if (external)
+ return extGraph.getGroupAndPortIdFromFullName(fullPortName, groupId, portId);
+
+ String groupName(String(fullPortName).upToFirstOccurrenceOf(":", false, false));
+ String portName(String(fullPortName).fromFirstOccurrenceOf(":", false, false));
+
+ for (int i=0, count=graph.getNumNodes(); igetProcessor());
+ CARLA_SAFE_ASSERT_CONTINUE(proc != nullptr);
+
+ if (proc->getName() != groupName)
+ continue;
+
+ groupId = node->nodeId;
+
+ if (portName == "events-in")
+ {
+ portId = kMidiInputPortOffset;
+ return true;
+ }
+
+ if (portName == "events-out")
+ {
+ portId = kMidiOutputPortOffset;
+ return true;
+ }
+
+ for (int j=0, numInputs=proc->getTotalNumInputChannels(); jgetInputChannelName(j) != portName)
+ continue;
+
+ portId = kAudioInputPortOffset+static_cast(j);
+ return true;
+ }
+
+ for (int j=0, numOutputs=proc->getTotalNumOutputChannels(); jgetOutputChannelName(j) != portName)
+ continue;
+
+ portId = kAudioOutputPortOffset+static_cast(j);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void PatchbayGraph::process(CarlaEngine::ProtectedData* const data, const float* const* const inBuf, float* const* const outBuf, const int frames)
+{
+ CARLA_SAFE_ASSERT_RETURN(data != nullptr,);
+ CARLA_SAFE_ASSERT_RETURN(data->events.in != nullptr,);
+ CARLA_SAFE_ASSERT_RETURN(data->events.out != nullptr,);
+ CARLA_SAFE_ASSERT_RETURN(frames > 0,);
+
+ // put events in juce buffer
+ {
+ midiBuffer.clear();
+ fillWaterMidiBufferFromEngineEvents(midiBuffer, data->events.in);
+ }
+
+ // put carla audio in juce buffer
+ {
+ int i=0;
+
+ for (; i < static_cast(inputs); ++i)
+ carla_copyFloats(audioBuffer.getWritePointer(i), inBuf[i], frames);
+
+ // clear remaining channels
+ for (const int count=audioBuffer.getNumChannels(); i(outputs); ++i)
+ carla_copyFloats(outBuf[i], audioBuffer.getReadPointer(i), frames);
+ }
+
+ // put juce events in carla buffer
+ {
+ carla_zeroStructs(data->events.out, kMaxEngineEventInternalCount);
+ fillEngineEventsFromWaterMidiBuffer(data->events.out, midiBuffer);
+ midiBuffer.clear();
+ }
+}
+
+// -----------------------------------------------------------------------
+// InternalGraph
+
+EngineInternalGraph::EngineInternalGraph(CarlaEngine* const engine) noexcept
+ : fIsReady(false),
+ fRack(nullptr),
+ kEngine(engine) {}
+
+EngineInternalGraph::~EngineInternalGraph() noexcept
+{
+ CARLA_SAFE_ASSERT(! fIsReady);
+ CARLA_SAFE_ASSERT(fRack == nullptr);
+}
+
+void EngineInternalGraph::create(const uint32_t inputs, const uint32_t outputs)
+{
+ fIsRack = (kEngine->getOptions().processMode == ENGINE_PROCESS_MODE_CONTINUOUS_RACK);
+
+ if (fIsRack)
+ {
+ CARLA_SAFE_ASSERT_RETURN(fRack == nullptr,);
+ fRack = new RackGraph(kEngine, inputs, outputs);
+ }
+ else
+ {
+ CARLA_SAFE_ASSERT_RETURN(fPatchbay == nullptr,);
+ fPatchbay = new PatchbayGraph(kEngine, inputs, outputs);
+ }
+
+ fIsReady = true;
+}
+
+void EngineInternalGraph::destroy() noexcept
+{
+ if (! fIsReady)
+ {
+ CARLA_SAFE_ASSERT(fRack == nullptr);
+ return;
+ }
+
+ if (fIsRack)
+ {
+ CARLA_SAFE_ASSERT_RETURN(fRack != nullptr,);
+ delete fRack;
+ fRack = nullptr;
+ }
+ else
+ {
+ CARLA_SAFE_ASSERT_RETURN(fPatchbay != nullptr,);
+ delete fPatchbay;
+ fPatchbay = nullptr;
+ }
+
+ fIsReady = false;
+}
+
+void EngineInternalGraph::setBufferSize(const uint32_t bufferSize)
+{
+ ScopedValueSetter svs(fIsReady, false, true);
+
+ if (fIsRack)
+ {
+ CARLA_SAFE_ASSERT_RETURN(fRack != nullptr,);
+ fRack->setBufferSize(bufferSize);
+ }
+ else
+ {
+ CARLA_SAFE_ASSERT_RETURN(fPatchbay != nullptr,);
+ fPatchbay->setBufferSize(bufferSize);
+ }
+}
+
+void EngineInternalGraph::setSampleRate(const double sampleRate)
+{
+ ScopedValueSetter svs(fIsReady, false, true);
+
+ if (fIsRack)
+ {
+ CARLA_SAFE_ASSERT_RETURN(fRack != nullptr,);
+ }
+ else
+ {
+ CARLA_SAFE_ASSERT_RETURN(fPatchbay != nullptr,);
+ fPatchbay->setSampleRate(sampleRate);
+ }
+}
+
+void EngineInternalGraph::setOffline(const bool offline)
+{
+ ScopedValueSetter svs(fIsReady, false, true);
+
+ if (fIsRack)
+ {
+ CARLA_SAFE_ASSERT_RETURN(fRack != nullptr,);
+ fRack->setOffline(offline);
+ }
+ else
+ {
+ CARLA_SAFE_ASSERT_RETURN(fPatchbay != nullptr,);
+ fPatchbay->setOffline(offline);
+ }
+}
+
+bool EngineInternalGraph::isReady() const noexcept
+{
+ return fIsReady;
+}
+
+RackGraph* EngineInternalGraph::getRackGraph() const noexcept
+{
+ CARLA_SAFE_ASSERT_RETURN(fIsRack, nullptr);
+ return fRack;
+}
+
+PatchbayGraph* EngineInternalGraph::getPatchbayGraph() const noexcept
+{
+ CARLA_SAFE_ASSERT_RETURN(! fIsRack, nullptr);
+ return fPatchbay;
+}
+
+void EngineInternalGraph::process(CarlaEngine::ProtectedData* const data, const float* const* const inBuf, float* const* const outBuf, const uint32_t frames)
+{
+ if (fIsRack)
+ {
+ CARLA_SAFE_ASSERT_RETURN(fRack != nullptr,);
+ fRack->processHelper(data, inBuf, outBuf, frames);
+ }
+ else
+ {
+ CARLA_SAFE_ASSERT_RETURN(fPatchbay != nullptr,);
+ fPatchbay->process(data, inBuf, outBuf, static_cast(frames));
+ }
+}
+
+void EngineInternalGraph::processRack(CarlaEngine::ProtectedData* const data, const float* inBuf[2], float* outBuf[2], const uint32_t frames)
+{
+ CARLA_SAFE_ASSERT_RETURN(fIsRack,);
+ CARLA_SAFE_ASSERT_RETURN(fRack != nullptr,);
+
+ fRack->process(data, inBuf, outBuf, frames);
+}
+
+// -----------------------------------------------------------------------
+// used for internal patchbay mode
+
+void EngineInternalGraph::addPlugin(CarlaPlugin* const plugin)
+{
+ CARLA_SAFE_ASSERT_RETURN(fPatchbay != nullptr,);
+ fPatchbay->addPlugin(plugin);
+}
+
+void EngineInternalGraph::replacePlugin(CarlaPlugin* const oldPlugin, CarlaPlugin* const newPlugin)
+{
+ CARLA_SAFE_ASSERT_RETURN(fPatchbay != nullptr,);
+ fPatchbay->replacePlugin(oldPlugin, newPlugin);
+}
+
+void EngineInternalGraph::renamePlugin(CarlaPlugin* const plugin, const char* const newName)
+{
+ CARLA_SAFE_ASSERT_RETURN(fPatchbay != nullptr,);
+ fPatchbay->renamePlugin(plugin, newName);
+}
+
+void EngineInternalGraph::removePlugin(CarlaPlugin* const plugin)
+{
+ CARLA_SAFE_ASSERT_RETURN(fPatchbay != nullptr,);
+ fPatchbay->removePlugin(plugin);
+}
+
+void EngineInternalGraph::removeAllPlugins()
+{
+ CARLA_SAFE_ASSERT_RETURN(fPatchbay != nullptr,);
+ fPatchbay->removeAllPlugins();
+}
+
+bool EngineInternalGraph::isUsingExternal() const noexcept
+{
+ if (fIsRack)
+ return true;
+
+ CARLA_SAFE_ASSERT_RETURN(fPatchbay != nullptr, false);
+ return fPatchbay->usingExternal;
+}
+
+void EngineInternalGraph::setUsingExternal(const bool usingExternal) noexcept
+{
+ CARLA_SAFE_ASSERT_RETURN(! fIsRack,);
+ CARLA_SAFE_ASSERT_RETURN(fPatchbay != nullptr,);
+ fPatchbay->usingExternal = usingExternal;
+}
+
+// -----------------------------------------------------------------------
+// CarlaEngine Patchbay stuff
+
+bool CarlaEngine::patchbayConnect(const uint groupA, const uint portA, const uint groupB, const uint portB)
+{
+ CARLA_SAFE_ASSERT_RETURN(pData->options.processMode == ENGINE_PROCESS_MODE_CONTINUOUS_RACK || pData->options.processMode == ENGINE_PROCESS_MODE_PATCHBAY, false);
+ CARLA_SAFE_ASSERT_RETURN(pData->graph.isReady(), false);
+ carla_debug("CarlaEngine::patchbayConnect(%u, %u, %u, %u)", groupA, portA, groupB, portB);
+
+ if (pData->options.processMode == ENGINE_PROCESS_MODE_CONTINUOUS_RACK)
+ {
+ RackGraph* const graph = pData->graph.getRackGraph();
+ CARLA_SAFE_ASSERT_RETURN(graph != nullptr, false);
+
+ return graph->connect(groupA, portA, groupB, portB);
+ }
+ else
+ {
+ PatchbayGraph* const graph = pData->graph.getPatchbayGraph();
+ CARLA_SAFE_ASSERT_RETURN(graph != nullptr, false);
+
+ return graph->connect(graph->usingExternal, groupA, portA, groupB, portB, true);
+ }
+
+ return false;
+}
+
+bool CarlaEngine::patchbayDisconnect(const uint connectionId)
+{
+ CARLA_SAFE_ASSERT_RETURN(pData->options.processMode == ENGINE_PROCESS_MODE_CONTINUOUS_RACK || pData->options.processMode == ENGINE_PROCESS_MODE_PATCHBAY, false);
+ CARLA_SAFE_ASSERT_RETURN(pData->graph.isReady(), false);
+ carla_debug("CarlaEngine::patchbayDisconnect(%u)", connectionId);
+
+ if (pData->options.processMode == ENGINE_PROCESS_MODE_CONTINUOUS_RACK)
+ {
+ RackGraph* const graph = pData->graph.getRackGraph();
+ CARLA_SAFE_ASSERT_RETURN(graph != nullptr, false);
+
+ return graph->disconnect(connectionId);
+ }
+ else
+ {
+ PatchbayGraph* const graph = pData->graph.getPatchbayGraph();
+ CARLA_SAFE_ASSERT_RETURN(graph != nullptr, false);
+
+ return graph->disconnect(connectionId);
+ }
+
+ return false;
+}
+
+bool CarlaEngine::patchbayRefresh(const bool external)
+{
+ // subclasses should handle this
+ CARLA_SAFE_ASSERT_RETURN(! external, false);
+
+ if (pData->options.processMode == ENGINE_PROCESS_MODE_CONTINUOUS_RACK)
+ {
+ // This is implemented in engine subclasses
+ setLastError("Unsupported operation");
+ return false;
+ }
+
+ if (pData->options.processMode == ENGINE_PROCESS_MODE_PATCHBAY)
+ {
+ PatchbayGraph* const graph = pData->graph.getPatchbayGraph();
+ CARLA_SAFE_ASSERT_RETURN(graph != nullptr, false);
+
+ graph->refresh("");
+ return true;
+ }
+
+ setLastError("Unsupported operation");
+ return false;
+}
+
+// -----------------------------------------------------------------------
+
+const char* const* CarlaEngine::getPatchbayConnections(const bool external) const
+{
+ CARLA_SAFE_ASSERT_RETURN(pData->graph.isReady(), nullptr);
+ carla_debug("CarlaEngine::getPatchbayConnections(%s)", bool2str(external));
+
+ if (pData->options.processMode == ENGINE_PROCESS_MODE_CONTINUOUS_RACK)
+ {
+ RackGraph* const graph = pData->graph.getRackGraph();
+ CARLA_SAFE_ASSERT_RETURN(graph != nullptr, nullptr);
+ CARLA_SAFE_ASSERT_RETURN(external, nullptr);
+
+ return graph->getConnections();
+ }
+ else
+ {
+ PatchbayGraph* const graph = pData->graph.getPatchbayGraph();
+ CARLA_SAFE_ASSERT_RETURN(graph != nullptr, nullptr);
+
+ return graph->getConnections(external);
+ }
+
+ return nullptr;
+}
+
+void CarlaEngine::restorePatchbayConnection(const bool external, const char* const sourcePort, const char* const targetPort, const bool sendCallback)
+{
+ CARLA_SAFE_ASSERT_RETURN(pData->graph.isReady(),);
+ CARLA_SAFE_ASSERT_RETURN(sourcePort != nullptr && sourcePort[0] != '\0',);
+ CARLA_SAFE_ASSERT_RETURN(targetPort != nullptr && targetPort[0] != '\0',);
+ carla_debug("CarlaEngine::restorePatchbayConnection(%s, \"%s\", \"%s\")", bool2str(external), sourcePort, targetPort);
+
+ uint groupA, portA;
+ uint groupB, portB;
+
+ if (pData->options.processMode == ENGINE_PROCESS_MODE_CONTINUOUS_RACK)
+ {
+ RackGraph* const graph = pData->graph.getRackGraph();
+ CARLA_SAFE_ASSERT_RETURN(graph != nullptr,);
+ CARLA_SAFE_ASSERT_RETURN(external,);
+
+ if (! graph->getGroupAndPortIdFromFullName(sourcePort, groupA, portA))
+ return;
+ if (! graph->getGroupAndPortIdFromFullName(targetPort, groupB, portB))
+ return;
+
+ graph->connect(groupA, portA, groupB, portB);
+ }
+ else
+ {
+ PatchbayGraph* const graph = pData->graph.getPatchbayGraph();
+ CARLA_SAFE_ASSERT_RETURN(graph != nullptr,);
+
+ if (! graph->getGroupAndPortIdFromFullName(external, sourcePort, groupA, portA))
+ return;
+ if (! graph->getGroupAndPortIdFromFullName(external, targetPort, groupB, portB))
+ return;
+
+ graph->connect(external, groupA, portA, groupB, portB, sendCallback);
+ }
}
// -----------------------------------------------------------------------
@@ -1121,7 +2189,7 @@ bool CarlaEngine::connectExternalGraphPort(const uint connectionType, const uint
CARLA_SAFE_ASSERT_RETURN(connectionType != 0 || (portName != nullptr && portName[0] != '\0'), false);
CARLA_SAFE_ASSERT_RETURN(pData->options.processMode == ENGINE_PROCESS_MODE_CONTINUOUS_RACK, false);
- RackGraph* const graph(pData->graph.getGraph());
+ RackGraph* const graph(pData->graph.getRackGraph());
CARLA_SAFE_ASSERT_RETURN(graph != nullptr, false);
const CarlaRecursiveMutexLocker cml(graph->audioBuffers.mutex);
@@ -1146,7 +2214,7 @@ bool CarlaEngine::disconnectExternalGraphPort(const uint connectionType, const u
CARLA_SAFE_ASSERT_RETURN(connectionType != 0 || (portName != nullptr && portName[0] != '\0'), false);
CARLA_SAFE_ASSERT_RETURN(pData->options.processMode == ENGINE_PROCESS_MODE_CONTINUOUS_RACK, false);
- RackGraph* const graph(pData->graph.getGraph());
+ RackGraph* const graph(pData->graph.getRackGraph());
CARLA_SAFE_ASSERT_RETURN(graph != nullptr, false);
const CarlaRecursiveMutexLocker cml(graph->audioBuffers.mutex);
diff --git a/source/backend/engine/CarlaEngineGraph.hpp b/source/backend/engine/CarlaEngineGraph.hpp
index e068cddd1..87c9cffad 100644
--- a/source/backend/engine/CarlaEngineGraph.hpp
+++ b/source/backend/engine/CarlaEngineGraph.hpp
@@ -23,6 +23,12 @@
#include "CarlaPatchbayUtils.hpp"
#include "CarlaStringList.hpp"
+#include "water/water.h"
+
+using water::AudioProcessorGraph;
+using water::AudioSampleBuffer;
+using water::MidiBuffer;
+
CARLA_BACKEND_START_NAMESPACE
// -----------------------------------------------------------------------
@@ -136,6 +142,48 @@ struct RackGraph {
CARLA_DECLARE_NON_COPY_CLASS(RackGraph)
};
+// -----------------------------------------------------------------------
+// PatchbayGraph
+
+struct PatchbayGraph {
+ PatchbayConnectionList connections;
+ AudioProcessorGraph graph;
+ AudioSampleBuffer audioBuffer;
+ MidiBuffer midiBuffer;
+ const uint32_t inputs;
+ const uint32_t outputs;
+ mutable CharStringListPtr retCon;
+ bool usingExternal;
+
+ ExternalGraph extGraph;
+
+ PatchbayGraph(CarlaEngine* const engine, const uint32_t inputs, const uint32_t outputs);
+ ~PatchbayGraph();
+
+ void setBufferSize(const uint32_t bufferSize);
+ void setSampleRate(const double sampleRate);
+ void setOffline(const bool offline);
+
+ void addPlugin(CarlaPlugin* const plugin);
+ void replacePlugin(CarlaPlugin* const oldPlugin, CarlaPlugin* const newPlugin);
+ void renamePlugin(CarlaPlugin* const plugin, const char* const newName);
+ void removePlugin(CarlaPlugin* const plugin);
+ void removeAllPlugins();
+
+ bool connect(const bool external, const uint groupA, const uint portA, const uint groupB, const uint portB, const bool sendCallback);
+ bool disconnect(const uint connectionId);
+ void disconnectInternalGroup(const uint groupId) noexcept;
+ void refresh(const char* const deviceName);
+
+ const char* const* getConnections(const bool external) const;
+ bool getGroupAndPortIdFromFullName(const bool external, const char* const fullPortName, uint& groupId, uint& portId) const;
+
+ void process(CarlaEngine::ProtectedData* const data, const float* const* const inBuf, float* const* const outBuf, const int frames);
+
+ CarlaEngine* const kEngine;
+ CARLA_DECLARE_NON_COPY_CLASS(PatchbayGraph)
+};
+
// -----------------------------------------------------------------------
CARLA_BACKEND_END_NAMESPACE
diff --git a/source/backend/engine/CarlaEngineInternal.cpp b/source/backend/engine/CarlaEngineInternal.cpp
index f8a198268..75f1c8075 100644
--- a/source/backend/engine/CarlaEngineInternal.cpp
+++ b/source/backend/engine/CarlaEngineInternal.cpp
@@ -468,6 +468,13 @@ bool CarlaEngine::ProtectedData::init(const char* const clientName)
switch (options.processMode)
{
+ case ENGINE_PROCESS_MODE_CONTINUOUS_RACK:
+ maxPluginNumber = MAX_RACK_PLUGINS;
+ options.forceStereo = true;
+ break;
+ case ENGINE_PROCESS_MODE_PATCHBAY:
+ maxPluginNumber = MAX_PATCHBAY_PLUGINS;
+ break;
case ENGINE_PROCESS_MODE_BRIDGE:
maxPluginNumber = 1;
break;
@@ -479,6 +486,7 @@ bool CarlaEngine::ProtectedData::init(const char* const clientName)
switch (options.processMode)
{
case ENGINE_PROCESS_MODE_CONTINUOUS_RACK:
+ case ENGINE_PROCESS_MODE_PATCHBAY:
case ENGINE_PROCESS_MODE_BRIDGE:
events.in = new EngineEvent[kMaxEngineEventInternalCount];
events.out = new EngineEvent[kMaxEngineEventInternalCount];
diff --git a/source/backend/engine/CarlaEngineInternal.hpp b/source/backend/engine/CarlaEngineInternal.hpp
index 1713b60c7..46fab2081 100644
--- a/source/backend/engine/CarlaEngineInternal.hpp
+++ b/source/backend/engine/CarlaEngineInternal.hpp
@@ -71,19 +71,37 @@ public:
void destroy() noexcept;
void setBufferSize(const uint32_t bufferSize);
+ void setSampleRate(const double sampleRate);
void setOffline(const bool offline);
bool isReady() const noexcept;
- RackGraph* getGraph() const noexcept;
+
+ RackGraph* getRackGraph() const noexcept;
+ PatchbayGraph* getPatchbayGraph() const noexcept;
void process(CarlaEngine::ProtectedData* const data, const float* const* const inBuf, float* const* const outBuf, const uint32_t frames);
// special direct process with connections already handled, used in JACK and Plugin
void processRack(CarlaEngine::ProtectedData* const data, const float* inBuf[2], float* outBuf[2], const uint32_t frames);
+ // used for internal patchbay mode
+ void addPlugin(CarlaPlugin* const plugin);
+ void replacePlugin(CarlaPlugin* const oldPlugin, CarlaPlugin* const newPlugin);
+ void renamePlugin(CarlaPlugin* const plugin, const char* const newName);
+ void removePlugin(CarlaPlugin* const plugin);
+ void removeAllPlugins();
+
+ bool isUsingExternal() const noexcept;
+ void setUsingExternal(const bool usingExternal) noexcept;
+
private:
+ bool fIsRack;
bool fIsReady;
- RackGraph* fRack;
+
+ union {
+ RackGraph* fRack;
+ PatchbayGraph* fPatchbay;
+ };
CarlaEngine* const kEngine;
diff --git a/source/backend/engine/CarlaEngineJack.cpp b/source/backend/engine/CarlaEngineJack.cpp
index 64737908c..079ca110c 100644
--- a/source/backend/engine/CarlaEngineJack.cpp
+++ b/source/backend/engine/CarlaEngineJack.cpp
@@ -806,6 +806,7 @@ public:
CarlaThread("CarlaEngineJackCallbacks"),
#endif
fClient(nullptr),
+ fExternalPatchbay(true),
fFreewheel(false),
#ifdef BUILD_BRIDGE
fIsRunning(false)
@@ -882,7 +883,8 @@ public:
CARLA_SAFE_ASSERT_RETURN(jackbridge_is_ok(), false);
carla_debug("CarlaEngineJack::init(\"%s\")", clientName);
- fFreewheel = false;
+ fFreewheel = false;
+ fExternalPatchbay = true;
CarlaString truncatedClientName(clientName);
truncatedClientName.truncate(getMaxClientNameSize());
@@ -949,14 +951,16 @@ public:
fTimebaseMaster = jackbridge_set_timebase_callback(fClient, true, carla_jack_timebase_callback, this);
- initJackPatchbay(jackClientName);
+ if (pData->options.processMode != ENGINE_PROCESS_MODE_PATCHBAY)
+ initJackPatchbay(jackClientName);
jackbridge_set_client_registration_callback(fClient, carla_jack_client_registration_callback, this);
jackbridge_set_port_registration_callback(fClient, carla_jack_port_registration_callback, this);
jackbridge_set_port_connect_callback(fClient, carla_jack_port_connect_callback, this);
jackbridge_set_port_rename_callback(fClient, carla_jack_port_rename_callback, this);
- if (pData->options.processMode == ENGINE_PROCESS_MODE_CONTINUOUS_RACK)
+ if (pData->options.processMode == ENGINE_PROCESS_MODE_CONTINUOUS_RACK ||
+ pData->options.processMode == ENGINE_PROCESS_MODE_PATCHBAY)
{
fRackPorts[kRackPortAudioIn1] = jackbridge_port_register(fClient, "audio-in1", JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0);
fRackPorts[kRackPortAudioIn2] = jackbridge_port_register(fClient, "audio-in2", JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0);
@@ -965,8 +969,16 @@ public:
fRackPorts[kRackPortEventIn] = jackbridge_port_register(fClient, "events-in", JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0);
fRackPorts[kRackPortEventOut] = jackbridge_port_register(fClient, "events-out", JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput, 0);
- // FIXME
- pData->graph.create(0, 0);
+ if (pData->options.processMode == ENGINE_PROCESS_MODE_CONTINUOUS_RACK)
+ {
+ // FIXME?
+ pData->graph.create(0, 0);
+ }
+ else
+ {
+ pData->graph.create(2, 2);
+ patchbayRefresh(false);
+ }
}
if (jackbridge_activate(fClient))
@@ -976,8 +988,11 @@ public:
return true;
}
- if (pData->options.processMode == ENGINE_PROCESS_MODE_CONTINUOUS_RACK)
+ if (pData->options.processMode == ENGINE_PROCESS_MODE_CONTINUOUS_RACK ||
+ pData->options.processMode == ENGINE_PROCESS_MODE_PATCHBAY)
+ {
pData->graph.destroy();
+ }
pData->close();
jackbridge_client_close(fClient);
@@ -1016,7 +1031,8 @@ public:
fPostPonedEvents.clear();
// clear rack/patchbay stuff
- if (pData->options.processMode == ENGINE_PROCESS_MODE_CONTINUOUS_RACK)
+ if (pData->options.processMode == ENGINE_PROCESS_MODE_CONTINUOUS_RACK ||
+ pData->options.processMode == ENGINE_PROCESS_MODE_PATCHBAY)
{
if (deactivated)
{
@@ -1108,8 +1124,11 @@ public:
#ifndef BUILD_BRIDGE
const char* renamePlugin(const uint id, const char* const newName) override
{
- if (pData->options.processMode == ENGINE_PROCESS_MODE_CONTINUOUS_RACK)
+ if (pData->options.processMode == ENGINE_PROCESS_MODE_CONTINUOUS_RACK ||
+ pData->options.processMode == ENGINE_PROCESS_MODE_PATCHBAY)
+ {
return CarlaEngine::renamePlugin(id, newName);
+ }
CARLA_SAFE_ASSERT_RETURN(pData->plugins != nullptr, nullptr);
CARLA_SAFE_ASSERT_RETURN(pData->curPluginCount != 0, nullptr);
@@ -1257,6 +1276,9 @@ public:
{
CARLA_SAFE_ASSERT_RETURN(fClient != nullptr, false);
+ if (pData->options.processMode == ENGINE_PROCESS_MODE_PATCHBAY && ! fExternalPatchbay)
+ return CarlaEngine::patchbayConnect(groupA, portA, groupB, portB);
+
const char* const fullPortNameA = fUsedPorts.getFullPortName(groupA, portA);
CARLA_SAFE_ASSERT_RETURN(fullPortNameA != nullptr && fullPortNameA[0] != '\0', false);
@@ -1276,6 +1298,9 @@ public:
{
CARLA_SAFE_ASSERT_RETURN(fClient != nullptr, false);
+ if (pData->options.processMode == ENGINE_PROCESS_MODE_PATCHBAY && ! fExternalPatchbay)
+ return CarlaEngine::patchbayDisconnect(connectionId);
+
ConnectionToId connectionToId = { 0, 0, 0, 0, 0 };
{
@@ -1312,10 +1337,19 @@ public:
return true;
}
- bool patchbayRefresh() override
+ bool patchbayRefresh(const bool external) override
{
CARLA_SAFE_ASSERT_RETURN(fClient != nullptr, false);
+ if (pData->options.processMode == ENGINE_PROCESS_MODE_PATCHBAY)
+ {
+ fExternalPatchbay = external;
+ pData->graph.setUsingExternal(external);
+
+ if (! external)
+ return CarlaEngine::patchbayRefresh(false);
+ }
+
fUsedGroups.clear();
fUsedPorts.clear();
fUsedConnections.clear();
@@ -1403,10 +1437,13 @@ public:
// -------------------------------------------------------------------
// Patchbay stuff
- const char* const* getPatchbayConnections() const override
+ const char* const* getPatchbayConnections(const bool external) const override
{
CARLA_SAFE_ASSERT_RETURN(fClient != nullptr, nullptr);
- carla_debug("CarlaEngineJack::getPatchbayConnections()");
+ carla_debug("CarlaEngineJack::getPatchbayConnections(%s)", bool2str(external));
+
+ if (pData->options.processMode == ENGINE_PROCESS_MODE_PATCHBAY && ! external)
+ return CarlaEngine::getPatchbayConnections(external);
CarlaStringList connList;
@@ -1442,13 +1479,16 @@ public:
return fRetConns;
}
- void restorePatchbayConnection(const char* const connSource, const char* const connTarget) override
+ void restorePatchbayConnection(const bool external, const char* const connSource, const char* const connTarget, const bool sendCallback) override
{
CARLA_SAFE_ASSERT_RETURN(fClient != nullptr,);
CARLA_SAFE_ASSERT_RETURN(connSource != nullptr && connSource[0] != '\0',);
CARLA_SAFE_ASSERT_RETURN(connTarget != nullptr && connTarget[0] != '\0',);
carla_debug("CarlaEngineJack::restorePatchbayConnection(\"%s\", \"%s\")", connSource, connTarget);
+ if (pData->options.processMode == ENGINE_PROCESS_MODE_PATCHBAY && ! external)
+ return CarlaEngine::restorePatchbayConnection(external, connSource, connTarget, sendCallback);
+
if (const jack_port_t* const port = jackbridge_port_by_name(fClient, connSource))
{
if (jackbridge_port_by_name(fClient, connTarget) == nullptr)
@@ -1599,7 +1639,8 @@ protected:
}
}
}
- else if (pData->options.processMode == ENGINE_PROCESS_MODE_CONTINUOUS_RACK)
+ else if (pData->options.processMode == ENGINE_PROCESS_MODE_CONTINUOUS_RACK ||
+ pData->options.processMode == ENGINE_PROCESS_MODE_PATCHBAY)
{
CARLA_SAFE_ASSERT_RETURN(pData->events.in != nullptr,);
CARLA_SAFE_ASSERT_RETURN(pData->events.out != nullptr,);
@@ -1719,6 +1760,9 @@ protected:
{
CARLA_SAFE_ASSERT_RETURN(name != nullptr && name[0] != '\0',);
+ // ignore this if on internal patchbay mode
+ if (! fExternalPatchbay) return;
+
// do nothing on client registration, wait for first port
if (reg) return;
@@ -1736,6 +1780,9 @@ protected:
void handleJackPortRegistrationCallback(const jack_port_id_t port, const bool reg)
{
+ // ignore this if on internal patchbay mode
+ if (! fExternalPatchbay) return;
+
const jack_port_t* const jackPort(jackbridge_port_by_id(fClient, port));
CARLA_SAFE_ASSERT_RETURN(jackPort != nullptr,);
@@ -1788,6 +1835,9 @@ protected:
void handleJackPortConnectCallback(const jack_port_id_t a, const jack_port_id_t b, const bool connect)
{
+ // ignore this if on internal patchbay mode
+ if (! fExternalPatchbay) return;
+
const jack_port_t* const jackPortA(jackbridge_port_by_id(fClient, a));
CARLA_SAFE_ASSERT_RETURN(jackPortA != nullptr,);
@@ -1850,6 +1900,9 @@ protected:
void handleJackPortRenameCallback(const jack_port_id_t port, const char* const oldFullName, const char* const newFullName)
{
+ // ignore this if on internal patchbay mode
+ if (! fExternalPatchbay) return;
+
CARLA_SAFE_ASSERT_RETURN(oldFullName != nullptr && oldFullName[0] != '\0',);
CARLA_SAFE_ASSERT_RETURN(newFullName != nullptr && newFullName[0] != '\0',);
@@ -1937,6 +1990,7 @@ protected:
private:
jack_client_t* fClient;
+ bool fExternalPatchbay;
bool fFreewheel;
// -------------------------------------------------------------------
@@ -2017,6 +2071,7 @@ private:
void initJackPatchbay(const char* const ourName)
{
+ CARLA_SAFE_ASSERT_RETURN(pData->options.processMode != ENGINE_PROCESS_MODE_PATCHBAY || fExternalPatchbay,);
CARLA_SAFE_ASSERT_RETURN(ourName != nullptr && ourName[0] != '\0',);
CarlaStringList parsedGroups;
diff --git a/source/backend/engine/CarlaEngineNative.cpp b/source/backend/engine/CarlaEngineNative.cpp
index 781bb1ace..e56fc3d6b 100644
--- a/source/backend/engine/CarlaEngineNative.cpp
+++ b/source/backend/engine/CarlaEngineNative.cpp
@@ -158,7 +158,7 @@ protected:
else if (std::strcmp(msg, "patchbay_refresh") == 0)
{
try {
- ok = fEngine->patchbayRefresh();
+ ok = fEngine->patchbayRefresh(false);
} CARLA_SAFE_EXCEPTION("patchbayRefresh");
}
else if (std::strcmp(msg, "transport_play") == 0)
@@ -586,9 +586,10 @@ private:
class CarlaEngineNative : public CarlaEngine
{
public:
- CarlaEngineNative(const NativeHostDescriptor* const host, const uint32_t inChan = 2, uint32_t outChan = 0)
+ CarlaEngineNative(const NativeHostDescriptor* const host, const bool isPatchbay, const uint32_t inChan = 2, uint32_t outChan = 0)
: CarlaEngine(),
pHost(host),
+ kIsPatchbay(isPatchbay),
fIsActive(false),
fIsRunning(false),
fUiServer(this),
@@ -606,15 +607,29 @@ public:
if (outChan == 0)
outChan = inChan;
- CARLA_SAFE_ASSERT(inChan == 2);
- CARLA_SAFE_ASSERT(outChan == 2);
- pData->options.processMode = ENGINE_PROCESS_MODE_CONTINUOUS_RACK;
- pData->options.transportMode = ENGINE_TRANSPORT_MODE_PLUGIN;
- pData->options.forceStereo = true;
- pData->options.preferPluginBridges = false;
- pData->options.preferUiBridges = false;
- init("Carla-Rack");
- pData->graph.create(0, 0); // FIXME?
+ // set-up engine
+ if (kIsPatchbay)
+ {
+ pData->options.processMode = ENGINE_PROCESS_MODE_PATCHBAY;
+ pData->options.transportMode = ENGINE_TRANSPORT_MODE_PLUGIN;
+ pData->options.forceStereo = false;
+ pData->options.preferPluginBridges = false;
+ pData->options.preferUiBridges = false;
+ init("Carla-Patchbay");
+ pData->graph.create(inChan, outChan);
+ }
+ else
+ {
+ CARLA_SAFE_ASSERT(inChan == 2);
+ CARLA_SAFE_ASSERT(outChan == 2);
+ pData->options.processMode = ENGINE_PROCESS_MODE_CONTINUOUS_RACK;
+ pData->options.transportMode = ENGINE_TRANSPORT_MODE_PLUGIN;
+ pData->options.forceStereo = true;
+ pData->options.preferPluginBridges = false;
+ pData->options.preferUiBridges = false;
+ init("Carla-Rack");
+ pData->graph.create(0, 0); // FIXME?
+ }
if (pData->options.resourceDir != nullptr)
delete[] pData->options.resourceDir;
@@ -1341,7 +1356,7 @@ protected:
// ---------------------------------------------------------------
// Do nothing if no plugins and rack mode
- if (pData->curPluginCount == 0)
+ if (pData->curPluginCount == 0 && ! kIsPatchbay)
{
if (outBuffer[0] != inBuffer[0])
carla_copyFloats(outBuffer[0], inBuffer[0], frames);
@@ -1382,6 +1397,14 @@ protected:
}
}
+ if (kIsPatchbay)
+ {
+ // -----------------------------------------------------------
+ // process
+
+ pData->graph.process(pData, inBuffer, outBuffer, frames);
+ }
+ else
{
// -----------------------------------------------------------
// create audio buffers
@@ -1456,7 +1479,10 @@ protected:
CarlaString path(pHost->resourceDir);
- path += CARLA_OS_SEP_STR "carla-plugin";
+ if (kIsPatchbay)
+ path += CARLA_OS_SEP_STR "carla-plugin-patchbay";
+ else
+ path += CARLA_OS_SEP_STR "carla-plugin";
#ifdef CARLA_OS_WIN
path += ".exe";
#endif
@@ -1486,6 +1512,9 @@ protected:
}
}
+ if (kIsPatchbay)
+ patchbayRefresh(false);
+
if (fWaitForReadyMsg)
{
carla_stdout("Using Carla plugin embedded, waiting for it to be ready...");
@@ -1653,6 +1682,26 @@ public:
return new CarlaEngineNative(host, false);
}
+ static NativePluginHandle _instantiatePatchbay(const NativeHostDescriptor* host)
+ {
+ return new CarlaEngineNative(host, true);
+ }
+
+ static NativePluginHandle _instantiatePatchbay3s(const NativeHostDescriptor* host)
+ {
+ return new CarlaEngineNative(host, true, 3, 2);
+ }
+
+ static NativePluginHandle _instantiatePatchbay16(const NativeHostDescriptor* host)
+ {
+ return new CarlaEngineNative(host, true, 16);
+ }
+
+ static NativePluginHandle _instantiatePatchbay32(const NativeHostDescriptor* host)
+ {
+ return new CarlaEngineNative(host, true, 32);
+ }
+
static void _cleanup(NativePluginHandle handle)
{
delete handlePtr;
@@ -1776,6 +1825,7 @@ public:
private:
const NativeHostDescriptor* const pHost;
+ const bool kIsPatchbay; // rack if false
bool fIsActive, fIsRunning;
CarlaEngineNativeUI fUiServer;
@@ -1843,6 +1893,174 @@ static const NativePluginDescriptor carlaRackDesc = {
CarlaEngineNative::_dispatcher
};
+static const NativePluginDescriptor carlaPatchbayDesc = {
+ /* category */ NATIVE_PLUGIN_CATEGORY_OTHER,
+ /* hints */ static_cast(NATIVE_PLUGIN_IS_SYNTH
+ |NATIVE_PLUGIN_HAS_UI
+ //|NATIVE_PLUGIN_NEEDS_FIXED_BUFFERS
+ |NATIVE_PLUGIN_NEEDS_UI_MAIN_THREAD
+ |NATIVE_PLUGIN_USES_STATE
+ |NATIVE_PLUGIN_USES_TIME),
+ /* supports */ static_cast(NATIVE_PLUGIN_SUPPORTS_EVERYTHING),
+ /* audioIns */ 2,
+ /* audioOuts */ 2,
+ /* midiIns */ 1,
+ /* midiOuts */ 1,
+ /* paramIns */ 0,
+ /* paramOuts */ 0,
+ /* name */ "Carla-Patchbay",
+ /* label */ "carlapatchbay",
+ /* maker */ "falkTX",
+ /* copyright */ "GNU GPL v2+",
+ CarlaEngineNative::_instantiatePatchbay,
+ CarlaEngineNative::_cleanup,
+ CarlaEngineNative::_get_parameter_count,
+ CarlaEngineNative::_get_parameter_info,
+ CarlaEngineNative::_get_parameter_value,
+ CarlaEngineNative::_get_midi_program_count,
+ CarlaEngineNative::_get_midi_program_info,
+ CarlaEngineNative::_set_parameter_value,
+ CarlaEngineNative::_set_midi_program,
+ /* _set_custom_data */ nullptr,
+ CarlaEngineNative::_ui_show,
+ CarlaEngineNative::_ui_idle,
+ /* _ui_set_parameter_value */ nullptr,
+ /* _ui_set_midi_program */ nullptr,
+ /* _ui_set_custom_data */ nullptr,
+ CarlaEngineNative::_activate,
+ CarlaEngineNative::_deactivate,
+ CarlaEngineNative::_process,
+ CarlaEngineNative::_get_state,
+ CarlaEngineNative::_set_state,
+ CarlaEngineNative::_dispatcher
+};
+
+static const NativePluginDescriptor carlaPatchbay3sDesc = {
+ /* category */ NATIVE_PLUGIN_CATEGORY_OTHER,
+ /* hints */ static_cast(NATIVE_PLUGIN_IS_SYNTH
+ |NATIVE_PLUGIN_HAS_UI
+ //|NATIVE_PLUGIN_NEEDS_FIXED_BUFFERS
+ |NATIVE_PLUGIN_NEEDS_UI_MAIN_THREAD
+ |NATIVE_PLUGIN_USES_STATE
+ |NATIVE_PLUGIN_USES_TIME),
+ /* supports */ static_cast(NATIVE_PLUGIN_SUPPORTS_EVERYTHING),
+ /* audioIns */ 3,
+ /* audioOuts */ 2,
+ /* midiIns */ 1,
+ /* midiOuts */ 1,
+ /* paramIns */ 0,
+ /* paramOuts */ 0,
+ /* name */ "Carla-Patchbay (sidechain)",
+ /* label */ "carlapatchbay3s",
+ /* maker */ "falkTX",
+ /* copyright */ "GNU GPL v2+",
+ CarlaEngineNative::_instantiatePatchbay3s,
+ CarlaEngineNative::_cleanup,
+ CarlaEngineNative::_get_parameter_count,
+ CarlaEngineNative::_get_parameter_info,
+ CarlaEngineNative::_get_parameter_value,
+ CarlaEngineNative::_get_midi_program_count,
+ CarlaEngineNative::_get_midi_program_info,
+ CarlaEngineNative::_set_parameter_value,
+ CarlaEngineNative::_set_midi_program,
+ /* _set_custom_data */ nullptr,
+ CarlaEngineNative::_ui_show,
+ CarlaEngineNative::_ui_idle,
+ /* _ui_set_parameter_value */ nullptr,
+ /* _ui_set_midi_program */ nullptr,
+ /* _ui_set_custom_data */ nullptr,
+ CarlaEngineNative::_activate,
+ CarlaEngineNative::_deactivate,
+ CarlaEngineNative::_process,
+ CarlaEngineNative::_get_state,
+ CarlaEngineNative::_set_state,
+ CarlaEngineNative::_dispatcher
+};
+
+static const NativePluginDescriptor carlaPatchbay16Desc = {
+ /* category */ NATIVE_PLUGIN_CATEGORY_OTHER,
+ /* hints */ static_cast(NATIVE_PLUGIN_IS_SYNTH
+ |NATIVE_PLUGIN_HAS_UI
+ //|NATIVE_PLUGIN_NEEDS_FIXED_BUFFERS
+ |NATIVE_PLUGIN_NEEDS_UI_MAIN_THREAD
+ |NATIVE_PLUGIN_USES_STATE
+ |NATIVE_PLUGIN_USES_TIME),
+ /* supports */ static_cast(NATIVE_PLUGIN_SUPPORTS_EVERYTHING),
+ /* audioIns */ 16,
+ /* audioOuts */ 16,
+ /* midiIns */ 1,
+ /* midiOuts */ 1,
+ /* paramIns */ 0,
+ /* paramOuts */ 0,
+ /* name */ "Carla-Patchbay (16chan)",
+ /* label */ "carlapatchbay16",
+ /* maker */ "falkTX",
+ /* copyright */ "GNU GPL v2+",
+ CarlaEngineNative::_instantiatePatchbay16,
+ CarlaEngineNative::_cleanup,
+ CarlaEngineNative::_get_parameter_count,
+ CarlaEngineNative::_get_parameter_info,
+ CarlaEngineNative::_get_parameter_value,
+ CarlaEngineNative::_get_midi_program_count,
+ CarlaEngineNative::_get_midi_program_info,
+ CarlaEngineNative::_set_parameter_value,
+ CarlaEngineNative::_set_midi_program,
+ /* _set_custom_data */ nullptr,
+ CarlaEngineNative::_ui_show,
+ CarlaEngineNative::_ui_idle,
+ /* _ui_set_parameter_value */ nullptr,
+ /* _ui_set_midi_program */ nullptr,
+ /* _ui_set_custom_data */ nullptr,
+ CarlaEngineNative::_activate,
+ CarlaEngineNative::_deactivate,
+ CarlaEngineNative::_process,
+ CarlaEngineNative::_get_state,
+ CarlaEngineNative::_set_state,
+ CarlaEngineNative::_dispatcher
+};
+
+static const NativePluginDescriptor carlaPatchbay32Desc = {
+ /* category */ NATIVE_PLUGIN_CATEGORY_OTHER,
+ /* hints */ static_cast(NATIVE_PLUGIN_IS_SYNTH
+ |NATIVE_PLUGIN_HAS_UI
+ //|NATIVE_PLUGIN_NEEDS_FIXED_BUFFERS
+ |NATIVE_PLUGIN_NEEDS_UI_MAIN_THREAD
+ |NATIVE_PLUGIN_USES_STATE
+ |NATIVE_PLUGIN_USES_TIME),
+ /* supports */ static_cast(NATIVE_PLUGIN_SUPPORTS_EVERYTHING),
+ /* audioIns */ 32,
+ /* audioOuts */ 32,
+ /* midiIns */ 1,
+ /* midiOuts */ 1,
+ /* paramIns */ 0,
+ /* paramOuts */ 0,
+ /* name */ "Carla-Patchbay (32chan)",
+ /* label */ "carlapatchbay32",
+ /* maker */ "falkTX",
+ /* copyright */ "GNU GPL v2+",
+ CarlaEngineNative::_instantiatePatchbay32,
+ CarlaEngineNative::_cleanup,
+ CarlaEngineNative::_get_parameter_count,
+ CarlaEngineNative::_get_parameter_info,
+ CarlaEngineNative::_get_parameter_value,
+ CarlaEngineNative::_get_midi_program_count,
+ CarlaEngineNative::_get_midi_program_info,
+ CarlaEngineNative::_set_parameter_value,
+ CarlaEngineNative::_set_midi_program,
+ /* _set_custom_data */ nullptr,
+ CarlaEngineNative::_ui_show,
+ CarlaEngineNative::_ui_idle,
+ /* _ui_set_parameter_value */ nullptr,
+ /* _ui_set_midi_program */ nullptr,
+ /* _ui_set_custom_data */ nullptr,
+ CarlaEngineNative::_activate,
+ CarlaEngineNative::_deactivate,
+ CarlaEngineNative::_process,
+ CarlaEngineNative::_get_state,
+ CarlaEngineNative::_set_state,
+ CarlaEngineNative::_dispatcher
+};
+
CARLA_BACKEND_END_NAMESPACE
// -----------------------------------------------------------------------
@@ -1854,6 +2072,10 @@ void carla_register_native_plugin_carla()
{
CARLA_BACKEND_USE_NAMESPACE;
carla_register_native_plugin(&carlaRackDesc);
+ carla_register_native_plugin(&carlaPatchbayDesc);
+ carla_register_native_plugin(&carlaPatchbay3sDesc);
+ carla_register_native_plugin(&carlaPatchbay16Desc);
+ carla_register_native_plugin(&carlaPatchbay32Desc);
}
// -----------------------------------------------------------------------
@@ -1866,6 +2088,14 @@ const NativePluginDescriptor* carla_get_native_rack_plugin()
return &carlaRackDesc;
}
+CARLA_EXPORT
+const NativePluginDescriptor* carla_get_native_patchbay_plugin();
+const NativePluginDescriptor* carla_get_native_patchbay_plugin()
+{
+ CARLA_BACKEND_USE_NAMESPACE;
+ return &carlaPatchbayDesc;
+}
+
// -----------------------------------------------------------------------
// Extra stuff for linking purposes
diff --git a/source/backend/engine/CarlaEnginePorts.cpp b/source/backend/engine/CarlaEnginePorts.cpp
index 1c5050bcf..f3aca651b 100644
--- a/source/backend/engine/CarlaEnginePorts.cpp
+++ b/source/backend/engine/CarlaEnginePorts.cpp
@@ -89,17 +89,30 @@ CarlaEngineEventPort::CarlaEngineEventPort(const CarlaEngineClient& client, cons
kProcessMode(client.getEngine().getProccessMode())
{
carla_debug("CarlaEngineEventPort::CarlaEngineEventPort(%s)", bool2str(isInputPort));
+
+ if (kProcessMode == ENGINE_PROCESS_MODE_PATCHBAY)
+ fBuffer = new EngineEvent[kMaxEngineEventInternalCount];
}
CarlaEngineEventPort::~CarlaEngineEventPort() noexcept
{
carla_debug("CarlaEngineEventPort::~CarlaEngineEventPort()");
+
+ if (kProcessMode == ENGINE_PROCESS_MODE_PATCHBAY)
+ {
+ CARLA_SAFE_ASSERT_RETURN(fBuffer != nullptr,);
+
+ delete[] fBuffer;
+ fBuffer = nullptr;
+ }
}
void CarlaEngineEventPort::initBuffer() noexcept
{
if (kProcessMode == ENGINE_PROCESS_MODE_CONTINUOUS_RACK || kProcessMode == ENGINE_PROCESS_MODE_BRIDGE)
fBuffer = kClient.getEngine().getInternalEventBuffer(kIsInput);
+ else if (kProcessMode == ENGINE_PROCESS_MODE_PATCHBAY && ! kIsInput)
+ carla_zeroStructs(fBuffer, kMaxEngineEventInternalCount);
}
uint32_t CarlaEngineEventPort::getEventCount() const noexcept
diff --git a/source/backend/engine/CarlaEngineRtAudio.cpp b/source/backend/engine/CarlaEngineRtAudio.cpp
index 672ae939a..7ae59908a 100644
--- a/source/backend/engine/CarlaEngineRtAudio.cpp
+++ b/source/backend/engine/CarlaEngineRtAudio.cpp
@@ -183,7 +183,7 @@ public:
CARLA_SAFE_ASSERT_RETURN(clientName != nullptr && clientName[0] != '\0', false);
carla_debug("CarlaEngineRtAudio::init(\"%s\")", clientName);
- if (pData->options.processMode != ENGINE_PROCESS_MODE_CONTINUOUS_RACK)
+ if (pData->options.processMode != ENGINE_PROCESS_MODE_CONTINUOUS_RACK && pData->options.processMode != ENGINE_PROCESS_MODE_PATCHBAY)
{
setLastError("Invalid process mode");
return false;
@@ -293,7 +293,10 @@ public:
return false;
}
- patchbayRefresh();
+ patchbayRefresh(false);
+
+ if (pData->options.processMode == ENGINE_PROCESS_MODE_PATCHBAY)
+ refreshExternalGraphPorts(pData->graph.getPatchbayGraph(), false);
callback(ENGINE_CALLBACK_ENGINE_STARTED, 0, pData->options.processMode, pData->options.transportMode, 0.0f, getCurrentDriverName());
return true;
@@ -402,11 +405,9 @@ public:
// -------------------------------------------------------------------
// Patchbay
- bool patchbayRefresh() override
+ template
+ bool refreshExternalGraphPorts(Graph* const graph, const bool sendCallback)
{
- CARLA_SAFE_ASSERT_RETURN(pData->graph.isReady(), false);
-
- RackGraph* const graph(pData->graph.getGraph());
CARLA_SAFE_ASSERT_RETURN(graph != nullptr, false);
char strBuf[STR_MAX+1];
@@ -473,7 +474,8 @@ public:
// ---------------------------------------------------------------
// now refresh
- graph->refresh(fDeviceName.buffer());
+ if (sendCallback)
+ graph->refresh(fDeviceName.buffer());
// ---------------------------------------------------------------
// add midi connections
@@ -495,7 +497,8 @@ public:
extGraph.connections.list.append(connectionToId);
- callback(ENGINE_CALLBACK_PATCHBAY_CONNECTION_ADDED, connectionToId.id, 0, 0, 0.0f, strBuf);
+ if (sendCallback)
+ callback(ENGINE_CALLBACK_PATCHBAY_CONNECTION_ADDED, connectionToId.id, 0, 0, 0.0f, strBuf);
}
fMidiOutMutex.lock();
@@ -517,7 +520,8 @@ public:
extGraph.connections.list.append(connectionToId);
- callback(ENGINE_CALLBACK_PATCHBAY_CONNECTION_ADDED, connectionToId.id, 0, 0, 0.0f, strBuf);
+ if (sendCallback)
+ callback(ENGINE_CALLBACK_PATCHBAY_CONNECTION_ADDED, connectionToId.id, 0, 0, 0.0f, strBuf);
}
fMidiOutMutex.unlock();
@@ -525,6 +529,21 @@ public:
return true;
}
+ bool patchbayRefresh(const bool external) override
+ {
+ CARLA_SAFE_ASSERT_RETURN(pData->graph.isReady(), false);
+
+ if (pData->options.processMode == ENGINE_PROCESS_MODE_CONTINUOUS_RACK)
+ return refreshExternalGraphPorts(pData->graph.getRackGraph(), true);
+
+ pData->graph.setUsingExternal(external);
+
+ if (external)
+ return refreshExternalGraphPorts(pData->graph.getPatchbayGraph(), true);
+
+ return CarlaEngine::patchbayRefresh(false);
+ }
+
// -------------------------------------------------------------------
protected:
diff --git a/source/modules/water/containers/juce_NamedValueSet.cpp b/source/modules/water/containers/juce_NamedValueSet.cpp
index 60110b98b..c763a3451 100644
--- a/source/modules/water/containers/juce_NamedValueSet.cpp
+++ b/source/modules/water/containers/juce_NamedValueSet.cpp
@@ -207,45 +207,3 @@ var* NamedValueSet::getVarPointerAt (int index) const noexcept
return nullptr;
}
-
-void NamedValueSet::setFromXmlAttributes (const XmlElement& xml)
-{
- values.clearQuick();
-
- for (const XmlElement::XmlAttributeNode* att = xml.attributes; att != nullptr; att = att->nextListItem)
- {
- if (att->name.toString().startsWith ("base64:"))
- {
- MemoryBlock mb;
-
- if (mb.fromBase64Encoding (att->value))
- {
- values.add (NamedValue (att->name.toString().substring (7), var (mb)));
- continue;
- }
- }
-
- values.add (NamedValue (att->name, var (att->value)));
- }
-}
-
-void NamedValueSet::copyToXmlAttributes (XmlElement& xml) const
-{
- for (NamedValue* e = values.end(), *i = values.begin(); i != e; ++i)
- {
- if (const MemoryBlock* mb = i->value.getBinaryData())
- {
- xml.setAttribute ("base64:" + i->name.toString(), mb->toBase64Encoding());
- }
- else
- {
- // These types can't be stored as XML!
- jassert (! i->value.isObject());
- jassert (! i->value.isMethod());
- jassert (! i->value.isArray());
-
- xml.setAttribute (i->name.toString(),
- i->value.toString());
- }
- }
-}
diff --git a/source/modules/water/containers/juce_Variant.cpp b/source/modules/water/containers/juce_Variant.cpp
index 82e7d424e..1559d48ae 100644
--- a/source/modules/water/containers/juce_Variant.cpp
+++ b/source/modules/water/containers/juce_Variant.cpp
@@ -36,8 +36,6 @@ enum VariantStreamMarkers
varMarker_Double = 4,
varMarker_String = 5,
varMarker_Int64 = 6,
- varMarker_Array = 7,
- varMarker_Binary = 8,
varMarker_Undefined = 9
};
@@ -53,9 +51,6 @@ public:
virtual double toDouble (const ValueUnion&) const noexcept { return 0; }
virtual String toString (const ValueUnion&) const { return String(); }
virtual bool toBool (const ValueUnion&) const noexcept { return false; }
- virtual ReferenceCountedObject* toObject (const ValueUnion&) const noexcept { return nullptr; }
- virtual Array* toArray (const ValueUnion&) const noexcept { return nullptr; }
- virtual MemoryBlock* toBinary (const ValueUnion&) const noexcept { return nullptr; }
virtual var clone (const var& original) const { return original; }
virtual bool isVoid() const noexcept { return false; }
@@ -65,10 +60,6 @@ public:
virtual bool isBool() const noexcept { return false; }
virtual bool isDouble() const noexcept { return false; }
virtual bool isString() const noexcept { return false; }
- virtual bool isObject() const noexcept { return false; }
- virtual bool isArray() const noexcept { return false; }
- virtual bool isBinary() const noexcept { return false; }
- virtual bool isMethod() const noexcept { return false; }
virtual void cleanUp (ValueUnion&) const noexcept {}
virtual void createCopy (ValueUnion& dest, const ValueUnion& source) const { dest = source; }
@@ -259,167 +250,6 @@ private:
static inline String* getString (ValueUnion& data) noexcept { return reinterpret_cast (data.stringValue); }
};
-//==============================================================================
-class var::VariantType_Object : public var::VariantType
-{
-public:
- VariantType_Object() noexcept {}
- static const VariantType_Object instance;
-
- void cleanUp (ValueUnion& data) const noexcept override { if (data.objectValue != nullptr) data.objectValue->decReferenceCount(); }
-
- void createCopy (ValueUnion& dest, const ValueUnion& source) const override
- {
- dest.objectValue = source.objectValue;
- if (dest.objectValue != nullptr)
- dest.objectValue->incReferenceCount();
- }
-
- String toString (const ValueUnion& data) const override { return "Object 0x" + String::toHexString ((int) (pointer_sized_int) data.objectValue); }
- bool toBool (const ValueUnion& data) const noexcept override { return data.objectValue != nullptr; }
- ReferenceCountedObject* toObject (const ValueUnion& data) const noexcept override { return data.objectValue; }
- bool isObject() const noexcept override { return true; }
-
- bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept override
- {
- return otherType.toObject (otherData) == data.objectValue;
- }
-
- var clone (const var& original) const override
- {
- if (DynamicObject* d = original.getDynamicObject())
- return d->clone().get();
-
- jassertfalse; // can only clone DynamicObjects!
- return var();
- }
-
- void writeToStream (const ValueUnion&, OutputStream& output) const override
- {
- jassertfalse; // Can't write an object to a stream!
- output.writeCompressedInt (0);
- }
-};
-
-//==============================================================================
-class var::VariantType_Array : public var::VariantType_Object
-{
-public:
- VariantType_Array() noexcept {}
- static const VariantType_Array instance;
-
- String toString (const ValueUnion&) const override { return "[Array]"; }
- ReferenceCountedObject* toObject (const ValueUnion&) const noexcept override { return nullptr; }
- bool isArray() const noexcept override { return true; }
-
- Array* toArray (const ValueUnion& data) const noexcept override
- {
- if (RefCountedArray* a = dynamic_cast (data.objectValue))
- return &(a->array);
-
- return nullptr;
- }
-
- bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept override
- {
- const Array* const thisArray = toArray (data);
- const Array* const otherArray = otherType.toArray (otherData);
- return thisArray == otherArray || (thisArray != nullptr && otherArray != nullptr && *otherArray == *thisArray);
- }
-
- var clone (const var& original) const override
- {
- Array arrayCopy;
-
- if (const Array* array = toArray (original.value))
- for (int i = 0; i < array->size(); ++i)
- arrayCopy.add (array->getReference(i).clone());
-
- return var (arrayCopy);
- }
-
- void writeToStream (const ValueUnion& data, OutputStream& output) const override
- {
- if (const Array* array = toArray (data))
- {
- MemoryOutputStream buffer (512);
- const int numItems = array->size();
- buffer.writeCompressedInt (numItems);
-
- for (int i = 0; i < numItems; ++i)
- array->getReference(i).writeToStream (buffer);
-
- output.writeCompressedInt (1 + (int) buffer.getDataSize());
- output.writeByte (varMarker_Array);
- output << buffer;
- }
- }
-
- struct RefCountedArray : public ReferenceCountedObject
- {
- RefCountedArray (const Array& a) : array (a) { incReferenceCount(); }
- #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS
- RefCountedArray (Array&& a) : array (static_cast&&> (a)) { incReferenceCount(); }
- #endif
- Array array;
- };
-};
-
-//==============================================================================
-class var::VariantType_Binary : public var::VariantType
-{
-public:
- VariantType_Binary() noexcept {}
-
- static const VariantType_Binary instance;
-
- void cleanUp (ValueUnion& data) const noexcept override { delete data.binaryValue; }
- void createCopy (ValueUnion& dest, const ValueUnion& source) const override { dest.binaryValue = new MemoryBlock (*source.binaryValue); }
-
- String toString (const ValueUnion& data) const override { return data.binaryValue->toBase64Encoding(); }
- bool isBinary() const noexcept override { return true; }
- MemoryBlock* toBinary (const ValueUnion& data) const noexcept override { return data.binaryValue; }
-
- bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept override
- {
- const MemoryBlock* const otherBlock = otherType.toBinary (otherData);
- return otherBlock != nullptr && *otherBlock == *data.binaryValue;
- }
-
- void writeToStream (const ValueUnion& data, OutputStream& output) const override
- {
- output.writeCompressedInt (1 + (int) data.binaryValue->getSize());
- output.writeByte (varMarker_Binary);
- output << *data.binaryValue;
- }
-};
-
-//==============================================================================
-class var::VariantType_Method : public var::VariantType
-{
-public:
- VariantType_Method() noexcept {}
- static const VariantType_Method instance;
-
- void cleanUp (ValueUnion& data) const noexcept override { if (data.methodValue != nullptr ) delete data.methodValue; }
- void createCopy (ValueUnion& dest, const ValueUnion& source) const override { dest.methodValue = new NativeFunction (*source.methodValue); }
-
- String toString (const ValueUnion&) const override { return "Method"; }
- bool toBool (const ValueUnion& data) const noexcept override { return data.methodValue != nullptr; }
- bool isMethod() const noexcept override { return true; }
-
- bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept override
- {
- return otherType.isMethod() && otherData.methodValue == data.methodValue;
- }
-
- void writeToStream (const ValueUnion&, OutputStream& output) const override
- {
- jassertfalse; // Can't write a method to a stream!
- output.writeCompressedInt (0);
- }
-};
-
//==============================================================================
const var::VariantType_Void var::VariantType_Void::instance;
const var::VariantType_Undefined var::VariantType_Undefined::instance;
@@ -428,10 +258,6 @@ const var::VariantType_Int64 var::VariantType_Int64::instance;
const var::VariantType_Bool var::VariantType_Bool::instance;
const var::VariantType_Double var::VariantType_Double::instance;
const var::VariantType_String var::VariantType_String::instance;
-const var::VariantType_Object var::VariantType_Object::instance;
-const var::VariantType_Array var::VariantType_Array::instance;
-const var::VariantType_Binary var::VariantType_Binary::instance;
-const var::VariantType_Method var::VariantType_Method::instance;
//==============================================================================
@@ -449,32 +275,8 @@ var::var (const int v) noexcept : type (&VariantType_Int::instance) { v
var::var (const int64 v) noexcept : type (&VariantType_Int64::instance) { value.int64Value = v; }
var::var (const bool v) noexcept : type (&VariantType_Bool::instance) { value.boolValue = v; }
var::var (const double v) noexcept : type (&VariantType_Double::instance) { value.doubleValue = v; }
-var::var (NativeFunction m) noexcept : type (&VariantType_Method::instance) { value.methodValue = new NativeFunction (m); }
-var::var (const Array& v) : type (&VariantType_Array::instance) { value.objectValue = new VariantType_Array::RefCountedArray(v); }
var::var (const String& v) : type (&VariantType_String::instance) { new (value.stringValue) String (v); }
var::var (const char* const v) : type (&VariantType_String::instance) { new (value.stringValue) String (v); }
-var::var (const wchar_t* const v) : type (&VariantType_String::instance) { new (value.stringValue) String (v); }
-var::var (const void* v, size_t sz) : type (&VariantType_Binary::instance) { value.binaryValue = new MemoryBlock (v, sz); }
-var::var (const MemoryBlock& v) : type (&VariantType_Binary::instance) { value.binaryValue = new MemoryBlock (v); }
-
-var::var (const StringArray& v) : type (&VariantType_Array::instance)
-{
- Array strings;
-
- const int n = v.size();
- for (int i = 0; i < n; ++i)
- strings.add (var (v[i]));
-
- value.objectValue = new VariantType_Array::RefCountedArray(strings);
-}
-
-var::var (ReferenceCountedObject* const object) : type (&VariantType_Object::instance)
-{
- value.objectValue = object;
-
- if (object != nullptr)
- object->incReferenceCount();
-}
var var::undefined() noexcept { return var (VariantType_Undefined::instance); }
@@ -486,10 +288,6 @@ bool var::isInt64() const noexcept { return type->isInt64(); }
bool var::isBool() const noexcept { return type->isBool(); }
bool var::isDouble() const noexcept { return type->isDouble(); }
bool var::isString() const noexcept { return type->isString(); }
-bool var::isObject() const noexcept { return type->isObject(); }
-bool var::isArray() const noexcept { return type->isArray(); }
-bool var::isBinaryData() const noexcept { return type->isBinary(); }
-bool var::isMethod() const noexcept { return type->isMethod(); }
var::operator int() const noexcept { return type->toInt (value); }
var::operator int64() const noexcept { return type->toInt64 (value); }
@@ -498,10 +296,6 @@ var::operator float() const noexcept { return (float) type->t
var::operator double() const noexcept { return type->toDouble (value); }
String var::toString() const { return type->toString (value); }
var::operator String() const { return type->toString (value); }
-ReferenceCountedObject* var::getObject() const noexcept { return type->toObject (value); }
-Array* var::getArray() const noexcept { return type->toArray (value); }
-MemoryBlock* var::getBinaryData() const noexcept { return type->toBinary (value); }
-DynamicObject* var::getDynamicObject() const noexcept { return dynamic_cast (getObject()); }
//==============================================================================
void var::swapWith (var& other) noexcept
@@ -516,12 +310,7 @@ var& var::operator= (const int64 v) { type->cleanUp (value); type =
var& var::operator= (const bool v) { type->cleanUp (value); type = &VariantType_Bool::instance; value.boolValue = v; return *this; }
var& var::operator= (const double v) { type->cleanUp (value); type = &VariantType_Double::instance; value.doubleValue = v; return *this; }
var& var::operator= (const char* const v) { type->cleanUp (value); type = &VariantType_String::instance; new (value.stringValue) String (v); return *this; }
-var& var::operator= (const wchar_t* const v) { type->cleanUp (value); type = &VariantType_String::instance; new (value.stringValue) String (v); return *this; }
var& var::operator= (const String& v) { type->cleanUp (value); type = &VariantType_String::instance; new (value.stringValue) String (v); return *this; }
-var& var::operator= (const MemoryBlock& v) { type->cleanUp (value); type = &VariantType_Binary::instance; value.binaryValue = new MemoryBlock (v); return *this; }
-var& var::operator= (const Array& v) { var v2 (v); swapWith (v2); return *this; }
-var& var::operator= (ReferenceCountedObject* v) { var v2 (v); swapWith (v2); return *this; }
-var& var::operator= (NativeFunction v) { var v2 (v); swapWith (v2); return *this; }
#if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS
var::var (var&& other) noexcept
@@ -542,16 +331,6 @@ var::var (String&& v) : type (&VariantType_String::instance)
new (value.stringValue) String (static_cast (v));
}
-var::var (MemoryBlock&& v) : type (&VariantType_Binary::instance)
-{
- value.binaryValue = new MemoryBlock (static_cast (v));
-}
-
-var::var (Array&& v) : type (&VariantType_Array::instance)
-{
- value.objectValue = new VariantType_Array::RefCountedArray (static_cast&&> (v));
-}
-
var& var::operator= (String&& v)
{
type->cleanUp (value);
@@ -589,207 +368,3 @@ var var::clone() const noexcept
{
return type->clone (*this);
}
-
-//==============================================================================
-const var& var::operator[] (const Identifier& propertyName) const
-{
- if (DynamicObject* const o = getDynamicObject())
- return o->getProperty (propertyName);
-
- return getNullVarRef();
-}
-
-const var& var::operator[] (const char* const propertyName) const
-{
- return operator[] (Identifier (propertyName));
-}
-
-var var::getProperty (const Identifier& propertyName, const var& defaultReturnValue) const
-{
- if (DynamicObject* const o = getDynamicObject())
- return o->getProperties().getWithDefault (propertyName, defaultReturnValue);
-
- return defaultReturnValue;
-}
-
-var::NativeFunction var::getNativeFunction() const
-{
- return isMethod() && (value.methodValue != nullptr) ? *value.methodValue : nullptr;
-}
-
-var var::invoke (const Identifier& method, const var* arguments, int numArguments) const
-{
- if (DynamicObject* const o = getDynamicObject())
- return o->invokeMethod (method, var::NativeFunctionArgs (*this, arguments, numArguments));
-
- return var();
-}
-
-var var::call (const Identifier& method) const
-{
- return invoke (method, nullptr, 0);
-}
-
-var var::call (const Identifier& method, const var& arg1) const
-{
- return invoke (method, &arg1, 1);
-}
-
-var var::call (const Identifier& method, const var& arg1, const var& arg2) const
-{
- var args[] = { arg1, arg2 };
- return invoke (method, args, 2);
-}
-
-var var::call (const Identifier& method, const var& arg1, const var& arg2, const var& arg3)
-{
- var args[] = { arg1, arg2, arg3 };
- return invoke (method, args, 3);
-}
-
-var var::call (const Identifier& method, const var& arg1, const var& arg2, const var& arg3, const var& arg4) const
-{
- var args[] = { arg1, arg2, arg3, arg4 };
- return invoke (method, args, 4);
-}
-
-var var::call (const Identifier& method, const var& arg1, const var& arg2, const var& arg3, const var& arg4, const var& arg5) const
-{
- var args[] = { arg1, arg2, arg3, arg4, arg5 };
- return invoke (method, args, 5);
-}
-
-//==============================================================================
-int var::size() const
-{
- if (const Array* const array = getArray())
- return array->size();
-
- return 0;
-}
-
-const var& var::operator[] (int arrayIndex) const
-{
- const Array* const array = getArray();
-
- // When using this method, the var must actually be an array, and the index
- // must be in-range!
- jassert (array != nullptr && isPositiveAndBelow (arrayIndex, array->size()));
-
- return array->getReference (arrayIndex);
-}
-
-var& var::operator[] (int arrayIndex)
-{
- const Array* const array = getArray();
-
- // When using this method, the var must actually be an array, and the index
- // must be in-range!
- jassert (array != nullptr && isPositiveAndBelow (arrayIndex, array->size()));
-
- return array->getReference (arrayIndex);
-}
-
-Array* var::convertToArray()
-{
- if (Array* array = getArray())
- return array;
-
- Array tempVar;
- if (! isVoid())
- tempVar.add (*this);
-
- *this = tempVar;
- return getArray();
-}
-
-void var::append (const var& n)
-{
- convertToArray()->add (n);
-}
-
-void var::remove (const int index)
-{
- if (Array* const array = getArray())
- array->remove (index);
-}
-
-void var::insert (const int index, const var& n)
-{
- convertToArray()->insert (index, n);
-}
-
-void var::resize (const int numArrayElementsWanted)
-{
- convertToArray()->resize (numArrayElementsWanted);
-}
-
-int var::indexOf (const var& n) const
-{
- if (const Array* const array = getArray())
- return array->indexOf (n);
-
- return -1;
-}
-
-//==============================================================================
-void var::writeToStream (OutputStream& output) const
-{
- type->writeToStream (value, output);
-}
-
-var var::readFromStream (InputStream& input)
-{
- const int numBytes = input.readCompressedInt();
-
- if (numBytes > 0)
- {
- switch (input.readByte())
- {
- case varMarker_Int: return var (input.readInt());
- case varMarker_Int64: return var (input.readInt64());
- case varMarker_BoolTrue: return var (true);
- case varMarker_BoolFalse: return var (false);
- case varMarker_Double: return var (input.readDouble());
- case varMarker_String:
- {
- MemoryOutputStream mo;
- mo.writeFromInputStream (input, numBytes - 1);
- return var (mo.toUTF8());
- }
-
- case varMarker_Binary:
- {
- MemoryBlock mb ((size_t) numBytes - 1);
-
- if (numBytes > 1)
- {
- const int numRead = input.read (mb.getData(), numBytes - 1);
- mb.setSize ((size_t) numRead);
- }
-
- return var (mb);
- }
-
- case varMarker_Array:
- {
- var v;
- Array* const destArray = v.convertToArray();
-
- for (int i = input.readCompressedInt(); --i >= 0;)
- destArray->add (readFromStream (input));
-
- return v;
- }
-
- default:
- input.skipNextBytes (numBytes - 1); break;
- }
- }
-
- return var();
-}
-
-var::NativeFunctionArgs::NativeFunctionArgs (const var& t, const var* args, int numArgs) noexcept
- : thisObject (t), arguments (args), numArguments (numArgs)
-{}
diff --git a/source/modules/water/containers/juce_Variant.h b/source/modules/water/containers/juce_Variant.h
index ec1e1c341..34e348cad 100644
--- a/source/modules/water/containers/juce_Variant.h
+++ b/source/modules/water/containers/juce_Variant.h
@@ -48,27 +48,6 @@
class JUCE_API var
{
public:
- //==============================================================================
- /** This structure is passed to a NativeFunction callback, and contains invocation
- details about the function's arguments and context.
- */
- struct NativeFunctionArgs
- {
- NativeFunctionArgs (const var& thisObject, const var* args, int numArgs) noexcept;
-
- const var& thisObject;
- const var* arguments;
- int numArguments;
-
- JUCE_DECLARE_NON_COPYABLE (NativeFunctionArgs)
- };
-
- #if JUCE_COMPILER_SUPPORTS_LAMBDAS
- using NativeFunction = std::function;
- #else
- typedef var (*NativeFunction) (const NativeFunctionArgs&);
- #endif
-
//==============================================================================
/** Creates a void variant. */
var() noexcept;
@@ -82,14 +61,7 @@ public:
var (bool value) noexcept;
var (double value) noexcept;
var (const char* value);
- var (const wchar_t* value);
var (const String& value);
- var (const Array& value);
- var (const StringArray& value);
- var (ReferenceCountedObject* object);
- var (NativeFunction method) noexcept;
- var (const void* binaryData, size_t dataSize);
- var (const MemoryBlock& binaryData);
var& operator= (const var& valueToCopy);
var& operator= (int value);
@@ -97,18 +69,11 @@ public:
var& operator= (bool value);
var& operator= (double value);
var& operator= (const char* value);
- var& operator= (const wchar_t* value);
var& operator= (const String& value);
- var& operator= (const MemoryBlock& value);
- var& operator= (const Array& value);
- var& operator= (ReferenceCountedObject* object);
- var& operator= (NativeFunction method);
#if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS
var (var&&) noexcept;
var (String&&);
- var (MemoryBlock&&);
- var (Array&&);
var& operator= (var&&) noexcept;
var& operator= (String&&);
#endif
@@ -127,25 +92,6 @@ public:
operator String() const;
String toString() const;
- /** If this variant holds an array, this provides access to it.
- NOTE: Beware when you use this - the array pointer is only valid for the lifetime
- of the variant that returned it, so be very careful not to call this method on temporary
- var objects that are the return-value of a function, and which may go out of scope before
- you use the array!
- */
- Array* getArray() const noexcept;
-
- /** If this variant holds a memory block, this provides access to it.
- NOTE: Beware when you use this - the MemoryBlock pointer is only valid for the lifetime
- of the variant that returned it, so be very careful not to call this method on temporary
- var objects that are the return-value of a function, and which may go out of scope before
- you use the MemoryBlock!
- */
- MemoryBlock* getBinaryData() const noexcept;
-
- ReferenceCountedObject* getObject() const noexcept;
- DynamicObject* getDynamicObject() const noexcept;
-
//==============================================================================
bool isVoid() const noexcept;
bool isUndefined() const noexcept;
@@ -154,10 +100,6 @@ public:
bool isBool() const noexcept;
bool isDouble() const noexcept;
bool isString() const noexcept;
- bool isObject() const noexcept;
- bool isArray() const noexcept;
- bool isBinaryData() const noexcept;
- bool isMethod() const noexcept;
/** Returns true if this var has the same value as the one supplied.
Note that this ignores the type, so a string var "123" and an integer var with the
@@ -181,109 +123,6 @@ public:
*/
var clone() const noexcept;
- //==============================================================================
- /** If the var is an array, this returns the number of elements.
- If the var isn't actually an array, this will return 0.
- */
- int size() const;
-
- /** If the var is an array, this can be used to return one of its elements.
- To call this method, you must make sure that the var is actually an array, and
- that the index is a valid number. If these conditions aren't met, behaviour is
- undefined.
- For more control over the array's contents, you can call getArray() and manipulate
- it directly as an Array\.
- */
- const var& operator[] (int arrayIndex) const;
-
- /** If the var is an array, this can be used to return one of its elements.
- To call this method, you must make sure that the var is actually an array, and
- that the index is a valid number. If these conditions aren't met, behaviour is
- undefined.
- For more control over the array's contents, you can call getArray() and manipulate
- it directly as an Array\.
- */
- var& operator[] (int arrayIndex);
-
- /** Appends an element to the var, converting it to an array if it isn't already one.
- If the var isn't an array, it will be converted to one, and if its value was non-void,
- this value will be kept as the first element of the new array. The parameter value
- will then be appended to it.
- For more control over the array's contents, you can call getArray() and manipulate
- it directly as an Array\.
- */
- void append (const var& valueToAppend);
-
- /** Inserts an element to the var, converting it to an array if it isn't already one.
- If the var isn't an array, it will be converted to one, and if its value was non-void,
- this value will be kept as the first element of the new array. The parameter value
- will then be inserted into it.
- For more control over the array's contents, you can call getArray() and manipulate
- it directly as an Array\.
- */
- void insert (int index, const var& value);
-
- /** If the var is an array, this removes one of its elements.
- If the index is out-of-range or the var isn't an array, nothing will be done.
- For more control over the array's contents, you can call getArray() and manipulate
- it directly as an Array\.
- */
- void remove (int index);
-
- /** Treating the var as an array, this resizes it to contain the specified number of elements.
- If the var isn't an array, it will be converted to one, and if its value was non-void,
- this value will be kept as the first element of the new array before resizing.
- For more control over the array's contents, you can call getArray() and manipulate
- it directly as an Array\.
- */
- void resize (int numArrayElementsWanted);
-
- /** If the var is an array, this searches it for the first occurrence of the specified value,
- and returns its index.
- If the var isn't an array, or if the value isn't found, this returns -1.
- */
- int indexOf (const var& value) const;
-
- //==============================================================================
- /** If this variant is an object, this returns one of its properties. */
- const var& operator[] (const Identifier& propertyName) const;
- /** If this variant is an object, this returns one of its properties. */
- const var& operator[] (const char* propertyName) const;
- /** If this variant is an object, this returns one of its properties, or a default
- fallback value if the property is not set. */
- var getProperty (const Identifier& propertyName, const var& defaultReturnValue) const;
-
- /** Invokes a named method call with no arguments. */
- var call (const Identifier& method) const;
- /** Invokes a named method call with one argument. */
- var call (const Identifier& method, const var& arg1) const;
- /** Invokes a named method call with 2 arguments. */
- var call (const Identifier& method, const var& arg1, const var& arg2) const;
- /** Invokes a named method call with 3 arguments. */
- var call (const Identifier& method, const var& arg1, const var& arg2, const var& arg3);
- /** Invokes a named method call with 4 arguments. */
- var call (const Identifier& method, const var& arg1, const var& arg2, const var& arg3, const var& arg4) const;
- /** Invokes a named method call with 5 arguments. */
- var call (const Identifier& method, const var& arg1, const var& arg2, const var& arg3, const var& arg4, const var& arg5) const;
- /** Invokes a named method call with a list of arguments. */
- var invoke (const Identifier& method, const var* arguments, int numArguments) const;
- /** If this object is a method, this returns the function pointer. */
- NativeFunction getNativeFunction() const;
-
- //==============================================================================
- /** Writes a binary representation of this value to a stream.
- The data can be read back later using readFromStream().
- @see JSON
- */
- void writeToStream (OutputStream& output) const;
-
- /** Reads back a stored binary representation of a value.
- The data in the stream must have been written using writeToStream(), or this
- will have unpredictable results.
- @see JSON
- */
- static var readFromStream (InputStream& input);
-
private:
//==============================================================================
class VariantType; friend class VariantType;
@@ -294,10 +133,6 @@ private:
class VariantType_Double; friend class VariantType_Double;
class VariantType_Bool; friend class VariantType_Bool;
class VariantType_String; friend class VariantType_String;
- class VariantType_Object; friend class VariantType_Object;
- class VariantType_Array; friend class VariantType_Array;
- class VariantType_Binary; friend class VariantType_Binary;
- class VariantType_Method; friend class VariantType_Method;
union ValueUnion
{
@@ -306,9 +141,6 @@ private:
bool boolValue;
double doubleValue;
char stringValue [sizeof (String)];
- ReferenceCountedObject* objectValue;
- MemoryBlock* binaryValue;
- NativeFunction* methodValue;
};
const VariantType* type;
diff --git a/source/modules/water/processors/juce_AudioProcessor.h b/source/modules/water/processors/juce_AudioProcessor.h
index f619127c8..b507a8c14 100644
--- a/source/modules/water/processors/juce_AudioProcessor.h
+++ b/source/modules/water/processors/juce_AudioProcessor.h
@@ -246,9 +246,6 @@ public:
*/
void setLatencySamples (int newLatency);
- /** Returns the length of the filter's tail, in seconds. */
- virtual double getTailLengthSeconds() const = 0;
-
/** Returns true if the processor wants midi messages. */
virtual bool acceptsMidi() const = 0;
@@ -261,6 +258,9 @@ public:
/** Returns true if this is a midi effect plug-in and does no audio processing. */
virtual bool isMidiEffect() const { return false; }
+ virtual const String getInputChannelName (int) const { return String(); }
+ virtual const String getOutputChannelName (int) const { return String(); }
+
//==============================================================================
/** This returns a critical section that will automatically be locked while the host
is calling the processBlock() method.
diff --git a/source/modules/water/processors/juce_AudioProcessorGraph.cpp b/source/modules/water/processors/juce_AudioProcessorGraph.cpp
index c645e3845..9fd08d584 100644
--- a/source/modules/water/processors/juce_AudioProcessorGraph.cpp
+++ b/source/modules/water/processors/juce_AudioProcessorGraph.cpp
@@ -1390,7 +1390,6 @@ void AudioProcessorGraph::processAudio (AudioSampleBuffer& buffer, MidiBuffer& m
midiMessages.addEvents (currentMidiOutputBuffer, 0, buffer.getNumSamples(), 0);
}
-double AudioProcessorGraph::getTailLengthSeconds() const { return 0; }
bool AudioProcessorGraph::acceptsMidi() const { return true; }
bool AudioProcessorGraph::producesMidi() const { return true; }
@@ -1516,11 +1515,6 @@ void AudioProcessorGraph::AudioGraphIOProcessor::processBlock (AudioSampleBuffer
processAudio (buffer, midiMessages);
}
-double AudioProcessorGraph::AudioGraphIOProcessor::getTailLengthSeconds() const
-{
- return 0;
-}
-
bool AudioProcessorGraph::AudioGraphIOProcessor::acceptsMidi() const
{
return type == midiOutputNode;
diff --git a/source/modules/water/processors/juce_AudioProcessorGraph.h b/source/modules/water/processors/juce_AudioProcessorGraph.h
index 414ca55fb..1163813ab 100644
--- a/source/modules/water/processors/juce_AudioProcessorGraph.h
+++ b/source/modules/water/processors/juce_AudioProcessorGraph.h
@@ -319,7 +319,6 @@ public:
void releaseResources() override;
void processBlock (AudioSampleBuffer&, MidiBuffer&) override;
- double getTailLengthSeconds() const override;
bool acceptsMidi() const override;
bool producesMidi() const override;
@@ -346,7 +345,6 @@ public:
void setNonRealtime (bool) noexcept override;
void setPlayHead (AudioPlayHead*) override;
- double getTailLengthSeconds() const override;
bool acceptsMidi() const override;
bool producesMidi() const override;
diff --git a/source/modules/water/water.cpp b/source/modules/water/water.cpp
index 40e2bf68f..d0e567a97 100644
--- a/source/modules/water/water.cpp
+++ b/source/modules/water/water.cpp
@@ -101,8 +101,8 @@ static int64 juce_fileSetPosition (void* handle, int64 pos)
#include "text/juce_CharacterFunctions.cpp"
#include "text/juce_String.cpp"
-//#include "containers/juce_NamedValueSet.cpp"
-//#include "containers/juce_Variant.cpp"
+#include "containers/juce_NamedValueSet.cpp"
+#include "containers/juce_Variant.cpp"
#include "files/juce_DirectoryIterator.cpp"
#include "files/juce_File.cpp"
@@ -115,7 +115,7 @@ static int64 juce_fileSetPosition (void* handle, int64 pos)
#include "misc/juce_Result.cpp"
#include "processors/juce_AudioProcessor.cpp"
-//#include "processors/juce_AudioProcessorGraph.cpp"
+#include "processors/juce_AudioProcessorGraph.cpp"
#include "streams/juce_FileInputSource.cpp"
#include "streams/juce_FileInputStream.cpp"
diff --git a/source/utils/CarlaBackendUtils.hpp b/source/utils/CarlaBackendUtils.hpp
index 5ddc226d5..483d20aae 100644
--- a/source/utils/CarlaBackendUtils.hpp
+++ b/source/utils/CarlaBackendUtils.hpp
@@ -358,6 +358,8 @@ const char* EngineProcessMode2Str(const EngineProcessMode mode) noexcept
return "ENGINE_PROCESS_MODE_MULTIPLE_CLIENTS";
case ENGINE_PROCESS_MODE_CONTINUOUS_RACK:
return "ENGINE_PROCESS_MODE_CONTINUOUS_RACK";
+ case ENGINE_PROCESS_MODE_PATCHBAY:
+ return "ENGINE_PROCESS_MODE_PATCHBAY";
case ENGINE_PROCESS_MODE_BRIDGE:
return "ENGINE_PROCESS_MODE_BRIDGE";
}
diff --git a/source/utils/CarlaEngineUtils.hpp b/source/utils/CarlaEngineUtils.hpp
index 0b4d67546..783f7bb07 100644
--- a/source/utils/CarlaEngineUtils.hpp
+++ b/source/utils/CarlaEngineUtils.hpp
@@ -20,6 +20,9 @@
#include "CarlaEngine.hpp"
#include "CarlaUtils.hpp"
+#include "CarlaMIDI.h"
+
+#include "water/water.h"
CARLA_BACKEND_START_NAMESPACE
@@ -110,6 +113,94 @@ const char* EngineControlEventType2Str(const EngineControlEventType type) noexce
return nullptr;
}
+// -----------------------------------------------------------------------
+
+static inline
+void fillEngineEventsFromWaterMidiBuffer(EngineEvent engineEvents[kMaxEngineEventInternalCount], const water::MidiBuffer& midiBuffer)
+{
+ const uint8_t* midiData;
+ int numBytes, sampleNumber;
+ ushort engineEventIndex = 0;
+
+ for (ushort i=0; i < kMaxEngineEventInternalCount; ++i)
+ {
+ const EngineEvent& engineEvent(engineEvents[i]);
+
+ if (engineEvent.type != kEngineEventTypeNull)
+ continue;
+
+ engineEventIndex = i;
+ break;
+ }
+
+ for (water::MidiBuffer::Iterator midiBufferIterator(midiBuffer); midiBufferIterator.getNextEvent(midiData, numBytes, sampleNumber) && engineEventIndex < kMaxEngineEventInternalCount;)
+ {
+ CARLA_SAFE_ASSERT_CONTINUE(numBytes > 0);
+ CARLA_SAFE_ASSERT_CONTINUE(sampleNumber >= 0);
+ CARLA_SAFE_ASSERT_CONTINUE(numBytes < 0xFF /* uint8_t max */);
+
+ EngineEvent& engineEvent(engineEvents[engineEventIndex++]);
+
+ engineEvent.time = static_cast(sampleNumber);
+ engineEvent.fillFromMidiData(static_cast(numBytes), midiData, 0);
+ }
+}
+
+// -----------------------------------------------------------------------
+
+static inline
+void fillWaterMidiBufferFromEngineEvents(water::MidiBuffer& midiBuffer, const EngineEvent engineEvents[kMaxEngineEventInternalCount])
+{
+ uint8_t size = 0;
+ uint8_t mdata[3] = { 0, 0, 0 };
+ const uint8_t* mdataPtr = mdata;
+ uint8_t mdataTmp[EngineMidiEvent::kDataSize];
+
+ for (ushort i=0; i < kMaxEngineEventInternalCount; ++i)
+ {
+ const EngineEvent& engineEvent(engineEvents[i]);
+
+ if (engineEvent.type == kEngineEventTypeNull)
+ {
+ break;
+ }
+ else if (engineEvent.type == kEngineEventTypeControl)
+ {
+ const EngineControlEvent& ctrlEvent(engineEvent.ctrl);
+
+ ctrlEvent.convertToMidiData(engineEvent.channel, size, mdata);
+ mdataPtr = mdata;
+ }
+ else if (engineEvent.type == kEngineEventTypeMidi)
+ {
+ const EngineMidiEvent& midiEvent(engineEvent.midi);
+
+ size = midiEvent.size;
+
+ if (size > EngineMidiEvent::kDataSize && midiEvent.dataExt != nullptr)
+ {
+ mdataPtr = midiEvent.dataExt;
+ }
+ else
+ {
+ // copy
+ carla_copy(mdataTmp, midiEvent.data, size);
+ // add channel
+ mdataTmp[0] = static_cast(mdataTmp[0] | (engineEvent.channel & MIDI_CHANNEL_BIT));
+ // done
+ mdataPtr = mdataTmp;
+ }
+ }
+ else
+ {
+ continue;
+ }
+
+ if (size > 0)
+ midiBuffer.addEvent(mdataPtr, static_cast(size), static_cast(engineEvent.time));
+ }
+}
+
// -------------------------------------------------------------------
// Helper classes