| @@ -645,6 +645,7 @@ protected: | |||
| void _addCVPortName(const bool, const char* const); | |||
| void _addEventPortName(const bool, const char* const); | |||
| const char* _getUniquePortName(const char* const); | |||
| void _clearPorts(); | |||
| CARLA_DECLARE_NON_COPY_CLASS(CarlaEngineClient) | |||
| #endif | |||
| @@ -1074,6 +1075,7 @@ protected: | |||
| friend class PendingRtEventsRunner; | |||
| friend class ScopedActionLock; | |||
| friend class ScopedEngineEnvironmentLocker; | |||
| friend class ScopedThreadStopper; | |||
| friend struct PatchbayGraph; | |||
| friend struct RackGraph; | |||
| @@ -576,9 +576,7 @@ bool CarlaEngine::addPlugin(const BinaryType btype, const PluginType ptype, | |||
| #ifndef BUILD_BRIDGE | |||
| if (oldPlugin != nullptr) | |||
| { | |||
| // the engine thread might be reading from the old plugin | |||
| pData->thread.stopThread(500); | |||
| pData->thread.startThread(); | |||
| const ScopedThreadStopper sts(this); | |||
| if (pData->options.processMode == ENGINE_PROCESS_MODE_PATCHBAY) | |||
| pData->graph.replacePlugin(oldPlugin, plugin); | |||
| @@ -635,7 +633,7 @@ bool CarlaEngine::removePlugin(const uint id) | |||
| CARLA_SAFE_ASSERT_RETURN_ERR(plugin != nullptr, "Could not find plugin to remove"); | |||
| CARLA_SAFE_ASSERT_RETURN_ERR(plugin->getId() == id, "Invalid engine internal data"); | |||
| pData->thread.stopThread(500); | |||
| const ScopedThreadStopper sts(this); | |||
| #ifndef BUILD_BRIDGE | |||
| if (pData->options.processMode == ENGINE_PROCESS_MODE_PATCHBAY) | |||
| @@ -664,9 +662,6 @@ bool CarlaEngine::removePlugin(const uint id) | |||
| delete plugin; | |||
| if (isRunning() && ! pData->aboutToClose) | |||
| pData->thread.startThread(); | |||
| callback(ENGINE_CALLBACK_PLUGIN_REMOVED, id, 0, 0, 0.0f, nullptr); | |||
| return true; | |||
| } | |||
| @@ -682,7 +677,7 @@ bool CarlaEngine::removeAllPlugins() | |||
| if (pData->curPluginCount == 0) | |||
| return true; | |||
| pData->thread.stopThread(500); | |||
| const ScopedThreadStopper sts(this); | |||
| const uint curPluginCount(pData->curPluginCount); | |||
| @@ -722,9 +717,6 @@ bool CarlaEngine::removeAllPlugins() | |||
| callback(ENGINE_CALLBACK_IDLE, 0, 0, 0, 0.0f, nullptr); | |||
| } | |||
| if (isRunning() && ! pData->aboutToClose) | |||
| pData->thread.startThread(); | |||
| return true; | |||
| } | |||
| @@ -825,38 +817,31 @@ bool CarlaEngine::switchPlugins(const uint idA, const uint idB) noexcept | |||
| CARLA_SAFE_ASSERT_RETURN_ERR(idB < pData->curPluginCount, "Invalid plugin Id"); | |||
| carla_debug("CarlaEngine::switchPlugins(%i)", idA, idB); | |||
| { | |||
| CarlaPlugin* const pluginA(pData->plugins[idA].plugin); | |||
| CarlaPlugin* const pluginB(pData->plugins[idB].plugin); | |||
| CarlaPlugin* const pluginA(pData->plugins[idA].plugin); | |||
| CarlaPlugin* const pluginB(pData->plugins[idB].plugin); | |||
| CARLA_SAFE_ASSERT_RETURN_ERR(pluginA != nullptr, "Could not find plugin to switch"); | |||
| CARLA_SAFE_ASSERT_RETURN_ERR(pluginA != nullptr, "Could not find plugin to switch"); | |||
| CARLA_SAFE_ASSERT_RETURN_ERR(pluginA->getId() == idA, "Invalid engine internal data"); | |||
| CARLA_SAFE_ASSERT_RETURN_ERR(pluginB->getId() == idB, "Invalid engine internal data"); | |||
| CARLA_SAFE_ASSERT_RETURN_ERR(pluginA != nullptr, "Could not find plugin to switch"); | |||
| CARLA_SAFE_ASSERT_RETURN_ERR(pluginA != nullptr, "Could not find plugin to switch"); | |||
| CARLA_SAFE_ASSERT_RETURN_ERR(pluginA->getId() == idA, "Invalid engine internal data"); | |||
| CARLA_SAFE_ASSERT_RETURN_ERR(pluginB->getId() == idB, "Invalid engine internal data"); | |||
| pData->thread.stopThread(500); | |||
| const ScopedThreadStopper sts(this); | |||
| if (pData->options.processMode == ENGINE_PROCESS_MODE_PATCHBAY) | |||
| pData->graph.replacePlugin(pluginA, pluginB); | |||
| } | |||
| if (pData->options.processMode == ENGINE_PROCESS_MODE_PATCHBAY) | |||
| pData->graph.replacePlugin(pluginA, pluginB); | |||
| const bool lockWait(isRunning() /*&& pData->options.processMode != ENGINE_PROCESS_MODE_MULTIPLE_CLIENTS*/); | |||
| const ScopedActionLock sal(this, kEnginePostActionSwitchPlugins, idA, idB, lockWait); | |||
| // TODO | |||
| /* | |||
| CarlaPlugin* const pluginA(pData->plugins[idA].plugin); | |||
| CarlaPlugin* const pluginB(pData->plugins[idB].plugin); | |||
| pluginA->updateOscURL(); | |||
| pluginB->updateOscURL(); | |||
| if (pluginA != nullptr && pluginB != nullptr) | |||
| { | |||
| pluginA->updateOscURL(); | |||
| pluginB->updateOscURL(); | |||
| } | |||
| if (isOscControlRegistered()) | |||
| oscSend_control_switch_plugins(idA, idB); | |||
| */ | |||
| // TODO | |||
| //if (isOscControlRegistered()) | |||
| // oscSend_control_switch_plugins(idA, idB); | |||
| if (isRunning() && ! pData->aboutToClose) | |||
| pData->thread.startThread(); | |||
| @@ -263,6 +263,16 @@ const char* CarlaEngineClient::_getUniquePortName(const char* const name) | |||
| return sname.dup(); | |||
| } | |||
| void CarlaEngineClient::_clearPorts() | |||
| { | |||
| pData->audioInList.clear(); | |||
| pData->audioOutList.clear(); | |||
| pData->cvInList.clear(); | |||
| pData->cvOutList.clear(); | |||
| pData->eventInList.clear(); | |||
| pData->eventOutList.clear(); | |||
| } | |||
| // ----------------------------------------------------------------------- | |||
| CARLA_BACKEND_END_NAMESPACE | |||
| @@ -393,6 +393,22 @@ ScopedActionLock::~ScopedActionLock() noexcept | |||
| pData->nextAction.mutex.unlock(); | |||
| } | |||
| // ----------------------------------------------------------------------- | |||
| // ScopedThreadStopper | |||
| ScopedThreadStopper::ScopedThreadStopper(CarlaEngine* const e) noexcept | |||
| : engine(e), | |||
| pData(e->pData) | |||
| { | |||
| pData->thread.stopThread(500); | |||
| } | |||
| ScopedThreadStopper::~ScopedThreadStopper() noexcept | |||
| { | |||
| if (engine->isRunning() && ! pData->aboutToClose) | |||
| pData->thread.startThread(); | |||
| } | |||
| // ----------------------------------------------------------------------- | |||
| // ScopedEngineEnvironmentLocker | |||
| @@ -220,8 +220,6 @@ struct CarlaEngine::ProtectedData { | |||
| // ------------------------------------------------------------------- | |||
| //friend class ScopedActionLock; | |||
| #ifdef CARLA_PROPER_CPP11_SUPPORT | |||
| ProtectedData() = delete; | |||
| CARLA_DECLARE_NON_COPY_STRUCT(ProtectedData) | |||
| @@ -260,6 +258,22 @@ private: | |||
| // ----------------------------------------------------------------------- | |||
| class ScopedThreadStopper | |||
| { | |||
| public: | |||
| ScopedThreadStopper(CarlaEngine* const engine) noexcept; | |||
| ~ScopedThreadStopper() noexcept; | |||
| private: | |||
| CarlaEngine* const engine; | |||
| CarlaEngine::ProtectedData* const pData; | |||
| CARLA_PREVENT_HEAP_ALLOCATION | |||
| CARLA_DECLARE_NON_COPY_CLASS(ScopedThreadStopper) | |||
| }; | |||
| // ----------------------------------------------------------------------- | |||
| CARLA_BACKEND_END_NAMESPACE | |||
| #endif // CARLA_ENGINE_INTERNAL_HPP_INCLUDED | |||
| @@ -530,8 +530,10 @@ public: | |||
| const char* realName = name; | |||
| // Create JACK port first, if needed | |||
| if (fUseClient && fJackClient != nullptr) | |||
| if (fUseClient) | |||
| { | |||
| CARLA_SAFE_ASSERT_RETURN(fJackClient != nullptr, nullptr); | |||
| realName = _getUniquePortName(name); | |||
| switch (portType) | |||
| @@ -638,6 +640,32 @@ public: | |||
| fEventPorts.removeAll(port); | |||
| } | |||
| void closeForRename(jack_client_t* const client) noexcept | |||
| { | |||
| if (fJackClient != nullptr) | |||
| { | |||
| if (isActive()) | |||
| { | |||
| try { | |||
| jackbridge_activate(fJackClient); | |||
| } catch(...) {} | |||
| } | |||
| try { | |||
| jackbridge_client_close(fJackClient); | |||
| } catch(...) {} | |||
| invalidate(); | |||
| } | |||
| fAudioPorts.clear(); | |||
| fCVPorts.clear(); | |||
| fEventPorts.clear(); | |||
| _clearPorts(); | |||
| fJackClient = client; | |||
| } | |||
| private: | |||
| jack_client_t* fJackClient; | |||
| const bool fUseClient; | |||
| @@ -646,8 +674,6 @@ private: | |||
| LinkedList<CarlaEngineJackCVPort*> fCVPorts; | |||
| LinkedList<CarlaEngineJackEventPort*> fEventPorts; | |||
| friend class CarlaEngineJack; | |||
| CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CarlaEngineJackClient) | |||
| }; | |||
| @@ -1017,6 +1043,8 @@ public: | |||
| return nullptr; | |||
| } | |||
| const ScopedThreadStopper sts(this); | |||
| CARLA_SAFE_ASSERT(plugin->getId() == id); | |||
| CarlaString uniqueName; | |||
| @@ -1025,7 +1053,7 @@ public: | |||
| const char* const tmpName = getUniquePluginName(newName); | |||
| uniqueName = tmpName; | |||
| delete[] tmpName; | |||
| } CARLA_SAFE_EXCEPTION("JACK renamePlugin"); | |||
| } CARLA_SAFE_EXCEPTION("JACK renamePlugin getUniquePluginName"); | |||
| if (uniqueName.isEmpty()) | |||
| { | |||
| @@ -1052,26 +1080,20 @@ public: | |||
| // we should not be able to do this, jack really needs to allow client rename | |||
| if (jack_client_t* const jackClient = jackbridge_client_open(uniqueName, JackNullOption, nullptr)) | |||
| { | |||
| // close old client | |||
| // disable plugin | |||
| plugin->setEnabled(false); | |||
| if (client->isActive()) | |||
| client->deactivate(); | |||
| plugin->clearBuffers(); | |||
| jackbridge_client_close(client->fJackClient); | |||
| // close client | |||
| client->closeForRename(jackClient); | |||
| // set new client data | |||
| uniqueName = jackbridge_get_client_name(jackClient); | |||
| jackbridge_set_thread_init_callback(jackClient, carla_jack_thread_init_callback, nullptr); | |||
| jackbridge_set_process_callback(jackClient, carla_jack_process_callback_plugin, plugin); | |||
| jackbridge_set_latency_callback(jackClient, carla_jack_latency_callback_plugin, plugin); | |||
| jackbridge_set_process_callback(jackClient, carla_jack_process_callback_plugin, plugin); | |||
| jackbridge_on_shutdown(jackClient, carla_jack_shutdown_callback_plugin, plugin); | |||
| client->fJackClient = jackClient; | |||
| needsReinit = true; | |||
| } | |||
| else | |||
| @@ -960,9 +960,8 @@ void CarlaPlugin::setEnabled(const bool yesNo) noexcept | |||
| if (pData->enabled == yesNo) | |||
| return; | |||
| pData->enabled = yesNo; | |||
| pData->masterMutex.lock(); | |||
| pData->enabled = yesNo; | |||
| pData->masterMutex.unlock(); | |||
| } | |||