| @@ -1150,7 +1150,7 @@ protected: | |||||
| * Do not free returned data. | * Do not free returned data. | ||||
| */ | */ | ||||
| virtual const char* const* getPatchbayConnections(const bool external) const; | 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 | #endif | ||||
| // ------------------------------------------------------------------- | // ------------------------------------------------------------------- | ||||
| @@ -604,6 +604,19 @@ bool CarlaEngine::addPlugin(const BinaryType btype, const PluginType ptype, | |||||
| canRun = false; | 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) | if (! canRun) | ||||
| { | { | ||||
| @@ -630,6 +643,9 @@ bool CarlaEngine::addPlugin(const BinaryType btype, const PluginType ptype, | |||||
| const ScopedThreadStopper sts(this); | 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 bool wasActive = oldPlugin->getInternalParameterValue(PARAMETER_ACTIVE) >= 0.5f; | ||||
| const float oldDryWet = oldPlugin->getInternalParameterValue(PARAMETER_DRYWET); | const float oldDryWet = oldPlugin->getInternalParameterValue(PARAMETER_DRYWET); | ||||
| const float oldVolume = oldPlugin->getInternalParameterValue(PARAMETER_VOLUME); | const float oldVolume = oldPlugin->getInternalParameterValue(PARAMETER_VOLUME); | ||||
| @@ -655,6 +671,11 @@ bool CarlaEngine::addPlugin(const BinaryType btype, const PluginType ptype, | |||||
| ++pData->curPluginCount; | ++pData->curPluginCount; | ||||
| callback(ENGINE_CALLBACK_PLUGIN_ADDED, id, 0, 0, 0.0f, plugin->getName()); | 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; | return true; | ||||
| @@ -684,6 +705,9 @@ bool CarlaEngine::removePlugin(const uint id) | |||||
| const ScopedThreadStopper sts(this); | const ScopedThreadStopper sts(this); | ||||
| #ifndef BUILD_BRIDGE | #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 bool lockWait(isRunning() /*&& pData->options.processMode != ENGINE_PROCESS_MODE_MULTIPLE_CLIENTS*/); | ||||
| const ScopedActionLock sal(this, kEnginePostActionRemovePlugin, id, 0, lockWait); | const ScopedActionLock sal(this, kEnginePostActionRemovePlugin, id, 0, lockWait); | ||||
| @@ -728,12 +752,17 @@ bool CarlaEngine::removeAllPlugins() | |||||
| const uint curPluginCount(pData->curPluginCount); | 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()) | if (isOscControlRegistered()) | ||||
| { | { | ||||
| for (uint i=0; i < curPluginCount; ++i) | for (uint i=0; i < curPluginCount; ++i) | ||||
| oscSend_control_remove_plugin(curPluginCount-i-1); | oscSend_control_remove_plugin(curPluginCount-i-1); | ||||
| } | } | ||||
| # endif | |||||
| #endif | #endif | ||||
| const bool lockWait(isRunning()); | const bool lockWait(isRunning()); | ||||
| @@ -782,6 +811,9 @@ const char* CarlaEngine::renamePlugin(const uint id, const char* const newName) | |||||
| plugin->setName(uniqueName); | plugin->setName(uniqueName); | ||||
| if (pData->options.processMode == ENGINE_PROCESS_MODE_PATCHBAY) | |||||
| pData->graph.renamePlugin(plugin, uniqueName); | |||||
| delete[] uniqueName; | delete[] uniqueName; | ||||
| return plugin->getName(); | return plugin->getName(); | ||||
| } | } | ||||
| @@ -867,6 +899,9 @@ bool CarlaEngine::switchPlugins(const uint idA, const uint idB) noexcept | |||||
| const ScopedThreadStopper sts(this); | 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 bool lockWait(isRunning() /*&& pData->options.processMode != ENGINE_PROCESS_MODE_MULTIPLE_CLIENTS*/); | ||||
| const ScopedActionLock sal(this, kEnginePostActionSwitchPlugins, idA, idB, lockWait); | 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); | carla_debug("CarlaEngine::bufferSizeChanged(%i)", newBufferSize); | ||||
| #ifndef BUILD_BRIDGE | #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); | pData->graph.setBufferSize(newBufferSize); | ||||
| } | |||||
| #endif | #endif | ||||
| pData->time.updateAudioValues(newBufferSize, pData->sampleRate); | pData->time.updateAudioValues(newBufferSize, pData->sampleRate); | ||||
| @@ -1631,6 +1669,14 @@ void CarlaEngine::sampleRateChanged(const double newSampleRate) | |||||
| { | { | ||||
| carla_debug("CarlaEngine::sampleRateChanged(%g)", 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); | pData->time.updateAudioValues(pData->bufferSize, newSampleRate); | ||||
| for (uint i=0; i < pData->curPluginCount; ++i) | 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)); | carla_debug("CarlaEngine::offlineModeChanged(%s)", bool2str(isOfflineNow)); | ||||
| #ifndef BUILD_BRIDGE | #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); | pData->graph.setOffline(isOfflineNow); | ||||
| } | |||||
| #endif | #endif | ||||
| for (uint i=0; i < pData->curPluginCount; ++i) | 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); | 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 | // if we're running inside some session-manager (and using JACK), let them handle the connections | ||||
| bool saveConnections; | |||||
| bool saveExternalConnections; | |||||
| /**/ if (isPlugin) | /**/ if (isPlugin) | ||||
| saveConnections = false; | |||||
| saveExternalConnections = false; | |||||
| else if (std::strcmp(getCurrentDriverName(), "JACK") != 0) | else if (std::strcmp(getCurrentDriverName(), "JACK") != 0) | ||||
| saveConnections = true; | |||||
| saveExternalConnections = true; | |||||
| else if (std::getenv("CARLA_DONT_MANAGE_CONNECTIONS") != nullptr) | else if (std::getenv("CARLA_DONT_MANAGE_CONNECTIONS") != nullptr) | ||||
| saveConnections = false; | |||||
| saveExternalConnections = false; | |||||
| else if (std::getenv("LADISH_APP_NAME") != nullptr) | else if (std::getenv("LADISH_APP_NAME") != nullptr) | ||||
| saveConnections = false; | |||||
| saveExternalConnections = false; | |||||
| else if (std::getenv("NSM_URL") != nullptr) | else if (std::getenv("NSM_URL") != nullptr) | ||||
| saveConnections = false; | |||||
| saveExternalConnections = false; | |||||
| else | 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); | MemoryOutputStream outPatchbay(2048); | ||||
| outPatchbay << "\n <Patchbay>\n"; | |||||
| outPatchbay << "\n <ExternalPatchbay>\n"; | |||||
| for (int i=0; patchbayConns[i] != nullptr && patchbayConns[i+1] != nullptr; ++i, ++i ) | 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 << " </Connection>\n"; | ||||
| } | } | ||||
| outPatchbay << " </Patchbay>\n"; | |||||
| outPatchbay << " </ExternalPatchbay>\n"; | |||||
| outStream << outPatchbay; | outStream << outPatchbay; | ||||
| } | } | ||||
| } | } | ||||
| @@ -2132,6 +2209,11 @@ bool CarlaEngine::loadProjectInternal(water::XmlDocument& xmlDoc) | |||||
| plugin->setEnabled(true); | plugin->setEnabled(true); | ||||
| callback(ENGINE_CALLBACK_PLUGIN_ADDED, pluginId, 0, 0, 0.0f, plugin->getName()); | 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 | else | ||||
| { | { | ||||
| @@ -2163,30 +2245,79 @@ bool CarlaEngine::loadProjectInternal(water::XmlDocument& xmlDoc) | |||||
| if (pData->aboutToClose) | if (pData->aboutToClose) | ||||
| return true; | 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) | /**/ if (isPlugin) | ||||
| loadConnections = false; | |||||
| loadExternalConnections = false; | |||||
| else if (std::strcmp(getCurrentDriverName(), "JACK") != 0) | else if (std::strcmp(getCurrentDriverName(), "JACK") != 0) | ||||
| loadConnections = true; | |||||
| loadExternalConnections = true; | |||||
| else if (std::getenv("CARLA_DONT_MANAGE_CONNECTIONS") != nullptr) | else if (std::getenv("CARLA_DONT_MANAGE_CONNECTIONS") != nullptr) | ||||
| loadConnections = false; | |||||
| loadExternalConnections = false; | |||||
| else if (std::getenv("LADISH_APP_NAME") != nullptr) | else if (std::getenv("LADISH_APP_NAME") != nullptr) | ||||
| loadConnections = false; | |||||
| loadExternalConnections = false; | |||||
| else if (std::getenv("NSM_URL") != nullptr) | else if (std::getenv("NSM_URL") != nullptr) | ||||
| loadConnections = false; | |||||
| loadExternalConnections = false; | |||||
| else | else | ||||
| loadConnections = true; | |||||
| loadExternalConnections = true; | |||||
| // handle connections | // handle connections | ||||
| if (loadConnections) | |||||
| if (loadExternalConnections) | |||||
| { | { | ||||
| const bool isUsingExternal(pData->graph.isUsingExternal()); | |||||
| for (XmlElement* elem = xmlElement->getFirstChildElement(); elem != nullptr; elem = elem->getNextElement()) | for (XmlElement* elem = xmlElement->getFirstChildElement(); elem != nullptr; elem = elem->getNextElement()) | ||||
| { | { | ||||
| const String& tagName(elem->getTagName()); | const String& tagName(elem->getTagName()); | ||||
| if (! tagName.equalsIgnoreCase("patchbay") && ! tagName.equalsIgnoreCase("externalpatchbay")) | |||||
| if (! tagName.equalsIgnoreCase("externalpatchbay")) | |||||
| continue; | continue; | ||||
| CarlaString sourcePort, targetPort; | CarlaString sourcePort, targetPort; | ||||
| @@ -2213,7 +2344,7 @@ bool CarlaEngine::loadProjectInternal(water::XmlDocument& xmlDoc) | |||||
| } | } | ||||
| if (sourcePort.isNotEmpty() && targetPort.isNotEmpty()) | if (sourcePort.isNotEmpty() && targetPort.isNotEmpty()) | ||||
| restorePatchbayConnection(sourcePort, targetPort); | |||||
| restorePatchbayConnection(true, sourcePort, targetPort, isUsingExternal); | |||||
| } | } | ||||
| break; | break; | ||||
| } | } | ||||
| @@ -187,7 +187,7 @@ EngineOptions::EngineOptions() noexcept | |||||
| : processMode(ENGINE_PROCESS_MODE_MULTIPLE_CLIENTS), | : processMode(ENGINE_PROCESS_MODE_MULTIPLE_CLIENTS), | ||||
| transportMode(ENGINE_TRANSPORT_MODE_JACK), | transportMode(ENGINE_TRANSPORT_MODE_JACK), | ||||
| #else | #else | ||||
| : processMode(ENGINE_PROCESS_MODE_CONTINUOUS_RACK), | |||||
| : processMode(ENGINE_PROCESS_MODE_PATCHBAY), | |||||
| transportMode(ENGINE_TRANSPORT_MODE_INTERNAL), | transportMode(ENGINE_TRANSPORT_MODE_INTERNAL), | ||||
| #endif | #endif | ||||
| transportExtra(nullptr), | transportExtra(nullptr), | ||||
| @@ -23,6 +23,12 @@ | |||||
| #include "CarlaPatchbayUtils.hpp" | #include "CarlaPatchbayUtils.hpp" | ||||
| #include "CarlaStringList.hpp" | #include "CarlaStringList.hpp" | ||||
| #include "water/water.h" | |||||
| using water::AudioProcessorGraph; | |||||
| using water::AudioSampleBuffer; | |||||
| using water::MidiBuffer; | |||||
| CARLA_BACKEND_START_NAMESPACE | CARLA_BACKEND_START_NAMESPACE | ||||
| // ----------------------------------------------------------------------- | // ----------------------------------------------------------------------- | ||||
| @@ -136,6 +142,48 @@ struct RackGraph { | |||||
| CARLA_DECLARE_NON_COPY_CLASS(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 | CARLA_BACKEND_END_NAMESPACE | ||||
| @@ -468,6 +468,13 @@ bool CarlaEngine::ProtectedData::init(const char* const clientName) | |||||
| switch (options.processMode) | 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: | case ENGINE_PROCESS_MODE_BRIDGE: | ||||
| maxPluginNumber = 1; | maxPluginNumber = 1; | ||||
| break; | break; | ||||
| @@ -479,6 +486,7 @@ bool CarlaEngine::ProtectedData::init(const char* const clientName) | |||||
| switch (options.processMode) | switch (options.processMode) | ||||
| { | { | ||||
| case ENGINE_PROCESS_MODE_CONTINUOUS_RACK: | case ENGINE_PROCESS_MODE_CONTINUOUS_RACK: | ||||
| case ENGINE_PROCESS_MODE_PATCHBAY: | |||||
| case ENGINE_PROCESS_MODE_BRIDGE: | case ENGINE_PROCESS_MODE_BRIDGE: | ||||
| events.in = new EngineEvent[kMaxEngineEventInternalCount]; | events.in = new EngineEvent[kMaxEngineEventInternalCount]; | ||||
| events.out = new EngineEvent[kMaxEngineEventInternalCount]; | events.out = new EngineEvent[kMaxEngineEventInternalCount]; | ||||
| @@ -71,19 +71,37 @@ public: | |||||
| void destroy() noexcept; | void destroy() noexcept; | ||||
| void setBufferSize(const uint32_t bufferSize); | void setBufferSize(const uint32_t bufferSize); | ||||
| void setSampleRate(const double sampleRate); | |||||
| void setOffline(const bool offline); | void setOffline(const bool offline); | ||||
| bool isReady() const noexcept; | 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); | 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 | // 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); | 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: | private: | ||||
| bool fIsRack; | |||||
| bool fIsReady; | bool fIsReady; | ||||
| RackGraph* fRack; | |||||
| union { | |||||
| RackGraph* fRack; | |||||
| PatchbayGraph* fPatchbay; | |||||
| }; | |||||
| CarlaEngine* const kEngine; | CarlaEngine* const kEngine; | ||||
| @@ -806,6 +806,7 @@ public: | |||||
| CarlaThread("CarlaEngineJackCallbacks"), | CarlaThread("CarlaEngineJackCallbacks"), | ||||
| #endif | #endif | ||||
| fClient(nullptr), | fClient(nullptr), | ||||
| fExternalPatchbay(true), | |||||
| fFreewheel(false), | fFreewheel(false), | ||||
| #ifdef BUILD_BRIDGE | #ifdef BUILD_BRIDGE | ||||
| fIsRunning(false) | fIsRunning(false) | ||||
| @@ -882,7 +883,8 @@ public: | |||||
| CARLA_SAFE_ASSERT_RETURN(jackbridge_is_ok(), false); | CARLA_SAFE_ASSERT_RETURN(jackbridge_is_ok(), false); | ||||
| carla_debug("CarlaEngineJack::init(\"%s\")", clientName); | carla_debug("CarlaEngineJack::init(\"%s\")", clientName); | ||||
| fFreewheel = false; | |||||
| fFreewheel = false; | |||||
| fExternalPatchbay = true; | |||||
| CarlaString truncatedClientName(clientName); | CarlaString truncatedClientName(clientName); | ||||
| truncatedClientName.truncate(getMaxClientNameSize()); | truncatedClientName.truncate(getMaxClientNameSize()); | ||||
| @@ -949,14 +951,16 @@ public: | |||||
| fTimebaseMaster = jackbridge_set_timebase_callback(fClient, true, carla_jack_timebase_callback, this); | 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_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_registration_callback(fClient, carla_jack_port_registration_callback, this); | ||||
| jackbridge_set_port_connect_callback(fClient, carla_jack_port_connect_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); | 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[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); | 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[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); | 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)) | if (jackbridge_activate(fClient)) | ||||
| @@ -976,8 +988,11 @@ public: | |||||
| return true; | 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->graph.destroy(); | ||||
| } | |||||
| pData->close(); | pData->close(); | ||||
| jackbridge_client_close(fClient); | jackbridge_client_close(fClient); | ||||
| @@ -1016,7 +1031,8 @@ public: | |||||
| fPostPonedEvents.clear(); | fPostPonedEvents.clear(); | ||||
| // clear rack/patchbay stuff | // 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) | if (deactivated) | ||||
| { | { | ||||
| @@ -1108,8 +1124,11 @@ public: | |||||
| #ifndef BUILD_BRIDGE | #ifndef BUILD_BRIDGE | ||||
| const char* renamePlugin(const uint id, const char* const newName) override | 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); | return CarlaEngine::renamePlugin(id, newName); | ||||
| } | |||||
| CARLA_SAFE_ASSERT_RETURN(pData->plugins != nullptr, nullptr); | CARLA_SAFE_ASSERT_RETURN(pData->plugins != nullptr, nullptr); | ||||
| CARLA_SAFE_ASSERT_RETURN(pData->curPluginCount != 0, nullptr); | CARLA_SAFE_ASSERT_RETURN(pData->curPluginCount != 0, nullptr); | ||||
| @@ -1257,6 +1276,9 @@ public: | |||||
| { | { | ||||
| CARLA_SAFE_ASSERT_RETURN(fClient != nullptr, false); | 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); | const char* const fullPortNameA = fUsedPorts.getFullPortName(groupA, portA); | ||||
| CARLA_SAFE_ASSERT_RETURN(fullPortNameA != nullptr && fullPortNameA[0] != '\0', false); | CARLA_SAFE_ASSERT_RETURN(fullPortNameA != nullptr && fullPortNameA[0] != '\0', false); | ||||
| @@ -1276,6 +1298,9 @@ public: | |||||
| { | { | ||||
| CARLA_SAFE_ASSERT_RETURN(fClient != nullptr, false); | 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 }; | ConnectionToId connectionToId = { 0, 0, 0, 0, 0 }; | ||||
| { | { | ||||
| @@ -1312,10 +1337,19 @@ public: | |||||
| return true; | return true; | ||||
| } | } | ||||
| bool patchbayRefresh() override | |||||
| bool patchbayRefresh(const bool external) override | |||||
| { | { | ||||
| CARLA_SAFE_ASSERT_RETURN(fClient != nullptr, false); | 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(); | fUsedGroups.clear(); | ||||
| fUsedPorts.clear(); | fUsedPorts.clear(); | ||||
| fUsedConnections.clear(); | fUsedConnections.clear(); | ||||
| @@ -1403,10 +1437,13 @@ public: | |||||
| // ------------------------------------------------------------------- | // ------------------------------------------------------------------- | ||||
| // Patchbay stuff | // 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_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; | CarlaStringList connList; | ||||
| @@ -1442,13 +1479,16 @@ public: | |||||
| return fRetConns; | 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(fClient != nullptr,); | ||||
| CARLA_SAFE_ASSERT_RETURN(connSource != nullptr && connSource[0] != '\0',); | CARLA_SAFE_ASSERT_RETURN(connSource != nullptr && connSource[0] != '\0',); | ||||
| CARLA_SAFE_ASSERT_RETURN(connTarget != nullptr && connTarget[0] != '\0',); | CARLA_SAFE_ASSERT_RETURN(connTarget != nullptr && connTarget[0] != '\0',); | ||||
| carla_debug("CarlaEngineJack::restorePatchbayConnection(\"%s\", \"%s\")", connSource, connTarget); | 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 (const jack_port_t* const port = jackbridge_port_by_name(fClient, connSource)) | ||||
| { | { | ||||
| if (jackbridge_port_by_name(fClient, connTarget) == nullptr) | 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.in != nullptr,); | ||||
| CARLA_SAFE_ASSERT_RETURN(pData->events.out != nullptr,); | CARLA_SAFE_ASSERT_RETURN(pData->events.out != nullptr,); | ||||
| @@ -1719,6 +1760,9 @@ protected: | |||||
| { | { | ||||
| CARLA_SAFE_ASSERT_RETURN(name != nullptr && name[0] != '\0',); | 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 | // do nothing on client registration, wait for first port | ||||
| if (reg) return; | if (reg) return; | ||||
| @@ -1736,6 +1780,9 @@ protected: | |||||
| void handleJackPortRegistrationCallback(const jack_port_id_t port, const bool reg) | 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)); | const jack_port_t* const jackPort(jackbridge_port_by_id(fClient, port)); | ||||
| CARLA_SAFE_ASSERT_RETURN(jackPort != nullptr,); | 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) | 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)); | const jack_port_t* const jackPortA(jackbridge_port_by_id(fClient, a)); | ||||
| CARLA_SAFE_ASSERT_RETURN(jackPortA != nullptr,); | 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) | 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(oldFullName != nullptr && oldFullName[0] != '\0',); | ||||
| CARLA_SAFE_ASSERT_RETURN(newFullName != nullptr && newFullName[0] != '\0',); | CARLA_SAFE_ASSERT_RETURN(newFullName != nullptr && newFullName[0] != '\0',); | ||||
| @@ -1937,6 +1990,7 @@ protected: | |||||
| private: | private: | ||||
| jack_client_t* fClient; | jack_client_t* fClient; | ||||
| bool fExternalPatchbay; | |||||
| bool fFreewheel; | bool fFreewheel; | ||||
| // ------------------------------------------------------------------- | // ------------------------------------------------------------------- | ||||
| @@ -2017,6 +2071,7 @@ private: | |||||
| void initJackPatchbay(const char* const ourName) | 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',); | CARLA_SAFE_ASSERT_RETURN(ourName != nullptr && ourName[0] != '\0',); | ||||
| CarlaStringList parsedGroups; | CarlaStringList parsedGroups; | ||||
| @@ -158,7 +158,7 @@ protected: | |||||
| else if (std::strcmp(msg, "patchbay_refresh") == 0) | else if (std::strcmp(msg, "patchbay_refresh") == 0) | ||||
| { | { | ||||
| try { | try { | ||||
| ok = fEngine->patchbayRefresh(); | |||||
| ok = fEngine->patchbayRefresh(false); | |||||
| } CARLA_SAFE_EXCEPTION("patchbayRefresh"); | } CARLA_SAFE_EXCEPTION("patchbayRefresh"); | ||||
| } | } | ||||
| else if (std::strcmp(msg, "transport_play") == 0) | else if (std::strcmp(msg, "transport_play") == 0) | ||||
| @@ -586,9 +586,10 @@ private: | |||||
| class CarlaEngineNative : public CarlaEngine | class CarlaEngineNative : public CarlaEngine | ||||
| { | { | ||||
| public: | 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(), | : CarlaEngine(), | ||||
| pHost(host), | pHost(host), | ||||
| kIsPatchbay(isPatchbay), | |||||
| fIsActive(false), | fIsActive(false), | ||||
| fIsRunning(false), | fIsRunning(false), | ||||
| fUiServer(this), | fUiServer(this), | ||||
| @@ -606,15 +607,29 @@ public: | |||||
| if (outChan == 0) | if (outChan == 0) | ||||
| outChan = inChan; | 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) | if (pData->options.resourceDir != nullptr) | ||||
| delete[] pData->options.resourceDir; | delete[] pData->options.resourceDir; | ||||
| @@ -1341,7 +1356,7 @@ protected: | |||||
| // --------------------------------------------------------------- | // --------------------------------------------------------------- | ||||
| // Do nothing if no plugins and rack mode | // Do nothing if no plugins and rack mode | ||||
| if (pData->curPluginCount == 0) | |||||
| if (pData->curPluginCount == 0 && ! kIsPatchbay) | |||||
| { | { | ||||
| if (outBuffer[0] != inBuffer[0]) | if (outBuffer[0] != inBuffer[0]) | ||||
| carla_copyFloats(outBuffer[0], inBuffer[0], frames); | 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 | // create audio buffers | ||||
| @@ -1456,7 +1479,10 @@ protected: | |||||
| CarlaString path(pHost->resourceDir); | 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 | #ifdef CARLA_OS_WIN | ||||
| path += ".exe"; | path += ".exe"; | ||||
| #endif | #endif | ||||
| @@ -1486,6 +1512,9 @@ protected: | |||||
| } | } | ||||
| } | } | ||||
| if (kIsPatchbay) | |||||
| patchbayRefresh(false); | |||||
| if (fWaitForReadyMsg) | if (fWaitForReadyMsg) | ||||
| { | { | ||||
| carla_stdout("Using Carla plugin embedded, waiting for it to be ready..."); | carla_stdout("Using Carla plugin embedded, waiting for it to be ready..."); | ||||
| @@ -1653,6 +1682,26 @@ public: | |||||
| return new CarlaEngineNative(host, false); | 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) | static void _cleanup(NativePluginHandle handle) | ||||
| { | { | ||||
| delete handlePtr; | delete handlePtr; | ||||
| @@ -1776,6 +1825,7 @@ public: | |||||
| private: | private: | ||||
| const NativeHostDescriptor* const pHost; | const NativeHostDescriptor* const pHost; | ||||
| const bool kIsPatchbay; // rack if false | |||||
| bool fIsActive, fIsRunning; | bool fIsActive, fIsRunning; | ||||
| CarlaEngineNativeUI fUiServer; | CarlaEngineNativeUI fUiServer; | ||||
| @@ -1843,6 +1893,174 @@ static const NativePluginDescriptor carlaRackDesc = { | |||||
| CarlaEngineNative::_dispatcher | 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 | CARLA_BACKEND_END_NAMESPACE | ||||
| // ----------------------------------------------------------------------- | // ----------------------------------------------------------------------- | ||||
| @@ -1854,6 +2072,10 @@ void carla_register_native_plugin_carla() | |||||
| { | { | ||||
| CARLA_BACKEND_USE_NAMESPACE; | CARLA_BACKEND_USE_NAMESPACE; | ||||
| carla_register_native_plugin(&carlaRackDesc); | 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; | 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 | // Extra stuff for linking purposes | ||||
| @@ -89,17 +89,30 @@ CarlaEngineEventPort::CarlaEngineEventPort(const CarlaEngineClient& client, cons | |||||
| kProcessMode(client.getEngine().getProccessMode()) | kProcessMode(client.getEngine().getProccessMode()) | ||||
| { | { | ||||
| carla_debug("CarlaEngineEventPort::CarlaEngineEventPort(%s)", bool2str(isInputPort)); | carla_debug("CarlaEngineEventPort::CarlaEngineEventPort(%s)", bool2str(isInputPort)); | ||||
| if (kProcessMode == ENGINE_PROCESS_MODE_PATCHBAY) | |||||
| fBuffer = new EngineEvent[kMaxEngineEventInternalCount]; | |||||
| } | } | ||||
| CarlaEngineEventPort::~CarlaEngineEventPort() noexcept | CarlaEngineEventPort::~CarlaEngineEventPort() noexcept | ||||
| { | { | ||||
| carla_debug("CarlaEngineEventPort::~CarlaEngineEventPort()"); | carla_debug("CarlaEngineEventPort::~CarlaEngineEventPort()"); | ||||
| if (kProcessMode == ENGINE_PROCESS_MODE_PATCHBAY) | |||||
| { | |||||
| CARLA_SAFE_ASSERT_RETURN(fBuffer != nullptr,); | |||||
| delete[] fBuffer; | |||||
| fBuffer = nullptr; | |||||
| } | |||||
| } | } | ||||
| void CarlaEngineEventPort::initBuffer() noexcept | void CarlaEngineEventPort::initBuffer() noexcept | ||||
| { | { | ||||
| if (kProcessMode == ENGINE_PROCESS_MODE_CONTINUOUS_RACK || kProcessMode == ENGINE_PROCESS_MODE_BRIDGE) | if (kProcessMode == ENGINE_PROCESS_MODE_CONTINUOUS_RACK || kProcessMode == ENGINE_PROCESS_MODE_BRIDGE) | ||||
| fBuffer = kClient.getEngine().getInternalEventBuffer(kIsInput); | fBuffer = kClient.getEngine().getInternalEventBuffer(kIsInput); | ||||
| else if (kProcessMode == ENGINE_PROCESS_MODE_PATCHBAY && ! kIsInput) | |||||
| carla_zeroStructs(fBuffer, kMaxEngineEventInternalCount); | |||||
| } | } | ||||
| uint32_t CarlaEngineEventPort::getEventCount() const noexcept | uint32_t CarlaEngineEventPort::getEventCount() const noexcept | ||||
| @@ -183,7 +183,7 @@ public: | |||||
| CARLA_SAFE_ASSERT_RETURN(clientName != nullptr && clientName[0] != '\0', false); | CARLA_SAFE_ASSERT_RETURN(clientName != nullptr && clientName[0] != '\0', false); | ||||
| carla_debug("CarlaEngineRtAudio::init(\"%s\")", clientName); | 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"); | setLastError("Invalid process mode"); | ||||
| return false; | return false; | ||||
| @@ -293,7 +293,10 @@ public: | |||||
| return false; | 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()); | callback(ENGINE_CALLBACK_ENGINE_STARTED, 0, pData->options.processMode, pData->options.transportMode, 0.0f, getCurrentDriverName()); | ||||
| return true; | return true; | ||||
| @@ -402,11 +405,9 @@ public: | |||||
| // ------------------------------------------------------------------- | // ------------------------------------------------------------------- | ||||
| // Patchbay | // 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); | CARLA_SAFE_ASSERT_RETURN(graph != nullptr, false); | ||||
| char strBuf[STR_MAX+1]; | char strBuf[STR_MAX+1]; | ||||
| @@ -473,7 +474,8 @@ public: | |||||
| // --------------------------------------------------------------- | // --------------------------------------------------------------- | ||||
| // now refresh | // now refresh | ||||
| graph->refresh(fDeviceName.buffer()); | |||||
| if (sendCallback) | |||||
| graph->refresh(fDeviceName.buffer()); | |||||
| // --------------------------------------------------------------- | // --------------------------------------------------------------- | ||||
| // add midi connections | // add midi connections | ||||
| @@ -495,7 +497,8 @@ public: | |||||
| extGraph.connections.list.append(connectionToId); | 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(); | fMidiOutMutex.lock(); | ||||
| @@ -517,7 +520,8 @@ public: | |||||
| extGraph.connections.list.append(connectionToId); | 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(); | fMidiOutMutex.unlock(); | ||||
| @@ -525,6 +529,21 @@ public: | |||||
| return true; | 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: | protected: | ||||
| @@ -207,45 +207,3 @@ var* NamedValueSet::getVarPointerAt (int index) const noexcept | |||||
| return nullptr; | 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_Double = 4, | ||||
| varMarker_String = 5, | varMarker_String = 5, | ||||
| varMarker_Int64 = 6, | varMarker_Int64 = 6, | ||||
| varMarker_Array = 7, | |||||
| varMarker_Binary = 8, | |||||
| varMarker_Undefined = 9 | varMarker_Undefined = 9 | ||||
| }; | }; | ||||
| @@ -53,9 +51,6 @@ public: | |||||
| virtual double toDouble (const ValueUnion&) const noexcept { return 0; } | virtual double toDouble (const ValueUnion&) const noexcept { return 0; } | ||||
| virtual String toString (const ValueUnion&) const { return String(); } | virtual String toString (const ValueUnion&) const { return String(); } | ||||
| virtual bool toBool (const ValueUnion&) const noexcept { return false; } | 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 var clone (const var& original) const { return original; } | ||||
| virtual bool isVoid() const noexcept { return false; } | virtual bool isVoid() const noexcept { return false; } | ||||
| @@ -65,10 +60,6 @@ public: | |||||
| virtual bool isBool() const noexcept { return false; } | virtual bool isBool() const noexcept { return false; } | ||||
| virtual bool isDouble() const noexcept { return false; } | virtual bool isDouble() const noexcept { return false; } | ||||
| virtual bool isString() 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 cleanUp (ValueUnion&) const noexcept {} | ||||
| virtual void createCopy (ValueUnion& dest, const ValueUnion& source) const { dest = source; } | 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); } | 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_Void var::VariantType_Void::instance; | ||||
| const var::VariantType_Undefined var::VariantType_Undefined::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_Bool var::VariantType_Bool::instance; | ||||
| const var::VariantType_Double var::VariantType_Double::instance; | const var::VariantType_Double var::VariantType_Double::instance; | ||||
| const var::VariantType_String var::VariantType_String::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 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 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 (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 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 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); } | 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::isBool() const noexcept { return type->isBool(); } | ||||
| bool var::isDouble() const noexcept { return type->isDouble(); } | bool var::isDouble() const noexcept { return type->isDouble(); } | ||||
| bool var::isString() const noexcept { return type->isString(); } | 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 int() const noexcept { return type->toInt (value); } | ||||
| var::operator int64() const noexcept { return type->toInt64 (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); } | var::operator double() const noexcept { return type->toDouble (value); } | ||||
| String var::toString() const { return type->toString (value); } | String var::toString() const { return type->toString (value); } | ||||
| var::operator String() 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 | 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 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 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 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 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 | #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS | ||||
| var::var (var&& other) noexcept | 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)); | 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) | var& var::operator= (String&& v) | ||||
| { | { | ||||
| type->cleanUp (value); | type->cleanUp (value); | ||||
| @@ -589,207 +368,3 @@ var var::clone() const noexcept | |||||
| { | { | ||||
| return type->clone (*this); | 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 | class JUCE_API var | ||||
| { | { | ||||
| public: | 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. */ | /** Creates a void variant. */ | ||||
| var() noexcept; | var() noexcept; | ||||
| @@ -82,14 +61,7 @@ public: | |||||
| var (bool value) noexcept; | var (bool value) noexcept; | ||||
| var (double value) noexcept; | var (double value) noexcept; | ||||
| var (const char* value); | var (const char* value); | ||||
| var (const wchar_t* value); | |||||
| var (const String& 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= (const var& valueToCopy); | ||||
| var& operator= (int value); | var& operator= (int value); | ||||
| @@ -97,18 +69,11 @@ public: | |||||
| var& operator= (bool value); | var& operator= (bool value); | ||||
| var& operator= (double value); | var& operator= (double value); | ||||
| var& operator= (const char* value); | var& operator= (const char* value); | ||||
| var& operator= (const wchar_t* value); | |||||
| var& operator= (const String& 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 | #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS | ||||
| var (var&&) noexcept; | var (var&&) noexcept; | ||||
| var (String&&); | var (String&&); | ||||
| var (MemoryBlock&&); | |||||
| var (Array<var>&&); | |||||
| var& operator= (var&&) noexcept; | var& operator= (var&&) noexcept; | ||||
| var& operator= (String&&); | var& operator= (String&&); | ||||
| #endif | #endif | ||||
| @@ -127,25 +92,6 @@ public: | |||||
| operator String() const; | operator String() const; | ||||
| String toString() 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 isVoid() const noexcept; | ||||
| bool isUndefined() const noexcept; | bool isUndefined() const noexcept; | ||||
| @@ -154,10 +100,6 @@ public: | |||||
| bool isBool() const noexcept; | bool isBool() const noexcept; | ||||
| bool isDouble() const noexcept; | bool isDouble() const noexcept; | ||||
| bool isString() 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. | /** 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 | 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; | 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: | private: | ||||
| //============================================================================== | //============================================================================== | ||||
| class VariantType; friend class VariantType; | class VariantType; friend class VariantType; | ||||
| @@ -294,10 +133,6 @@ private: | |||||
| class VariantType_Double; friend class VariantType_Double; | class VariantType_Double; friend class VariantType_Double; | ||||
| class VariantType_Bool; friend class VariantType_Bool; | class VariantType_Bool; friend class VariantType_Bool; | ||||
| class VariantType_String; friend class VariantType_String; | 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 | union ValueUnion | ||||
| { | { | ||||
| @@ -306,9 +141,6 @@ private: | |||||
| bool boolValue; | bool boolValue; | ||||
| double doubleValue; | double doubleValue; | ||||
| char stringValue [sizeof (String)]; | char stringValue [sizeof (String)]; | ||||
| ReferenceCountedObject* objectValue; | |||||
| MemoryBlock* binaryValue; | |||||
| NativeFunction* methodValue; | |||||
| }; | }; | ||||
| const VariantType* type; | const VariantType* type; | ||||
| @@ -246,9 +246,6 @@ public: | |||||
| */ | */ | ||||
| void setLatencySamples (int newLatency); | 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. */ | /** Returns true if the processor wants midi messages. */ | ||||
| virtual bool acceptsMidi() const = 0; | 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. */ | /** Returns true if this is a midi effect plug-in and does no audio processing. */ | ||||
| virtual bool isMidiEffect() const { return false; } | 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 | /** This returns a critical section that will automatically be locked while the host | ||||
| is calling the processBlock() method. | is calling the processBlock() method. | ||||
| @@ -1390,7 +1390,6 @@ void AudioProcessorGraph::processAudio (AudioSampleBuffer& buffer, MidiBuffer& m | |||||
| midiMessages.addEvents (currentMidiOutputBuffer, 0, buffer.getNumSamples(), 0); | midiMessages.addEvents (currentMidiOutputBuffer, 0, buffer.getNumSamples(), 0); | ||||
| } | } | ||||
| double AudioProcessorGraph::getTailLengthSeconds() const { return 0; } | |||||
| bool AudioProcessorGraph::acceptsMidi() const { return true; } | bool AudioProcessorGraph::acceptsMidi() const { return true; } | ||||
| bool AudioProcessorGraph::producesMidi() const { return true; } | bool AudioProcessorGraph::producesMidi() const { return true; } | ||||
| @@ -1516,11 +1515,6 @@ void AudioProcessorGraph::AudioGraphIOProcessor::processBlock (AudioSampleBuffer | |||||
| processAudio (buffer, midiMessages); | processAudio (buffer, midiMessages); | ||||
| } | } | ||||
| double AudioProcessorGraph::AudioGraphIOProcessor::getTailLengthSeconds() const | |||||
| { | |||||
| return 0; | |||||
| } | |||||
| bool AudioProcessorGraph::AudioGraphIOProcessor::acceptsMidi() const | bool AudioProcessorGraph::AudioGraphIOProcessor::acceptsMidi() const | ||||
| { | { | ||||
| return type == midiOutputNode; | return type == midiOutputNode; | ||||
| @@ -319,7 +319,6 @@ public: | |||||
| void releaseResources() override; | void releaseResources() override; | ||||
| void processBlock (AudioSampleBuffer&, MidiBuffer&) override; | void processBlock (AudioSampleBuffer&, MidiBuffer&) override; | ||||
| double getTailLengthSeconds() const override; | |||||
| bool acceptsMidi() const override; | bool acceptsMidi() const override; | ||||
| bool producesMidi() const override; | bool producesMidi() const override; | ||||
| @@ -346,7 +345,6 @@ public: | |||||
| void setNonRealtime (bool) noexcept override; | void setNonRealtime (bool) noexcept override; | ||||
| void setPlayHead (AudioPlayHead*) override; | void setPlayHead (AudioPlayHead*) override; | ||||
| double getTailLengthSeconds() const override; | |||||
| bool acceptsMidi() const override; | bool acceptsMidi() const override; | ||||
| bool producesMidi() 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_CharacterFunctions.cpp" | ||||
| #include "text/juce_String.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_DirectoryIterator.cpp" | ||||
| #include "files/juce_File.cpp" | #include "files/juce_File.cpp" | ||||
| @@ -115,7 +115,7 @@ static int64 juce_fileSetPosition (void* handle, int64 pos) | |||||
| #include "misc/juce_Result.cpp" | #include "misc/juce_Result.cpp" | ||||
| #include "processors/juce_AudioProcessor.cpp" | #include "processors/juce_AudioProcessor.cpp" | ||||
| //#include "processors/juce_AudioProcessorGraph.cpp" | |||||
| #include "processors/juce_AudioProcessorGraph.cpp" | |||||
| #include "streams/juce_FileInputSource.cpp" | #include "streams/juce_FileInputSource.cpp" | ||||
| #include "streams/juce_FileInputStream.cpp" | #include "streams/juce_FileInputStream.cpp" | ||||
| @@ -358,6 +358,8 @@ const char* EngineProcessMode2Str(const EngineProcessMode mode) noexcept | |||||
| return "ENGINE_PROCESS_MODE_MULTIPLE_CLIENTS"; | return "ENGINE_PROCESS_MODE_MULTIPLE_CLIENTS"; | ||||
| case ENGINE_PROCESS_MODE_CONTINUOUS_RACK: | case ENGINE_PROCESS_MODE_CONTINUOUS_RACK: | ||||
| return "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: | case ENGINE_PROCESS_MODE_BRIDGE: | ||||
| return "ENGINE_PROCESS_MODE_BRIDGE"; | return "ENGINE_PROCESS_MODE_BRIDGE"; | ||||
| } | } | ||||
| @@ -20,6 +20,9 @@ | |||||
| #include "CarlaEngine.hpp" | #include "CarlaEngine.hpp" | ||||
| #include "CarlaUtils.hpp" | #include "CarlaUtils.hpp" | ||||
| #include "CarlaMIDI.h" | |||||
| #include "water/water.h" | |||||
| CARLA_BACKEND_START_NAMESPACE | CARLA_BACKEND_START_NAMESPACE | ||||
| @@ -110,6 +113,94 @@ const char* EngineControlEventType2Str(const EngineControlEventType type) noexce | |||||
| return nullptr; | 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 | // Helper classes | ||||