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