diff --git a/source/backend/CarlaUtils.cpp b/source/backend/CarlaUtils.cpp index df67ee3bc..8e7ea18cf 100644 --- a/source/backend/CarlaUtils.cpp +++ b/source/backend/CarlaUtils.cpp @@ -17,22 +17,13 @@ #include "CarlaUtils.h" +#include "CarlaPipeUtils.hpp" #include "CarlaThread.hpp" #include "juce_core.h" // ------------------------------------------------------------------------------------------------------------------- -void carla_set_process_name(const char* name) -{ - carla_debug("carla_set_process_name(\"%s\")", name); - - CarlaThread::setCurrentThreadName(name); - juce::Thread::setCurrentThreadName(name); -} - -// ------------------------------------------------------------------------------------------------------------------- - const char* carla_get_library_filename() { carla_debug("carla_get_library_filename()"); @@ -64,3 +55,153 @@ const char* carla_get_library_folder() } // ------------------------------------------------------------------------------------------------------------------- + +void carla_set_locale_C() +{ + ::setlocale(LC_NUMERIC, "C"); +} + +// ------------------------------------------------------------------------------------------------------------------- + +void carla_set_process_name(const char* name) +{ + carla_debug("carla_set_process_name(\"%s\")", name); + + CarlaThread::setCurrentThreadName(name); + juce::Thread::setCurrentThreadName(name); +} + +// ------------------------------------------------------------------------------------------------------------------- + +class CarlaPipeClientPlugin : public CarlaPipeClient +{ +public: + CarlaPipeClientPlugin(const CarlaPipeCallbackFunc callbackFunc, void* const callbackPtr) noexcept + : CarlaPipeClient(), + fCallbackFunc(callbackFunc), + fCallbackPtr(callbackPtr), + leakDetector_CarlaPipeClientPlugin() + { + CARLA_SAFE_ASSERT(fCallbackFunc != nullptr); + } + + const char* readlineblock(const uint timeout) noexcept + { + return CarlaPipeClient::readlineblock(timeout); + } + + bool msgReceived(const char* const msg) noexcept + { + if (fCallbackFunc != nullptr) + { + try { + fCallbackFunc(fCallbackPtr, msg); + } CARLA_SAFE_EXCEPTION("msgReceived"); + } + + return true; + } + +private: + const CarlaPipeCallbackFunc fCallbackFunc; + void* const fCallbackPtr; + + CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CarlaPipeClientPlugin) +}; + +CarlaPipeClientHandle carla_pipe_client_new(const char* argv[], CarlaPipeCallbackFunc callbackFunc, void* callbackPtr) +{ + carla_debug("carla_pipe_client_new(%p, %p, %p)", argv, callbackFunc, callbackPtr); + + CarlaPipeClientPlugin* const pipe(new CarlaPipeClientPlugin(callbackFunc, callbackPtr)); + + if (! pipe->init(argv)) + { + delete pipe; + return nullptr; + } + + return pipe; +} + +void carla_pipe_client_idle(CarlaPipeClientHandle handle) +{ + CARLA_SAFE_ASSERT_RETURN(handle != nullptr,); + + ((CarlaPipeClientPlugin*)handle)->idle(); +} + +bool carla_pipe_client_is_running(CarlaPipeClientHandle handle) +{ + CARLA_SAFE_ASSERT_RETURN(handle != nullptr, false); + + return ((CarlaPipeClientPlugin*)handle)->isRunning(); +} + +void carla_pipe_client_lock(CarlaPipeClientHandle handle) +{ + CARLA_SAFE_ASSERT_RETURN(handle != nullptr,); + + return ((CarlaPipeClientPlugin*)handle)->lock(); +} + +void carla_pipe_client_unlock(CarlaPipeClientHandle handle) +{ + CARLA_SAFE_ASSERT_RETURN(handle != nullptr,); + + return ((CarlaPipeClientPlugin*)handle)->unlock(); +} + +const char* carla_pipe_client_readlineblock(CarlaPipeClientHandle handle, uint timeout) +{ + CARLA_SAFE_ASSERT_RETURN(handle != nullptr, nullptr); + + return ((CarlaPipeClientPlugin*)handle)->readlineblock(timeout); +} + +bool carla_pipe_client_write_msg(CarlaPipeClientHandle handle, const char* msg) +{ + CARLA_SAFE_ASSERT_RETURN(handle != nullptr, false); + + return ((CarlaPipeClientPlugin*)handle)->writeMsg(msg); +} + +bool carla_pipe_client_write_and_fix_msg(CarlaPipeClientHandle handle, const char* msg) +{ + CARLA_SAFE_ASSERT_RETURN(handle != nullptr, false); + + return ((CarlaPipeClientPlugin*)handle)->writeAndFixMsg(msg); +} + +bool carla_pipe_client_flush(CarlaPipeClientHandle handle) +{ + CARLA_SAFE_ASSERT_RETURN(handle != nullptr, false); + + return ((CarlaPipeClientPlugin*)handle)->flush(); +} + +bool carla_pipe_client_flush_and_unlock(CarlaPipeClientHandle handle) +{ + CARLA_SAFE_ASSERT_RETURN(handle != nullptr, false); + + CarlaPipeClientPlugin* const pipe((CarlaPipeClientPlugin*)handle); + const bool ret(pipe->flush()); + pipe->unlock(); + return ret; +} + +void carla_pipe_client_destroy(CarlaPipeClientHandle handle) +{ + CARLA_SAFE_ASSERT_RETURN(handle != nullptr,); + carla_debug("carla_pipe_client_destroy(%p)", handle); + + CarlaPipeClientPlugin* const pipe((CarlaPipeClientPlugin*)handle); + pipe->close(); + delete pipe; +} + +// ------------------------------------------------------------------------------------------------------------------- + +#include "CarlaPipeUtils.cpp" + +// ------------------------------------------------------------------------------------------------------------------- diff --git a/source/backend/CarlaUtils.h b/source/backend/CarlaUtils.h index 96d27b6f1..1aec97be2 100644 --- a/source/backend/CarlaUtils.h +++ b/source/backend/CarlaUtils.h @@ -30,9 +30,14 @@ */ /*! - * Get the juce version used in the current Carla build. + * TODO. */ -CARLA_EXPORT void carla_set_process_name(const char* name); +typedef void* CarlaPipeClientHandle; + +/*! + * TODO. + */ +typedef void (*CarlaPipeCallbackFunc)(void* ptr, const char* msg); /*! * Get the current carla library filename. @@ -44,6 +49,71 @@ CARLA_EXPORT const char* carla_get_library_filename(); */ CARLA_EXPORT const char* carla_get_library_folder(); +/*! + * TODO. + */ +CARLA_EXPORT void carla_set_locale_C(); + +/*! + * Get the juce version used in the current Carla build. + */ +CARLA_EXPORT void carla_set_process_name(const char* name); + +/*! + * TODO. + */ +CARLA_EXPORT CarlaPipeClientHandle carla_pipe_client_new(const char* argv[], CarlaPipeCallbackFunc callbackFunc, void* callbackPtr); + +/*! + * TODO. + */ +CARLA_EXPORT void carla_pipe_client_idle(CarlaPipeClientHandle handle); + +/*! + * TODO. + */ +CARLA_EXPORT bool carla_pipe_client_is_running(CarlaPipeClientHandle handle); + +/*! + * TODO. + */ +CARLA_EXPORT void carla_pipe_client_lock(CarlaPipeClientHandle handle); + +/*! + * TODO. + */ +CARLA_EXPORT void carla_pipe_client_unlock(CarlaPipeClientHandle handle); + +/*! + * TODO. + */ +CARLA_EXPORT const char* carla_pipe_client_readlineblock(CarlaPipeClientHandle handle, uint timeout); + +/*! + * TODO. + */ +CARLA_EXPORT bool carla_pipe_client_write_msg(CarlaPipeClientHandle handle, const char* msg); + +/*! + * TODO. + */ +CARLA_EXPORT bool carla_pipe_client_write_and_fix_msg(CarlaPipeClientHandle handle, const char* msg); + +/*! + * TODO. + */ +CARLA_EXPORT bool carla_pipe_client_flush(CarlaPipeClientHandle handle); + +/*! + * TODO. + */ +CARLA_EXPORT bool carla_pipe_client_flush_and_unlock(CarlaPipeClientHandle handle); + +/*! + * TODO. + */ +CARLA_EXPORT void carla_pipe_client_destroy(CarlaPipeClientHandle handle); + /** @} */ #endif /* CARLA_UTILS_H_INCLUDED */ diff --git a/source/backend/engine/CarlaEngineNative.cpp b/source/backend/engine/CarlaEngineNative.cpp index 8209194f5..69184f465 100644 --- a/source/backend/engine/CarlaEngineNative.cpp +++ b/source/backend/engine/CarlaEngineNative.cpp @@ -967,7 +967,7 @@ protected: std::sprintf(fTmpBuf, "%f\n", value3); fUiServer.writeMsg(fTmpBuf); - fUiServer.writeAndFixMsg(valueStr); + fUiServer.writeAndFixMsg(valueStr != nullptr ? valueStr : ""); fUiServer.flush(); } @@ -1465,7 +1465,7 @@ protected: // send transport fUiServer.writeAndFixMsg("transport"); - fUiServer.writeAndFixMsg(bool2str(timeInfo.playing)); + fUiServer.writeMsg(timeInfo.playing ? "true\n" : "false\n"); if (timeInfo.valid & EngineTimeInfo::kValidBBT) { diff --git a/source/carla_app.py b/source/carla_app.py index 46ca97af9..c322e6a3e 100644 --- a/source/carla_app.py +++ b/source/carla_app.py @@ -43,47 +43,22 @@ class CarlaApplication(object): def __init__(self, appName = "Carla2", libPrefix = None): object.__init__(self) - # try to find styles dir - stylesDir = "" + pathBinaries, pathResources = getPaths(libPrefix) - CWDl = CWD.lower() - - # standalone, installed system-wide linux - if libPrefix is not None: - stylesDir = os.path.join(libPrefix, "lib", "carla") - - # standalone, local source - elif CWDl.endswith("source"): - stylesDir = os.path.abspath(os.path.join(CWD, "..", "bin")) - - if WINDOWS: - # Fixes local wine build - QApplication.addLibraryPath("C:\\Python34\\Lib\\site-packages\\PyQt5\\plugins") - - # plugin - elif CWDl.endswith("resources"): - # installed system-wide linux - if CWDl.endswith("/share/carla/resources"): - stylesDir = os.path.abspath(os.path.join(CWD, "..", "..", "..", "lib", "carla")) - - # local source - elif CWDl.endswith("native-plugins%sresources" % os.sep): - stylesDir = os.path.abspath(os.path.join(CWD, "..", "..", "..", "..", "bin")) - - # other - else: - stylesDir = os.path.abspath(os.path.join(CWD, "..")) - - # everything else - else: - stylesDir = CWD + # Needed for MacOS LV2 plugin + if MACOS and os.path.exists(CWD): + QApplication.addLibraryPath(CWD) - if os.path.exists(stylesDir): - QApplication.addLibraryPath(stylesDir) + # Needed for local wine build + if WINDOWS and CWD.endswith("source"): + QApplication.addLibraryPath("C:\\Python34\\Lib\\site-packages\\PyQt5\\plugins") - if WINDOWS: - stylesDir = "" + # Use binary dir as library path (except in Windows) + if os.path.exists(pathBinaries) and not WINDOWS: + QApplication.addLibraryPath(pathBinaries) + stylesDir = pathBinaries + # If style is not available we can still fake it in Qt5 elif config_UseQt5: stylesDir = "" @@ -91,10 +66,6 @@ class CarlaApplication(object): self.createApp(appName) return - # Needed for MacOS LV2 plugin - if MACOS and os.path.exists(CWD): - QApplication.addLibraryPath(CWD) - # base settings settings = QSettings("falkTX", appName) useProTheme = settings.value(CARLA_KEY_MAIN_USE_PRO_THEME, CARLA_DEFAULT_MAIN_USE_PRO_THEME, type=bool) diff --git a/source/carla_host.py b/source/carla_host.py index ec88f35fc..996a5cb97 100644 --- a/source/carla_host.py +++ b/source/carla_host.py @@ -1970,58 +1970,7 @@ def initHost(initName, libPrefixOrPluginClass, isControl, isPlugin, failError): PluginClass = None libPrefix = libPrefixOrPluginClass - # -------------------------------------------------------------------------------------------------------- - # Set Carla library name - - libname = "libcarla_%s2" % ("control" if isControl else "standalone") - utilsname = "libcarla_utils" - - if WINDOWS: - libname += ".dll" - utilsname += ".dll" - elif MACOS: - libname += ".dylib" - utilsname += ".dylib" - else: - libname += ".so" - utilsname += ".so" - - # -------------------------------------------------------------------------------------------------------- - # Set binary dir - - CWDl = CWD.lower() - - # standalone, installed system-wide linux - if libPrefix is not None: - pathBinaries = os.path.join(libPrefix, "lib", "carla") - pathResources = os.path.join(libPrefix, "share", "carla", "resources") - - # standalone, local source - elif CWDl.endswith("source"): - pathBinaries = os.path.abspath(os.path.join(CWD, "..", "bin")) - pathResources = os.path.join(pathBinaries, "resources") - - # plugin - elif CWDl.endswith("resources"): - # installed system-wide linux - if CWDl.endswith("/share/carla/resources"): - pathBinaries = os.path.abspath(os.path.join(CWD, "..", "..", "..", "lib", "carla")) - pathResources = CWD - - # local source - elif CWDl.endswith("native-plugins%sresources" % os.sep): - pathBinaries = os.path.abspath(os.path.join(CWD, "..", "..", "..", "..", "bin")) - pathResources = CWD - - # other - else: - pathBinaries = os.path.abspath(os.path.join(CWD, "..")) - pathResources = CWD - - # everything else - else: - pathBinaries = CWD - pathResources = os.path.join(pathBinaries, "resources") + pathBinaries, pathResources = getPaths(libPrefix) # -------------------------------------------------------------------------------------------------------- # Fail if binary dir is not found @@ -2032,6 +1981,12 @@ def initHost(initName, libPrefixOrPluginClass, isControl, isPlugin, failError): sys.exit(1) return + # -------------------------------------------------------------------------------------------------------- + # Set Carla library name + + libname = "libcarla_%s2.%s" % ("control" if isControl else "standalone", DLL_EXTENSION) + utilsname = "libcarla_utils.%s" % (DLL_EXTENSION) + # -------------------------------------------------------------------------------------------------------- # Print info @@ -2075,6 +2030,7 @@ def initHost(initName, libPrefixOrPluginClass, isControl, isPlugin, failError): gCarla.utils = CarlaUtils(os.path.join(pathBinaries, utilsname)) gCarla.utils.set_process_name(initName) + gCarla.utils.set_locale_C() # -------------------------------------------------------------------------------------------------------- # Done diff --git a/source/carla_shared.py b/source/carla_shared.py index 50b6d5edf..457e8868c 100644 --- a/source/carla_shared.py +++ b/source/carla_shared.py @@ -503,6 +503,16 @@ if not CWD: if os.path.isfile(CWD): CWD = os.path.dirname(CWD) +# ------------------------------------------------------------------------------------------------------------ +# Set DLL_EXTENSION + +if WINDOWS: + DLL_EXTENSION = "dll" +elif MACOS: + DLL_EXTENSION = "dylib" +else: + DLL_EXTENSION = "so" + # ------------------------------------------------------------------------------------------------------------ # Check if a value is a number (float support) @@ -530,6 +540,46 @@ def toList(value): def getIcon(icon, size = 16): return QIcon.fromTheme(icon, QIcon(":/%ix%i/%s.png" % (size, size, icon))) +# ------------------------------------------------------------------------------------------------------------ +# Get paths (binaries, resources) + +def getPaths(libPrefix = None): + CWDl = CWD.lower() + + # standalone, installed system-wide linux + if libPrefix is not None: + pathBinaries = os.path.join(libPrefix, "lib", "carla") + pathResources = os.path.join(libPrefix, "share", "carla", "resources") + + # standalone, local source + elif CWDl.endswith("source"): + pathBinaries = os.path.abspath(os.path.join(CWD, "..", "bin")) + pathResources = os.path.join(pathBinaries, "resources") + + # plugin + elif CWDl.endswith("resources"): + # installed system-wide linux + if CWDl.endswith("/share/carla/resources"): + pathBinaries = os.path.abspath(os.path.join(CWD, "..", "..", "..", "lib", "carla")) + pathResources = CWD + + # local source + elif CWDl.endswith("native-plugins%sresources" % os.sep): + pathBinaries = os.path.abspath(os.path.join(CWD, "..", "..", "..", "..", "bin")) + pathResources = CWD + + # other + else: + pathBinaries = os.path.abspath(os.path.join(CWD, "..")) + pathResources = CWD + + # everything else + else: + pathBinaries = CWD + pathResources = os.path.join(pathBinaries, "resources") + + return (pathBinaries, pathResources) + # ------------------------------------------------------------------------------------------------------------ # Signal handler # TODO move to carla_host.py or something diff --git a/source/carla_utils.py b/source/carla_utils.py index 78d4d0e27..26a41a178 100644 --- a/source/carla_utils.py +++ b/source/carla_utils.py @@ -16,6 +16,11 @@ # # For a full copy of the GNU General Public License see the doc/GPL.txt file. +# ------------------------------------------------------------------------------------------------------------ +# Imports (Global) + +from sys import argv + # ------------------------------------------------------------------------------------------------------------ # Imports (Custom) @@ -82,6 +87,12 @@ def getPluginTypeFromString(stype): print("getPluginTypeFromString(\"%s\") - invalid string type" % stype) return PLUGIN_NONE +# ------------------------------------------------------------------------------------------------------------ +# Carla Pipe stuff + +CarlaPipeClientHandle = c_void_p +CarlaPipeCallbackFunc = CFUNCTYPE(None, c_void_p, c_char_p) + # ------------------------------------------------------------------------------------------------------------ # Carla Utils object using a DLL @@ -91,19 +102,52 @@ class CarlaUtils(object): self.lib = cdll.LoadLibrary(filename) - self.lib.carla_set_process_name.argtypes = [c_char_p] - self.lib.carla_set_process_name.restype = None - self.lib.carla_get_library_filename.argtypes = None self.lib.carla_get_library_filename.restype = c_char_p self.lib.carla_get_library_folder.argtypes = None self.lib.carla_get_library_folder.restype = c_char_p - # -------------------------------------------------------------------------------------------------------- + self.lib.carla_set_locale_C.argtypes = None + self.lib.carla_set_locale_C.restype = None - def set_process_name(self, name): - self.lib.carla_set_process_name(name.encode("utf-8")) + self.lib.carla_set_process_name.argtypes = [c_char_p] + self.lib.carla_set_process_name.restype = None + + self.lib.carla_pipe_client_new.argtypes = [POINTER(c_char_p), CarlaPipeCallbackFunc, c_void_p] + self.lib.carla_pipe_client_new.restype = CarlaPipeClientHandle + + self.lib.carla_pipe_client_idle.argtypes = [CarlaPipeClientHandle] + self.lib.carla_pipe_client_idle.restype = None + + self.lib.carla_pipe_client_is_running.argtypes = [CarlaPipeClientHandle] + self.lib.carla_pipe_client_is_running.restype = c_bool + + self.lib.carla_pipe_client_lock.argtypes = [CarlaPipeClientHandle] + self.lib.carla_pipe_client_lock.restype = None + + self.lib.carla_pipe_client_unlock.argtypes = [CarlaPipeClientHandle] + self.lib.carla_pipe_client_unlock.restype = None + + self.lib.carla_pipe_client_readlineblock.argtypes = [CarlaPipeClientHandle, c_uint] + self.lib.carla_pipe_client_readlineblock.restype = c_char_p + + self.lib.carla_pipe_client_write_msg.argtypes = [CarlaPipeClientHandle, c_char_p] + self.lib.carla_pipe_client_write_msg.restype = c_bool + + self.lib.carla_pipe_client_write_and_fix_msg.argtypes = [CarlaPipeClientHandle, c_char_p] + self.lib.carla_pipe_client_write_and_fix_msg.restype = c_bool + + self.lib.carla_pipe_client_flush.argtypes = [CarlaPipeClientHandle] + self.lib.carla_pipe_client_flush.restype = c_bool + + self.lib.carla_pipe_client_flush_and_unlock.argtypes = [CarlaPipeClientHandle] + self.lib.carla_pipe_client_flush_and_unlock.restype = c_bool + + self.lib.carla_pipe_client_destroy.argtypes = [CarlaPipeClientHandle] + self.lib.carla_pipe_client_destroy.restype = None + + # -------------------------------------------------------------------------------------------------------- def get_library_filename(self): return charPtrToString(self.lib.carla_get_library_filename()) @@ -111,4 +155,51 @@ class CarlaUtils(object): def get_library_folder(self): return charPtrToString(self.lib.carla_get_library_folder()) + def set_locale_C(self): + self.lib.carla_set_locale_C() + + def set_process_name(self, name): + self.lib.carla_set_process_name(name.encode("utf-8")) + + def pipe_client_new(self, func): + argc = len(argv) + cagrvtype = c_char_p * argc + cargv = cagrvtype() + + for i in range(argc): + cargv[i] = c_char_p(argv[i].encode("utf-8")) + + self._pipeClientCallback = CarlaPipeCallbackFunc(func) + return self.lib.carla_pipe_client_new(cargv, self._pipeClientCallback, None) + + def pipe_client_idle(self, handle): + self.lib.carla_pipe_client_idle(handle) + + def pipe_client_is_running(self, handle): + return bool(self.lib.carla_pipe_client_is_running(handle)) + + def pipe_client_lock(self, handle): + self.lib.carla_pipe_client_lock(handle) + + def pipe_client_unlock(self, handle): + self.lib.carla_pipe_client_unlock(handle) + + def pipe_client_readlineblock(self, handle, timeout): + return charPtrToString(self.lib.carla_pipe_client_readlineblock(handle, timeout)) + + def pipe_client_write_msg(self, handle, msg): + return bool(self.lib.carla_pipe_client_write_msg(handle, msg.encode("utf-8"))) + + def pipe_client_write_and_fix_msg(self, handle, msg): + return bool(self.lib.carla_pipe_client_write_and_fix_msg(handle, msg.encode("utf-8"))) + + def pipe_client_flush(self, handle): + return bool(self.lib.carla_pipe_client_flush(handle)) + + def pipe_client_flush_and_unlock(self, handle): + return bool(self.lib.carla_pipe_client_flush_and_unlock(handle)) + + def pipe_client_destroy(self, handle): + self.lib.carla_pipe_client_destroy(handle) + # ------------------------------------------------------------------------------------------------------------ diff --git a/source/externalui.py b/source/externalui.py index dd0775500..40041f9d9 100755 --- a/source/externalui.py +++ b/source/externalui.py @@ -16,204 +16,174 @@ # # For a full copy of the GNU General Public License see the doc/GPL.txt file. -# ----------------------------------------------------------------------- -# Imports +# ------------------------------------------------------------------------------------------------------------ +# Imports (Global) -from os import fdopen, O_NONBLOCK -from fcntl import fcntl, F_GETFL, F_SETFL from sys import argv -from time import sleep -# ----------------------------------------------------------------------- +# ------------------------------------------------------------------------------------------------------------ +# Imports (Custom Stuff) + +from carla_shared import * + +# ------------------------------------------------------------------------------------------------------------ # External UI class ExternalUI(object): def __init__(self): object.__init__(self) - self.fPipeRecv = None - self.fPipeSend = None self.fQuitReceived = False if len(argv) > 1: self.fSampleRate = float(argv[1]) self.fUiName = argv[2] - - pipeRecvServer = int(argv[3]) - pipeRecvClient = int(argv[4]) - pipeSendServer = int(argv[5]) - pipeSendClient = int(argv[6]) - - oldFlags = fcntl(pipeRecvServer, F_GETFL) - fcntl(pipeRecvServer, F_SETFL, oldFlags | O_NONBLOCK) - - self.fPipeRecv = fdopen(pipeRecvServer, 'r') - self.fPipeSend = fdopen(pipeSendServer, 'w') - + self.fPipeClient = gCarla.utils.pipe_client_new(lambda s,msg: self.msgCallback(msg)) else: self.fSampleRate = 44100.0 self.fUiName = "TestUI" + self.fPipeClient = None - self.fPipeRecv = None - self.fPipeSend = None + # ------------------------------------------------------------------- + # Public methods def ready(self): - if self.fPipeRecv is not None: + if self.fPipeClient is not None: # send empty line (just newline char) self.send([""]) else: # testing, show UI only - self.d_uiShow() + self.uiShow() + + def isRunning(self): + if self.fPipeClient is not None: + return gCarla.utils.pipe_client_is_running(self.fPipeClient) + return False + + def idleExternalUI(self): + if self.fPipeClient is not None: + gCarla.utils.pipe_client_idle(self.fPipeClient) + + def closeExternalUI(self): + if not self.fQuitReceived: + self.send(["exiting"]) + + if self.fPipeClient is not None: + gCarla.utils.pipe_client_destroy(self.fPipeClient) + self.fPipeClient = None # ------------------------------------------------------------------- # Host DSP State - def d_getSampleRate(self): + def getSampleRate(self): return self.fSampleRate - def d_editParameter(self, index, started): - self.send(["editParam", index, started]) - - def d_setParameterValue(self, index, value): + def sendControl(self, index, value): self.send(["control", index, value]) - def d_setProgram(self, channel, bank, program): + def sendProgram(self, channel, bank, program): self.send(["program", channel, bank, program]) - def d_setState(self, key, value): + def sendConfigure(self, key, value): self.send(["configure", key, value]) - def d_sendNote(self, onOff, channel, note, velocity): + def sendNote(self, onOff, channel, note, velocity): self.send(["note", onOff, channel, note, velocity]) # ------------------------------------------------------------------- # DSP Callbacks - def d_parameterChanged(self, index, value): + def dspParameterChanged(self, index, value): return - def d_programChanged(self, channel, bank, program): + def dspProgramChanged(self, channel, bank, program): return - def d_stateChanged(self, key, value): + def dspStateChanged(self, key, value): return - def d_noteReceived(self, onOff, channel, note, velocity): + def dspNoteReceived(self, onOff, channel, note, velocity): return # ------------------------------------------------------------------- # ExternalUI Callbacks - def d_uiShow(self): + def uiShow(self): return - def d_uiHide(self): + def uiHide(self): return - def d_uiQuit(self): - return + def uiQuit(self): + self.closeExternalUI() - def d_uiTitleChanged(self, uiTitle): + def uiTitleChanged(self, uiTitle): return # ------------------------------------------------------------------- - # Public methods + # Callback - def closeExternalUI(self): - if not self.fQuitReceived: - self.send(["exiting"]) + def msgCallback(self, msg): + msg = charPtrToString(msg) - if self.fPipeRecv is not None: - self.fPipeRecv.close() - self.fPipeRecv = None + #if not msg: + #return - if self.fPipeSend is not None: - self.fPipeSend.close() - self.fPipeSend = None + if msg == "control": + index = int(self.readlineblock()) + value = float(self.readlineblock()) + self.dspParameterChanged(index, value) - def idleExternalUI(self): - while True: - if self.fPipeRecv is None: - return True - - try: - msg = self.fPipeRecv.readline().strip() - except IOError: - return False - - if not msg: - return True - - elif msg == "control": - index = int(self.readlineblock()) - value = float(self.readlineblock()) - self.d_parameterChanged(index, value) - - elif msg == "program": - channel = int(self.readlineblock()) - bank = int(self.readlineblock()) - program = int(self.readlineblock()) - self.d_programChanged(channel, bank, program) - - elif msg == "configure": - key = self.readlineblock().replace("\r", "\n") - value = self.readlineblock().replace("\r", "\n") - self.d_stateChanged(key, value) - - elif msg == "note": - onOff = bool(self.readlineblock() == "true") - channel = int(self.readlineblock()) - note = int(self.readlineblock()) - velocity = int(self.readlineblock()) - self.d_noteReceived(onOff, channel, note, velocity) - - elif msg == "show": - self.d_uiShow() - - elif msg == "hide": - self.d_uiHide() - - elif msg == "quit": - self.fQuitReceived = True - self.d_uiQuit() - - elif msg == "uiTitle": - uiTitle = self.readlineblock().replace("\r", "\n") - self.d_uiTitleChanged(uiTitle) + elif msg == "program": + channel = int(self.readlineblock()) + bank = int(self.readlineblock()) + program = int(self.readlineblock()) + self.dspProgramChanged(channel, bank, program) - else: - print("unknown message: \"" + msg + "\"") + elif msg == "configure": + key = self.readlineblock() #.replace("\r", "\n") + value = self.readlineblock() #.replace("\r", "\n") + self.dspStateChanged(key, value) + + elif msg == "note": + onOff = bool(self.readlineblock() == "true") + channel = int(self.readlineblock()) + note = int(self.readlineblock()) + velocity = int(self.readlineblock()) + self.dspNoteReceived(onOff, channel, note, velocity) + + elif msg == "show": + self.uiShow() - return True + elif msg == "hide": + self.uiHide() + + elif msg == "quit": + self.fQuitReceived = True + self.uiQuit() + + elif msg == "uiTitle": + uiTitle = self.readlineblock() #.replace("\r", "\n") + self.uiTitleChanged(uiTitle) + + else: + print("unknown message: \"" + msg + "\"") # ------------------------------------------------------------------- # Internal stuff def readlineblock(self): - if self.fPipeRecv is None: + if self.fPipeClient is None: return "" - # try a maximum of 20 times - # 20 * 50ms = 1000ms - for x in range(20): - try: - msg = self.fPipeRecv.readline() - except IOError: - msg = "" - - if msg: - return msg.strip() - - # try again in 50 ms - sleep(0.050) - - print("readlineblock timed out") - return "" + return gCarla.utils.pipe_client_readlineblock(self.fPipeClient, 50) def send(self, lines): - if self.fPipeSend is None: + if self.fPipeClient is None or len(lines) == 0: return + gCarla.utils.pipe_client_lock(self.fPipeClient) + for line in lines: if line is None: line2 = "(null)" @@ -229,6 +199,6 @@ class ExternalUI(object): print("unknown data type to send:", type(line)) return - self.fPipeSend.write(line2 + "\n") + gCarla.utils.pipe_client_write_msg(self.fPipeClient, line2 + "\n") - self.fPipeSend.flush() + gCarla.utils.pipe_client_flush_and_unlock(self.fPipeClient) diff --git a/source/modules/native-plugins/resources/bigmeter-ui b/source/modules/native-plugins/resources/bigmeter-ui index 9481ee173..533b5e010 100755 --- a/source/modules/native-plugins/resources/bigmeter-ui +++ b/source/modules/native-plugins/resources/bigmeter-ui @@ -16,6 +16,12 @@ # # For a full copy of the GNU General Public License see the doc/GPL.txt file. +# ----------------------------------------------------------------------- +# Imports (Custom) + +from carla_shared import * +from carla_utils import * + # ----------------------------------------------------------------------- # Imports (ExternalUI) @@ -49,7 +55,7 @@ class DistrhoUIBigMeter(ExternalUI, DigitalPeakMeter): # ------------------------------------------------------------------- # DSP Callbacks - def d_parameterChanged(self, index, value): + def dspParameterChanged(self, index, value): if index == 0: color = int(value) @@ -72,25 +78,26 @@ class DistrhoUIBigMeter(ExternalUI, DigitalPeakMeter): # ------------------------------------------------------------------- # ExternalUI Callbacks - def d_uiShow(self): + def uiShow(self): self.show() - def d_uiHide(self): + def uiHide(self): self.hide() - def d_uiQuit(self): + def uiQuit(self): + self.closeExternalUI() self.close() app.quit() - def d_uiTitleChanged(self, uiTitle): + def uiTitleChanged(self, uiTitle): self.setWindowTitle(uiTitle) # ------------------------------------------------------------------- # Qt events def timerEvent(self, event): - if event.timerId() == self.fIdleTimer and not self.idleExternalUI(): - self.d_uiQuit() + if event.timerId() == self.fIdleTimer: + self.idleExternalUI() DigitalPeakMeter.timerEvent(self, event) def closeEvent(self, event): @@ -100,6 +107,12 @@ class DistrhoUIBigMeter(ExternalUI, DigitalPeakMeter): #--------------- main ------------------ if __name__ == '__main__': import resources_rc + + pathBinaries, pathResources = getPaths() + gCarla.utils = CarlaUtils(os.path.join(pathBinaries, "libcarla_utils." + DLL_EXTENSION)) + gCarla.utils.set_locale_C() + gCarla.utils.set_process_name("BigMeter") + app = CarlaApplication("BigMeter") gui = DistrhoUIBigMeter() app.exit_exec() diff --git a/source/modules/native-plugins/resources/carla-plugin b/source/modules/native-plugins/resources/carla-plugin index 563bcd9a8..5fd2fc328 100755 --- a/source/modules/native-plugins/resources/carla-plugin +++ b/source/modules/native-plugins/resources/carla-plugin @@ -36,7 +36,6 @@ class PluginHost(CarlaHostPlugin): # --------------------------------------------------------------- self.fExternalUI = None - self.fIsRunning = True # ------------------------------------------------------------------- @@ -53,22 +52,16 @@ class PluginHost(CarlaHostPlugin): # ------------------------------------------------------------------- def engine_init(self, driverName, clientName): - self.fIsRunning = True return True def engine_close(self): - self.fIsRunning = False return True def engine_idle(self): - if self.fExternalUI.idleExternalUI(): - return - - self.fIsRunning = False - self.fExternalUI.d_uiQuit() + self.fExternalUI.idleExternalUI() def is_engine_running(self): - return self.fIsRunning + return self.fExternalUI.isRunning() def set_engine_about_to_close(self): return @@ -97,19 +90,20 @@ class CarlaMiniW(ExternalUI, HostWindow): # ------------------------------------------------------------------- # ExternalUI Callbacks - def d_uiShow(self): + def uiShow(self): if self.parent() is None: self.show() - def d_uiHide(self): + def uiHide(self): if self.parent() is None: self.hide() - def d_uiQuit(self): + def uiQuit(self): + self.closeExternalUI() self.close() app.quit() - def d_uiTitleChanged(self, uiTitle): + def uiTitleChanged(self, uiTitle): self.setWindowTitle(uiTitle) # ------------------------------------------------------------------- @@ -120,248 +114,239 @@ class CarlaMiniW(ExternalUI, HostWindow): HostWindow.closeEvent(self, event) # ------------------------------------------------------------------- - # Custom idler - - def idleExternalUI(self): - while True: - if self.fPipeRecv is None: - return True - - try: - msg = self.fPipeRecv.readline().strip() - except IOError: - return False - - if not msg: - return True - - elif msg.startswith("PEAKS_"): - pluginId = int(msg.replace("PEAKS_", "")) - in1, in2, out1, out2 = [float(i) for i in self.readlineblock().split(":")] - self.host._set_peaks(pluginId, in1, in2, out1, out2) - - elif msg.startswith("PARAMVAL_"): - pluginId, paramId = [int(i) for i in msg.replace("PARAMVAL_", "").split(":")] - paramValue = float(self.readlineblock()) - if paramId < 0: - self.host._set_internalValue(pluginId, paramId, paramValue) - else: - self.host._set_parameterValue(pluginId, paramId, paramValue) - - elif msg.startswith("ENGINE_CALLBACK_"): - action = int(msg.replace("ENGINE_CALLBACK_", "")) - pluginId = int(self.readlineblock()) - value1 = int(self.readlineblock()) - value2 = int(self.readlineblock()) - value3 = float(self.readlineblock()) - valueStr = self.readlineblock().replace("\r", "\n") - - if action == ENGINE_CALLBACK_PLUGIN_RENAMED: - self.host._set_pluginName(pluginId, valueStr) - elif action == ENGINE_CALLBACK_PARAMETER_VALUE_CHANGED: - if value1 < 0: - self.host._set_internalValue(pluginId, value1, value3) - else: - self.host._set_parameterValue(pluginId, value1, value3) - elif action == ENGINE_CALLBACK_PARAMETER_DEFAULT_CHANGED: - self.host._set_parameterDefault(pluginId, value1, value3) - elif action == ENGINE_CALLBACK_PARAMETER_MIDI_CC_CHANGED: - self.host._set_parameterMidiCC(pluginId, value1, value2) - elif action == ENGINE_CALLBACK_PARAMETER_MIDI_CHANNEL_CHANGED: - self.host._set_parameterMidiChannel(pluginId, value1, value2) - elif action == ENGINE_CALLBACK_PROGRAM_CHANGED: - self.host._set_currentProgram(pluginId, value1) - elif action == ENGINE_CALLBACK_MIDI_PROGRAM_CHANGED: - self.host._set_currentMidiProgram(pluginId, value1) - - engineCallback(self.host, action, pluginId, value1, value2, value3, valueStr) - - elif msg.startswith("ENGINE_OPTION_"): - option = int(msg.replace("ENGINE_OPTION_", "")) - forced = bool(self.readlineblock() == "true") - value = self.readlineblock() - - if self.fFirstInit and not forced: - continue - - if option == ENGINE_OPTION_PROCESS_MODE: - self.host.processMode = int(value) - elif option == ENGINE_OPTION_TRANSPORT_MODE: - self.host.transportMode = int(value) - elif option == ENGINE_OPTION_FORCE_STEREO: - self.host.forceStereo = bool(value == "true") - elif option == ENGINE_OPTION_PREFER_PLUGIN_BRIDGES: - self.host.preferPluginBridges = bool(value == "true") - elif option == ENGINE_OPTION_PREFER_UI_BRIDGES: - self.host.preferUIBridges = bool(value == "true") - elif option == ENGINE_OPTION_UIS_ALWAYS_ON_TOP: - self.host.uisAlwaysOnTop = bool(value == "true") - elif option == ENGINE_OPTION_MAX_PARAMETERS: - self.host.maxParameters = int(value) - elif option == ENGINE_OPTION_UI_BRIDGES_TIMEOUT: - self.host.uiBridgesTimeout = int(value) - elif option == ENGINE_OPTION_PATH_BINARIES: - self.host.pathBinaries = value - elif option == ENGINE_OPTION_PATH_RESOURCES: - self.host.pathResources = value - - elif msg.startswith("PLUGIN_INFO_"): - pluginId = int(msg.replace("PLUGIN_INFO_", "")) - self.host._add(pluginId) - - type_, category, hints, uniqueId, optsAvail, optsEnabled = [int(i) for i in self.readlineblock().split(":")] - filename = self.readlineblock().replace("\r", "\n") - name = self.readlineblock().replace("\r", "\n") - iconName = self.readlineblock().replace("\r", "\n") - realName = self.readlineblock().replace("\r", "\n") - label = self.readlineblock().replace("\r", "\n") - maker = self.readlineblock().replace("\r", "\n") - copyright = self.readlineblock().replace("\r", "\n") - - pinfo = { - 'type': type_, - 'category': category, - 'hints': hints, - 'optionsAvailable': optsAvail, - 'optionsEnabled': optsEnabled, - 'filename': filename, - 'name': name, - 'label': label, - 'maker': maker, - 'copyright': copyright, - 'iconName': iconName, - 'patchbayClientId': 0, - 'uniqueId': uniqueId - } - self.host._set_pluginInfo(pluginId, pinfo) - self.host._set_pluginRealName(pluginId, realName) - - elif msg.startswith("AUDIO_COUNT_"): - pluginId, ins, outs = [int(i) for i in msg.replace("AUDIO_COUNT_", "").split(":")] - self.host._set_audioCountInfo(pluginId, {'ins': ins, 'outs': outs}) - - elif msg.startswith("MIDI_COUNT_"): - pluginId, ins, outs = [int(i) for i in msg.replace("MIDI_COUNT_", "").split(":")] - self.host._set_midiCountInfo(pluginId, {'ins': ins, 'outs': outs}) - - elif msg.startswith("PARAMETER_COUNT_"): - pluginId, ins, outs, count = [int(i) for i in msg.replace("PARAMETER_COUNT_", "").split(":")] - self.host._set_parameterCountInfo(pluginId, count, {'ins': ins, 'outs': outs}) - - elif msg.startswith("PARAMETER_DATA_"): - pluginId, paramId = [int(i) for i in msg.replace("PARAMETER_DATA_", "").split(":")] - paramType, paramHints, midiChannel, midiCC = [int(i) for i in self.readlineblock().split(":")] - paramName = self.readlineblock().replace("\r", "\n") - paramUnit = self.readlineblock().replace("\r", "\n") - - paramInfo = { - 'name': paramName, - 'symbol': "", - 'unit': paramUnit, - 'scalePointCount': 0, - } - self.host._set_parameterInfo(pluginId, paramId, paramInfo) - - paramData = { - 'type': paramType, - 'hints': paramHints, - 'index': paramId, - 'rindex': -1, - 'midiCC': midiCC, - 'midiChannel': midiChannel - } - self.host._set_parameterData(pluginId, paramId, paramData) - - elif msg.startswith("PARAMETER_RANGES_"): - pluginId, paramId = [int(i) for i in msg.replace("PARAMETER_RANGES_", "").split(":")] - def_, min_, max_, step, stepSmall, stepLarge = [float(i) for i in self.readlineblock().split(":")] - - paramRanges = { - 'def': def_, - 'min': min_, - 'max': max_, - 'step': step, - 'stepSmall': stepSmall, - 'stepLarge': stepLarge - } - self.host._set_parameterRanges(pluginId, paramId, paramRanges) - - elif msg.startswith("PROGRAM_COUNT_"): - pluginId, count, current = [int(i) for i in msg.replace("PROGRAM_COUNT_", "").split(":")] - self.host._set_programCount(pluginId, count) - self.host._set_currentProgram(pluginId, current) - - elif msg.startswith("PROGRAM_NAME_"): - pluginId, progId = [int(i) for i in msg.replace("PROGRAM_NAME_", "").split(":")] - progName = self.readlineblock().replace("\r", "\n") - self.host._set_programName(pluginId, progId, progName) - - elif msg.startswith("MIDI_PROGRAM_COUNT_"): - pluginId, count, current = [int(i) for i in msg.replace("MIDI_PROGRAM_COUNT_", "").split(":")] - self.host._set_midiProgramCount(pluginId, count) - self.host._set_currentMidiProgram(pluginId, current) - - elif msg.startswith("MIDI_PROGRAM_DATA_"): - pluginId, midiProgId = [int(i) for i in msg.replace("MIDI_PROGRAM_DATA_", "").split(":")] - bank, program = [int(i) for i in self.readlineblock().split(":")] - name = self.readlineblock().replace("\r", "\n") - self.host._set_midiProgramData(pluginId, midiProgId, {'bank': bank, 'program': program, 'name': name}) - - elif msg == "complete-license": - license = self.readlineblock().replace("\r", "\n") - self.host.fCompleteLicenseText = license - - elif msg == "juce-version": - version = self.readlineblock().replace("\r", "\n") - self.host.fJuceVersion = version - - elif msg == "file-exts": - exts = self.readlineblock().replace("\r", "\n") - self.host.fSupportedFileExts = exts - # only now we know the supported extensions - self.fDirModel.setNameFilters(exts.split(";")) - - elif msg == "max-plugin-number": - maxnum = int(self.readlineblock()) - self.host.fMaxPluginNumber = maxnum - - elif msg == "buffer-size": - bufsize = int(self.readlineblock()) - self.host.fBufferSize = bufsize - - elif msg == "sample-rate": - srate = float(self.readlineblock()) - self.host.fSampleRate = srate - - elif msg == "transport": - playing = bool(self.readlineblock() == "true") - frame, bar, beat, tick = [int(i) for i in self.readlineblock().split(":")] - bpm = float(self.readlineblock()) - self.host._set_transport(playing, frame, bar, beat, tick, bpm) - - elif msg == "error": - error = self.readlineblock().replace("\r", "\n") - engineCallback(self.host, ENGINE_CALLBACK_ERROR, 0, 0, 0, 0.0, error) - - elif msg == "show": - self.fFirstInit = False - self.d_uiShow() - - elif msg == "hide": - self.d_uiHide() - - elif msg == "quit": - self.fQuitReceived = True - self.d_uiQuit() - - elif msg == "uiTitle": - uiTitle = self.readlineblock().replace("\r", "\n") - self.d_uiTitleChanged(uiTitle) + # Custom callback - else: - print("unknown message: \"" + msg + "\"") + def msgCallback(self, msg): + msg = charPtrToString(msg) - return True + #if not msg: + #return + + if msg.startswith("PEAKS_"): + pluginId = int(msg.replace("PEAKS_", "")) + in1, in2, out1, out2 = [float(i) for i in self.readlineblock().split(":")] + self.host._set_peaks(pluginId, in1, in2, out1, out2) + + elif msg.startswith("PARAMVAL_"): + pluginId, paramId = [int(i) for i in msg.replace("PARAMVAL_", "").split(":")] + paramValue = float(self.readlineblock()) + if paramId < 0: + self.host._set_internalValue(pluginId, paramId, paramValue) + else: + self.host._set_parameterValue(pluginId, paramId, paramValue) + + elif msg.startswith("ENGINE_CALLBACK_"): + action = int(msg.replace("ENGINE_CALLBACK_", "")) + pluginId = int(self.readlineblock()) + value1 = int(self.readlineblock()) + value2 = int(self.readlineblock()) + value3 = float(self.readlineblock()) + valueStr = self.readlineblock().replace("\r", "\n") + + if action == ENGINE_CALLBACK_PLUGIN_RENAMED: + self.host._set_pluginName(pluginId, valueStr) + elif action == ENGINE_CALLBACK_PARAMETER_VALUE_CHANGED: + if value1 < 0: + self.host._set_internalValue(pluginId, value1, value3) + else: + self.host._set_parameterValue(pluginId, value1, value3) + elif action == ENGINE_CALLBACK_PARAMETER_DEFAULT_CHANGED: + self.host._set_parameterDefault(pluginId, value1, value3) + elif action == ENGINE_CALLBACK_PARAMETER_MIDI_CC_CHANGED: + self.host._set_parameterMidiCC(pluginId, value1, value2) + elif action == ENGINE_CALLBACK_PARAMETER_MIDI_CHANNEL_CHANGED: + self.host._set_parameterMidiChannel(pluginId, value1, value2) + elif action == ENGINE_CALLBACK_PROGRAM_CHANGED: + self.host._set_currentProgram(pluginId, value1) + elif action == ENGINE_CALLBACK_MIDI_PROGRAM_CHANGED: + self.host._set_currentMidiProgram(pluginId, value1) + + engineCallback(self.host, action, pluginId, value1, value2, value3, valueStr) + + elif msg.startswith("ENGINE_OPTION_"): + option = int(msg.replace("ENGINE_OPTION_", "")) + forced = bool(self.readlineblock() == "true") + value = self.readlineblock() + + if self.fFirstInit and not forced: + return + + if option == ENGINE_OPTION_PROCESS_MODE: + self.host.processMode = int(value) + elif option == ENGINE_OPTION_TRANSPORT_MODE: + self.host.transportMode = int(value) + elif option == ENGINE_OPTION_FORCE_STEREO: + self.host.forceStereo = bool(value == "true") + elif option == ENGINE_OPTION_PREFER_PLUGIN_BRIDGES: + self.host.preferPluginBridges = bool(value == "true") + elif option == ENGINE_OPTION_PREFER_UI_BRIDGES: + self.host.preferUIBridges = bool(value == "true") + elif option == ENGINE_OPTION_UIS_ALWAYS_ON_TOP: + self.host.uisAlwaysOnTop = bool(value == "true") + elif option == ENGINE_OPTION_MAX_PARAMETERS: + self.host.maxParameters = int(value) + elif option == ENGINE_OPTION_UI_BRIDGES_TIMEOUT: + self.host.uiBridgesTimeout = int(value) + elif option == ENGINE_OPTION_PATH_BINARIES: + self.host.pathBinaries = value + elif option == ENGINE_OPTION_PATH_RESOURCES: + self.host.pathResources = value + + elif msg.startswith("PLUGIN_INFO_"): + pluginId = int(msg.replace("PLUGIN_INFO_", "")) + self.host._add(pluginId) + + type_, category, hints, uniqueId, optsAvail, optsEnabled = [int(i) for i in self.readlineblock().split(":")] + filename = self.readlineblock().replace("\r", "\n") + name = self.readlineblock().replace("\r", "\n") + iconName = self.readlineblock().replace("\r", "\n") + realName = self.readlineblock().replace("\r", "\n") + label = self.readlineblock().replace("\r", "\n") + maker = self.readlineblock().replace("\r", "\n") + copyright = self.readlineblock().replace("\r", "\n") + + pinfo = { + 'type': type_, + 'category': category, + 'hints': hints, + 'optionsAvailable': optsAvail, + 'optionsEnabled': optsEnabled, + 'filename': filename, + 'name': name, + 'label': label, + 'maker': maker, + 'copyright': copyright, + 'iconName': iconName, + 'patchbayClientId': 0, + 'uniqueId': uniqueId + } + self.host._set_pluginInfo(pluginId, pinfo) + self.host._set_pluginRealName(pluginId, realName) + + elif msg.startswith("AUDIO_COUNT_"): + pluginId, ins, outs = [int(i) for i in msg.replace("AUDIO_COUNT_", "").split(":")] + self.host._set_audioCountInfo(pluginId, {'ins': ins, 'outs': outs}) + + elif msg.startswith("MIDI_COUNT_"): + pluginId, ins, outs = [int(i) for i in msg.replace("MIDI_COUNT_", "").split(":")] + self.host._set_midiCountInfo(pluginId, {'ins': ins, 'outs': outs}) + + elif msg.startswith("PARAMETER_COUNT_"): + pluginId, ins, outs, count = [int(i) for i in msg.replace("PARAMETER_COUNT_", "").split(":")] + self.host._set_parameterCountInfo(pluginId, count, {'ins': ins, 'outs': outs}) + + elif msg.startswith("PARAMETER_DATA_"): + pluginId, paramId = [int(i) for i in msg.replace("PARAMETER_DATA_", "").split(":")] + paramType, paramHints, midiChannel, midiCC = [int(i) for i in self.readlineblock().split(":")] + paramName = self.readlineblock().replace("\r", "\n") + paramUnit = self.readlineblock().replace("\r", "\n") + + paramInfo = { + 'name': paramName, + 'symbol': "", + 'unit': paramUnit, + 'scalePointCount': 0, + } + self.host._set_parameterInfo(pluginId, paramId, paramInfo) + + paramData = { + 'type': paramType, + 'hints': paramHints, + 'index': paramId, + 'rindex': -1, + 'midiCC': midiCC, + 'midiChannel': midiChannel + } + self.host._set_parameterData(pluginId, paramId, paramData) + + elif msg.startswith("PARAMETER_RANGES_"): + pluginId, paramId = [int(i) for i in msg.replace("PARAMETER_RANGES_", "").split(":")] + def_, min_, max_, step, stepSmall, stepLarge = [float(i) for i in self.readlineblock().split(":")] + + paramRanges = { + 'def': def_, + 'min': min_, + 'max': max_, + 'step': step, + 'stepSmall': stepSmall, + 'stepLarge': stepLarge + } + self.host._set_parameterRanges(pluginId, paramId, paramRanges) + + elif msg.startswith("PROGRAM_COUNT_"): + pluginId, count, current = [int(i) for i in msg.replace("PROGRAM_COUNT_", "").split(":")] + self.host._set_programCount(pluginId, count) + self.host._set_currentProgram(pluginId, current) + + elif msg.startswith("PROGRAM_NAME_"): + pluginId, progId = [int(i) for i in msg.replace("PROGRAM_NAME_", "").split(":")] + progName = self.readlineblock().replace("\r", "\n") + self.host._set_programName(pluginId, progId, progName) + + elif msg.startswith("MIDI_PROGRAM_COUNT_"): + pluginId, count, current = [int(i) for i in msg.replace("MIDI_PROGRAM_COUNT_", "").split(":")] + self.host._set_midiProgramCount(pluginId, count) + self.host._set_currentMidiProgram(pluginId, current) + + elif msg.startswith("MIDI_PROGRAM_DATA_"): + pluginId, midiProgId = [int(i) for i in msg.replace("MIDI_PROGRAM_DATA_", "").split(":")] + bank, program = [int(i) for i in self.readlineblock().split(":")] + name = self.readlineblock().replace("\r", "\n") + self.host._set_midiProgramData(pluginId, midiProgId, {'bank': bank, 'program': program, 'name': name}) + + elif msg == "complete-license": + license = self.readlineblock().replace("\r", "\n") + self.host.fCompleteLicenseText = license + + elif msg == "juce-version": + version = self.readlineblock().replace("\r", "\n") + self.host.fJuceVersion = version + + elif msg == "file-exts": + exts = self.readlineblock().replace("\r", "\n") + self.host.fSupportedFileExts = exts + # only now we know the supported extensions + self.fDirModel.setNameFilters(exts.split(";")) + + elif msg == "max-plugin-number": + maxnum = int(self.readlineblock()) + self.host.fMaxPluginNumber = maxnum + + elif msg == "buffer-size": + bufsize = int(self.readlineblock()) + self.host.fBufferSize = bufsize + + elif msg == "sample-rate": + srate = float(self.readlineblock()) + self.host.fSampleRate = srate + + elif msg == "transport": + playing = bool(self.readlineblock() == "true") + frame, bar, beat, tick = [int(i) for i in self.readlineblock().split(":")] + bpm = float(self.readlineblock()) + self.host._set_transport(playing, frame, bar, beat, tick, bpm) + + elif msg == "error": + error = self.readlineblock().replace("\r", "\n") + engineCallback(self.host, ENGINE_CALLBACK_ERROR, 0, 0, 0, 0.0, error) + + elif msg == "show": + self.fFirstInit = False + self.uiShow() + + elif msg == "hide": + self.uiHide() + + elif msg == "quit": + self.fQuitReceived = True + self.uiQuit() + + elif msg == "uiTitle": + uiTitle = self.readlineblock().replace("\r", "\n") + self.uiTitleChanged(uiTitle) + + else: + print("unknown message: \"" + msg + "\"") # ------------------------------------------------------------------------------------------------------------ # Embed plugin UI diff --git a/source/modules/native-plugins/resources/notes-ui b/source/modules/native-plugins/resources/notes-ui index a1f3cabd0..ac21b3de7 100755 --- a/source/modules/native-plugins/resources/notes-ui +++ b/source/modules/native-plugins/resources/notes-ui @@ -31,6 +31,12 @@ else: from PyQt4.QtCore import pyqtSlot from PyQt4.QtGui import QGridLayout, QLabel, QPushButton, QTextEdit, QWidget +# ----------------------------------------------------------------------- +# Imports (Custom) + +from carla_shared import * +from carla_utils import * + # ----------------------------------------------------------------------- # Imports (ExternalUI) @@ -98,7 +104,7 @@ class DistrhoUINotes(ExternalUI, QWidget): if pageValue != self.fNotes[self.fCurPage-1]: self.fNotes[self.fCurPage-1] = pageValue - self.d_setState(pageKey, pageValue) + self.sendConfigure(pageKey, pageValue) # ------------------------------------------------------------------- @@ -106,7 +112,7 @@ class DistrhoUINotes(ExternalUI, QWidget): def slot_buttonClicked(self, click): readOnly = not click self.fTextEdit.setReadOnly(readOnly) - self.d_setState("readOnly", "yes" if readOnly else "no") + self.sendConfigure("readOnly", "yes" if readOnly else "no") @pyqtSlot(float) def slot_progressBarValueChanged(self, value): @@ -122,10 +128,10 @@ class DistrhoUINotes(ExternalUI, QWidget): self.fSaveTextNowChecker = -1 # change current page - self.d_parameterChanged(0, value) + self.dspParameterChanged(0, value) # tell host about this change - self.d_setParameterValue(0, value) + self.sendControl(0, value) @pyqtSlot() def slot_textChanged(self): @@ -134,7 +140,7 @@ class DistrhoUINotes(ExternalUI, QWidget): # ------------------------------------------------------------------- # DSP Callbacks - def d_parameterChanged(self, index, value): + def dspParameterChanged(self, index, value): if index != 0: return @@ -148,7 +154,7 @@ class DistrhoUINotes(ExternalUI, QWidget): self.fProgressBar.setValue(self.fCurPage) self.fProgressBar.update() - def d_stateChanged(self, key, value): + def dspStateChanged(self, key, value): if key == "guiWidth": try: width = int(value) @@ -187,17 +193,18 @@ class DistrhoUINotes(ExternalUI, QWidget): # ------------------------------------------------------------------- # ExternalUI Callbacks - def d_uiShow(self): + def uiShow(self): self.show() - def d_uiHide(self): + def uiHide(self): self.hide() - def d_uiQuit(self): + def uiQuit(self): + self.closeExternalUI() self.close() app.quit() - def d_uiTitleChanged(self, uiTitle): + def uiTitleChanged(self, uiTitle): self.setWindowTitle(uiTitle) # ------------------------------------------------------------------- @@ -209,9 +216,11 @@ class DistrhoUINotes(ExternalUI, QWidget): def timerEvent(self, event): if event.timerId() == self.fIdleTimer: + self.idleExternalUI() + if self.fSaveSizeNowChecker == 11: - self.d_setState("guiWidth", str(self.width())) - self.d_setState("guiHeight", str(self.height())) + self.sendConfigure("guiWidth", str(self.width())) + self.sendConfigure("guiHeight", str(self.height())) self.fSaveSizeNowChecker = -1 elif self.fSaveSizeNowChecker >= 0: self.fSaveSizeNowChecker += 1 @@ -222,9 +231,6 @@ class DistrhoUINotes(ExternalUI, QWidget): elif self.fSaveTextNowChecker >= 0: self.fSaveTextNowChecker += 1 - if not self.idleExternalUI(): - self.d_uiQuit() - QWidget.timerEvent(self, event) def closeEvent(self, event): @@ -234,6 +240,12 @@ class DistrhoUINotes(ExternalUI, QWidget): #--------------- main ------------------ if __name__ == '__main__': import resources_rc + + pathBinaries, pathResources = getPaths() + gCarla.utils = CarlaUtils(os.path.join(pathBinaries, "libcarla_utils." + DLL_EXTENSION)) + gCarla.utils.set_locale_C() + gCarla.utils.set_process_name("Notes") + app = CarlaApplication("Notes") gui = DistrhoUINotes() app.exit_exec() diff --git a/source/utils/CarlaPipeUtils.cpp b/source/utils/CarlaPipeUtils.cpp index 8dcac6c52..1f75dc634 100644 --- a/source/utils/CarlaPipeUtils.cpp +++ b/source/utils/CarlaPipeUtils.cpp @@ -1150,7 +1150,7 @@ CarlaPipeClient::~CarlaPipeClient() noexcept close(); } -bool CarlaPipeClient::init(char* argv[]) noexcept +bool CarlaPipeClient::init(const char* argv[]) noexcept { CARLA_SAFE_ASSERT_RETURN(pData->pipeRecv == INVALID_PIPE_VALUE, false); CARLA_SAFE_ASSERT_RETURN(pData->pipeSend == INVALID_PIPE_VALUE, false); diff --git a/source/utils/CarlaPipeUtils.hpp b/source/utils/CarlaPipeUtils.hpp index d0368805a..f2f907539 100644 --- a/source/utils/CarlaPipeUtils.hpp +++ b/source/utils/CarlaPipeUtils.hpp @@ -72,15 +72,12 @@ public: // ------------------------------------------------------------------- -private: +protected: struct PrivateData; PrivateData* const pData; // ------------------------------------------------------------------- - friend class CarlaPipeClient; - friend class CarlaPipeServer; - // internal const char* readline() noexcept; const char* readlineblock(const uint32_t timeOutMilliseconds = 50) noexcept; @@ -112,7 +109,7 @@ public: CarlaPipeClient() noexcept; ~CarlaPipeClient() noexcept override; - bool init(char* argv[]) noexcept; + bool init(const char* argv[]) noexcept; void close() noexcept; CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CarlaPipeClient)