@@ -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 | |||
// ------------------------------------------------------------------- | |||
@@ -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 <Patchbay>\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 << " <Connection>\n"; | |||
outPatchbay << " <Source>" << xmlSafeString(connSource, true) << "</Source>\n"; | |||
outPatchbay << " <Target>" << xmlSafeString(connTarget, true) << "</Target>\n"; | |||
outPatchbay << " </Connection>\n"; | |||
} | |||
outPatchbay << " </Patchbay>\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 <Patchbay>\n"; | |||
outPatchbay << "\n <ExternalPatchbay>\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 << " </Connection>\n"; | |||
} | |||
outPatchbay << " </Patchbay>\n"; | |||
outPatchbay << " </ExternalPatchbay>\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; | |||
} | |||
@@ -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), | |||
@@ -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 | |||
@@ -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]; | |||
@@ -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; | |||
@@ -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; | |||
@@ -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<NativePluginHints>(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<NativePluginSupports>(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<NativePluginHints>(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<NativePluginSupports>(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<NativePluginHints>(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<NativePluginSupports>(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<NativePluginHints>(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<NativePluginSupports>(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 | |||
@@ -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 | |||
@@ -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<PatchbayGraph>(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<class Graph> | |||
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<RackGraph>(pData->graph.getRackGraph(), true); | |||
pData->graph.setUsingExternal(external); | |||
if (external) | |||
return refreshExternalGraphPorts<PatchbayGraph>(pData->graph.getPatchbayGraph(), true); | |||
return CarlaEngine::patchbayRefresh(false); | |||
} | |||
// ------------------------------------------------------------------- | |||
protected: | |||
@@ -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()); | |||
} | |||
} | |||
} |
@@ -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<var>* 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<String*> (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<var>* toArray (const ValueUnion& data) const noexcept override | |||
{ | |||
if (RefCountedArray* a = dynamic_cast<RefCountedArray*> (data.objectValue)) | |||
return &(a->array); | |||
return nullptr; | |||
} | |||
bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept override | |||
{ | |||
const Array<var>* const thisArray = toArray (data); | |||
const Array<var>* const otherArray = otherType.toArray (otherData); | |||
return thisArray == otherArray || (thisArray != nullptr && otherArray != nullptr && *otherArray == *thisArray); | |||
} | |||
var clone (const var& original) const override | |||
{ | |||
Array<var> arrayCopy; | |||
if (const Array<var>* 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<var>* 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<var>& a) : array (a) { incReferenceCount(); } | |||
#if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS | |||
RefCountedArray (Array<var>&& a) : array (static_cast<Array<var>&&> (a)) { incReferenceCount(); } | |||
#endif | |||
Array<var> 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<var>& 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<var> 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>* 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<DynamicObject*> (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<var>& 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<String&&> (v)); | |||
} | |||
var::var (MemoryBlock&& v) : type (&VariantType_Binary::instance) | |||
{ | |||
value.binaryValue = new MemoryBlock (static_cast<MemoryBlock&&> (v)); | |||
} | |||
var::var (Array<var>&& v) : type (&VariantType_Array::instance) | |||
{ | |||
value.objectValue = new VariantType_Array::RefCountedArray (static_cast<Array<var>&&> (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<var>* const array = getArray()) | |||
return array->size(); | |||
return 0; | |||
} | |||
const var& var::operator[] (int arrayIndex) const | |||
{ | |||
const Array<var>* 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<var>* 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>* var::convertToArray() | |||
{ | |||
if (Array<var>* array = getArray()) | |||
return array; | |||
Array<var> 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<var>* 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<var>* 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<var>* 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) | |||
{} |
@@ -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<var (const NativeFunctionArgs&)>; | |||
#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<var>& 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<var>& 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>&&); | |||
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<var>* 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\<var\>. | |||
*/ | |||
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\>. | |||
*/ | |||
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\<var\>. | |||
*/ | |||
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\<var\>. | |||
*/ | |||
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\<var\>. | |||
*/ | |||
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\<var\>. | |||
*/ | |||
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; | |||
@@ -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. | |||
@@ -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; | |||
@@ -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; | |||
@@ -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" | |||
@@ -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"; | |||
} | |||
@@ -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<uint32_t>(sampleNumber); | |||
engineEvent.fillFromMidiData(static_cast<uint8_t>(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<uint8_t>(mdataTmp, midiEvent.data, size); | |||
// add channel | |||
mdataTmp[0] = static_cast<uint8_t>(mdataTmp[0] | (engineEvent.channel & MIDI_CHANNEL_BIT)); | |||
// done | |||
mdataPtr = mdataTmp; | |||
} | |||
} | |||
else | |||
{ | |||
continue; | |||
} | |||
if (size > 0) | |||
midiBuffer.addEvent(mdataPtr, static_cast<int>(size), static_cast<int>(engineEvent.time)); | |||
} | |||
} | |||
// ------------------------------------------------------------------- | |||
// Helper classes | |||