From 8229f8cb57fe2d6d2f3059fd8de2cdef51ce4f94 Mon Sep 17 00:00:00 2001 From: falkTX Date: Tue, 18 Jun 2013 17:16:55 +0100 Subject: [PATCH] Initial code for Carla-Plugin UI; Fixes for Carla-Control --- source/backend/engine/CarlaEngineNative.cpp | 188 ++++++++++++++++++-- source/backend/plugin/CarlaPluginThread.cpp | 5 + source/carla.py | 7 +- source/carla_control.py | 57 +++--- source/carla_shared.py | 6 + 5 files changed, 226 insertions(+), 37 deletions(-) diff --git a/source/backend/engine/CarlaEngineNative.cpp b/source/backend/engine/CarlaEngineNative.cpp index fb48390c6..4a1273964 100644 --- a/source/backend/engine/CarlaEngineNative.cpp +++ b/source/backend/engine/CarlaEngineNative.cpp @@ -22,19 +22,134 @@ #include "CarlaNative.hpp" +#include #include CARLA_BACKEND_START_NAMESPACE // ----------------------------------------------------------------------- +class CarlaEngineNativeThread : public QThread +{ +public: + enum UiState { + UiNone = 0, + UiHide, + UiShow, + UiCrashed + }; + + CarlaEngineNativeThread(CarlaEngine* const engine) + : kEngine(engine), + fBinary("carla-control"), + fProcess(nullptr), + fUiState(UiNone) + { + carla_debug("CarlaEngineNativeThread::CarlaEngineNativeThread(engine:\"%s\")", engine->getName()); + } + + ~CarlaEngineNativeThread() + { + CARLA_ASSERT_INT(fUiState == UiNone, fUiState); + carla_debug("CarlaEngineNativeThread::~CarlaEngineNativeThread()"); + + if (fProcess != nullptr) + { + delete fProcess; + fProcess = nullptr; + } + } + + void setOscData(const char* const binary) + { + fBinary = binary; + } + + UiState getUiState() + { + const UiState state(fUiState); + fUiState = UiNone; + + return state; + } + + void stop() + { + if (fProcess == nullptr) + return; + + fUiState = UiNone; + fProcess->kill(); + //fProcess->close(); + } + +protected: + void run() + { + carla_debug("CarlaEngineNativeThread::run() - binary:\"%s\"", (const char*)fBinary); + + if (fProcess == nullptr) + { + fProcess = new QProcess(nullptr); + fProcess->setProcessChannelMode(QProcess::ForwardedChannels); + } + else if (fProcess->state() == QProcess::Running) + { + carla_stderr("CarlaEngineNativeThread::run() - already running, giving up..."); + + fUiState = UiCrashed; + fProcess->terminate(); + //kEngine->callback(CarlaBackend::CALLBACK_SHOW_GUI, kPlugin->id(), -1, 0, 0.0f, nullptr); + // TODO: tell master to hide UI + return; + } + + QStringList arguments; + arguments << kEngine->getOscServerPathTCP(); + + fProcess->start((const char*)fBinary, arguments); + fProcess->waitForStarted(); + + fUiState = UiShow; + + fProcess->waitForFinished(-1); + + if (fProcess->exitCode() == 0) + { + // Hide + fUiState = UiHide; + carla_stdout("CarlaEngineNativeThread::run() - GUI closed"); + } + else + { + // Kill + fUiState = UiCrashed; + carla_stderr("CarlaEngineNativeThread::run() - GUI crashed while running"); + } + } + +private: + CarlaEngine* const kEngine; + + CarlaString fBinary; + QProcess* fProcess; + + UiState fUiState; + + CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CarlaEngineNativeThread) +}; + +// ----------------------------------------------------------------------- + class CarlaEngineNative : public PluginDescriptorClass, public CarlaEngine { public: CarlaEngineNative(const HostDescriptor* const host) : PluginDescriptorClass(host), - CarlaEngine() + CarlaEngine(), + fIsRunning(true), + fThread(this) { carla_debug("CarlaEngineNative::CarlaEngineNative()"); @@ -45,12 +160,29 @@ public: fOptions.preferPluginBridges = false; fOptions.preferUiBridges = false; init("Carla-Plugin"); + + // set control thread binary + CarlaString threadBinary(hostResourceDir()); + threadBinary += "/../"; + threadBinary += "carla_control.py"; + + fThread.setOscData(threadBinary); + + // TESTING +// if (! addPlugin(PLUGIN_INTERNAL, nullptr, "MIDI Transpose", "midiTranspose")) +// carla_stdout("TESTING PLUG1 ERROR:\n%s", getLastError()); +// if (! addPlugin(PLUGIN_INTERNAL, nullptr, "ZynAddSubFX", "zynaddsubfx")) +// carla_stdout("TESTING PLUG2 ERROR:\n%s", getLastError()); +// if (! addPlugin(PLUGIN_INTERNAL, nullptr, "Ping Pong Pan", "PingPongPan")) +// carla_stdout("TESTING PLUG3 ERROR:\n%s", getLastError()); } ~CarlaEngineNative() override { carla_debug("CarlaEngineNative::~CarlaEngineNative()"); + fIsRunning = false; + setAboutToClose(); removeAllPlugins(); close(); @@ -81,7 +213,7 @@ protected: bool isRunning() const override { - return true; + return fIsRunning; } bool isOffline() const override @@ -319,7 +451,7 @@ protected: { carla_zeroFloat(outBuffer[0], frames); carla_zeroFloat(outBuffer[1], frames); - return CarlaEngine::proccessPendingEvents(); + return proccessPendingEvents(); } // --------------------------------------------------------------- @@ -412,14 +544,14 @@ protected: // --------------------------------------------------------------- // create audio buffers - float* inBuf[2] = { inBuffer[0], inBuffer[1] }; + float* inBuf[2] = { inBuffer[0], inBuffer[1] }; float* outBuf[2] = { outBuffer[0], outBuffer[1] }; // --------------------------------------------------------------- // process - CarlaEngine::processRack(inBuf, outBuf, frames); - CarlaEngine::proccessPendingEvents(); + processRack(inBuf, outBuf, frames); + proccessPendingEvents(); } // ------------------------------------------------------------------- @@ -427,17 +559,44 @@ protected: void uiShow(const bool show) override { - return; + if (show) + { + fThread.start(); + } + else + { +#if 0 + for (uint32_t i=0; i < kData->curPluginCount; ++i) + { + CarlaPlugin* const plugin(kData->plugins[i].plugin); - // TODO + if (plugin == nullptr || ! plugin->enabled()) + continue; - // unused - (void)show; + plugin->showGui(false); + } +#endif + + fThread.stop(); + } } void uiIdle() override { CarlaEngine::idle(); + + switch(fThread.getUiState()) + { + case CarlaEngineNativeThread::UiNone: + case CarlaEngineNativeThread::UiShow: + break; + case CarlaEngineNativeThread::UiCrashed: + hostDispatcher(HOST_OPCODE_UI_UNAVAILABLE, 0, 0, nullptr); + break; + case CarlaEngineNativeThread::UiHide: + uiClosed(); + break; + } } void uiSetParameterValue(const uint32_t index, const float value) override @@ -495,7 +654,7 @@ protected: for (unsigned int i=0; i < kData->curPluginCount; ++i) { - CarlaPlugin* const plugin = kData->plugins[i].plugin; + CarlaPlugin* const plugin(kData->plugins[i].plugin); if (plugin != nullptr && plugin->enabled()) { @@ -566,6 +725,9 @@ protected: // ------------------------------------------------------------------- private: + bool fIsRunning; + CarlaEngineNativeThread fThread; + PluginDescriptorClassEND(CarlaEngineNative) CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CarlaEngineNative) }; @@ -574,14 +736,14 @@ private: static const PluginDescriptor carlaDesc = { /* category */ ::PLUGIN_CATEGORY_OTHER, - /* hints */ static_cast< ::PluginHints>(::PLUGIN_IS_SYNTH|::PLUGIN_USES_SINGLE_THREAD|::PLUGIN_USES_STATE), + /* hints */ static_cast< ::PluginHints>(::PLUGIN_IS_SYNTH|::PLUGIN_HAS_GUI|::PLUGIN_USES_SINGLE_THREAD|::PLUGIN_USES_STATE), /* audioIns */ 2, /* audioOuts */ 2, /* midiIns */ 1, /* midiOuts */ 1, /* paramIns */ 0, /* paramOuts */ 0, - /* name */ "Carla-Plugin", + /* name */ "Carla-Plugin (TESTING)", /* label */ "carla", /* maker */ "falkTX", /* copyright */ "GNU GPL v2+", diff --git a/source/backend/plugin/CarlaPluginThread.cpp b/source/backend/plugin/CarlaPluginThread.cpp index 4f5294423..ae3ce9c74 100644 --- a/source/backend/plugin/CarlaPluginThread.cpp +++ b/source/backend/plugin/CarlaPluginThread.cpp @@ -55,8 +55,13 @@ CarlaPluginThread::CarlaPluginThread(CarlaBackend::CarlaEngine* const engine, Ca CarlaPluginThread::~CarlaPluginThread() { + carla_debug("CarlaPluginThread::~CarlaPluginThread()"); + if (fProcess != nullptr) + { delete fProcess; + fProcess = nullptr; + } } void CarlaPluginThread::setMode(const CarlaPluginThread::Mode mode) diff --git a/source/carla.py b/source/carla.py index e97d2879a..418238bb1 100755 --- a/source/carla.py +++ b/source/carla.py @@ -2297,9 +2297,12 @@ if __name__ == '__main__': app.setOrganizationName("falkTX") app.setWindowIcon(QIcon(":/scalable/carla.svg")) - for i in range(len(app.arguments())): + argv = app.arguments() + argc = len(argv) + + for i in range(argc): if i == 0: continue - argument = app.arguments()[i] + argument = argv[i] if argument.startswith("--with-appname="): appName = os.path.basename(argument.replace("--with-appname=", "")) diff --git a/source/carla_control.py b/source/carla_control.py index da4322036..df43e7be0 100755 --- a/source/carla_control.py +++ b/source/carla_control.py @@ -601,8 +601,8 @@ class ControlServer(ServerThread): # Main Window class CarlaControlW(QMainWindow): - def __init__(self, parent=None): - QMainWindow.__init__(self, parent) + def __init__(self, oscAddr=None): + QMainWindow.__init__(self, None) self.ui = ui_carla_control.Ui_CarlaControlW() self.ui.setupUi(self) @@ -680,6 +680,33 @@ class CarlaControlW(QMainWindow): self.connect(self, SIGNAL("SetPeaks(int, double, double, double, double)"), SLOT("slot_handleSetPeaks(int, double, double, double, double)")) self.connect(self, SIGNAL("Exit()"), SLOT("slot_handleExit()")) + if oscAddr: + self.connectToAddr(oscAddr) + + def connectToAddr(self, addr): + global lo_target, lo_targetName + + self.lo_address = oscAddr + lo_target = Address(self.lo_address) + lo_targetName = self.lo_address.rsplit("/", 1)[-1] + print("Connecting to \"%s\" as '%s'..." % (self.lo_address, lo_targetName)) + + try: + self.lo_server = ControlServer(self, LO_UDP if self.lo_address.startswith("osc.udp") else LO_TCP) + except: # ServerError, err: + print("Connecting error!") + #print str(err) + QMessageBox.critical(self, self.tr("Error"), self.tr("Failed to connect, operation failed.")) + return + + if self.lo_server: + self.lo_server.start() + self.ui.act_file_refresh.setEnabled(True) + lo_send(lo_target, "/register", self.lo_server.getFullURL()) + + self.fIdleTimerFast = self.startTimer(60) + self.fIdleTimerSlow = self.startTimer(60*2) + def removeAll(self): self.killTimer(self.fIdleTimerFast) self.killTimer(self.fIdleTimerSlow) @@ -725,22 +752,7 @@ class CarlaControlW(QMainWindow): self.slot_handleExit() - self.lo_address = askValue[0] - lo_target = Address(self.lo_address) - lo_targetName = self.lo_address.rsplit("/", 1)[-1] - print("Connecting to \"%s\" as '%s'..." % (self.lo_address, lo_targetName)) - - try: - self.lo_server = ControlServer(self, LO_UDP if self.lo_address.startswith("osc.udp") else LO_TCP) - except: # ServerError, err: - print("Connecting error!") - #print str(err) - QMessageBox.critical(self, self.tr("Error"), self.tr("Failed to connect, operation failed.")) - - if self.lo_server: - self.lo_server.start() - self.ui.act_file_refresh.setEnabled(True) - lo_send(lo_target, "/register", self.lo_server.getFullURL()) + self.connectToAddr(askValue[0]) @pyqtSlot() def slot_fileRefresh(self): @@ -1057,13 +1069,11 @@ if __name__ == '__main__': app.setWindowIcon(QIcon(":/scalable/carla-control.svg")) libPrefix = None + oscAddr = None argv = app.arguments() argc = len(argv) - #print(argc) - #print(argv) - for i in range(argc): if i == 0: continue argument = argv[i] @@ -1071,6 +1081,9 @@ if __name__ == '__main__': if argument.startswith("--with-libprefix="): libPrefix = argument.replace("--with-libprefix=", "") + elif argument.startswith("osc."): + oscAddr = argument + if libPrefix is not None: libName = os.path.join(libPrefix, "lib", "carla", carla_libname) else: @@ -1089,7 +1102,7 @@ if __name__ == '__main__': Carla.host = Host() # Create GUI - Carla.gui = CarlaControlW() + Carla.gui = CarlaControlW(oscAddr) # Set-up custom signal handling setUpSignals() diff --git a/source/carla_shared.py b/source/carla_shared.py index cb8d0232e..cdd363ac3 100644 --- a/source/carla_shared.py +++ b/source/carla_shared.py @@ -1182,6 +1182,9 @@ class PluginEdit(QDialog): self.fPluginInfo['maker'] = cString(self.fPluginInfo['maker']) self.fPluginInfo['copyright'] = cString(self.fPluginInfo['copyright']) + if not Carla.isLocal: + self.fPluginInfo['hints'] &= ~PLUGIN_HAS_GUI + self.reloadInfo() self.reloadParameters() self.reloadPrograms() @@ -1982,6 +1985,9 @@ class PluginWidget(QFrame): self.fPluginInfo['maker'] = cString(self.fPluginInfo['maker']) self.fPluginInfo['copyright'] = cString(self.fPluginInfo['copyright']) + if not Carla.isLocal: + self.fPluginInfo['hints'] &= ~PLUGIN_HAS_GUI + self.fLastGreenLedState = False self.fLastBlueLedState = False