diff --git a/source/backend/CarlaBackend.h b/source/backend/CarlaBackend.h index 346cc97ec..dd0b5b098 100644 --- a/source/backend/CarlaBackend.h +++ b/source/backend/CarlaBackend.h @@ -54,6 +54,16 @@ CARLA_BACKEND_START_NAMESPACE */ static const uint MAX_DEFAULT_PLUGINS = 99; +/*! + * Maximum number of loadable plugins in rack mode. + */ +static const uint MAX_RACK_PLUGINS = 16; + +/*! + * Maximum number of loadable plugins in patchbay mode. + */ +static const uint MAX_PATCHBAY_PLUGINS = 255; + /*! * Maximum default number of parameters allowed. * @see ENGINE_OPTION_MAX_PARAMETERS @@ -962,7 +972,7 @@ typedef enum { /*! * Set the engine processing mode. - * Default is ENGINE_PROCESS_MODE_MULTIPLE_CLIENTS on Linux and ENGINE_PROCESS_MODE_CONTINUOUS_RACK for all other OSes. + * Default is ENGINE_PROCESS_MODE_MULTIPLE_CLIENTS on Linux and ENGINE_PROCESS_MODE_PATCHBAY for all other OSes. * @see EngineProcessMode */ ENGINE_OPTION_PROCESS_MODE = 1, @@ -1131,10 +1141,15 @@ typedef enum { */ ENGINE_PROCESS_MODE_CONTINUOUS_RACK = 2, + /*! + * Single client, 'patchbay' mode. + */ + ENGINE_PROCESS_MODE_PATCHBAY = 3, + /*! * Special mode, used in plugin-bridges only. */ - ENGINE_PROCESS_MODE_BRIDGE = 3 + ENGINE_PROCESS_MODE_BRIDGE = 4 } EngineProcessMode; diff --git a/source/backend/CarlaEngine.hpp b/source/backend/CarlaEngine.hpp index f054860ba..4a1256db8 100644 --- a/source/backend/CarlaEngine.hpp +++ b/source/backend/CarlaEngine.hpp @@ -981,7 +981,7 @@ public: /*! * Force the engine to resend all patchbay clients, ports and connections again. */ - virtual bool patchbayRefresh(); + virtual bool patchbayRefresh(const bool external); #endif // ------------------------------------------------------------------- @@ -1149,8 +1149,8 @@ protected: * Virtual functions for handling patchbay state. * Do not free returned data. */ - virtual const char* const* getPatchbayConnections() const; - virtual void restorePatchbayConnection(const char* const sourcePort, const char* const targetPort); + virtual const char* const* getPatchbayConnections(const bool external) const; + virtual void restorePatchbayConnection(const bool external, const char* const sourcePort, const char* const targetPort); #endif // ------------------------------------------------------------------- diff --git a/source/backend/CarlaHost.h b/source/backend/CarlaHost.h index 269aaa88c..e745fb785 100644 --- a/source/backend/CarlaHost.h +++ b/source/backend/CarlaHost.h @@ -418,8 +418,10 @@ CARLA_EXPORT bool carla_patchbay_disconnect(uint connectionId); /*! * Force the engine to resend all patchbay clients, ports and connections again. + * @param external Wherever to show external/hardware ports instead of internal ones. + * Only valid in patchbay engine mode, other modes will ignore this. */ -CARLA_EXPORT bool carla_patchbay_refresh(); +CARLA_EXPORT bool carla_patchbay_refresh(bool external); /*! * Start playback of the engine transport. diff --git a/source/backend/CarlaStandalone.cpp b/source/backend/CarlaStandalone.cpp index eedf9b0bc..a00ee5934 100644 --- a/source/backend/CarlaStandalone.cpp +++ b/source/backend/CarlaStandalone.cpp @@ -728,12 +728,12 @@ bool carla_patchbay_disconnect(uint connectionId) return false; } -bool carla_patchbay_refresh() +bool carla_patchbay_refresh(bool external) { - carla_debug("carla_patchbay_refresh()"); + carla_debug("carla_patchbay_refresh(%s)", bool2str(external)); if (gStandalone.engine != nullptr) - return gStandalone.engine->patchbayRefresh(); + return gStandalone.engine->patchbayRefresh(external); carla_stderr2("Engine is not running"); gStandalone.lastError = "Engine is not running"; diff --git a/source/backend/plugin/CarlaPlugin.cpp b/source/backend/plugin/CarlaPlugin.cpp index 5b926af9d..e78b433fe 100644 --- a/source/backend/plugin/CarlaPlugin.cpp +++ b/source/backend/plugin/CarlaPlugin.cpp @@ -87,10 +87,17 @@ CarlaPlugin::CarlaPlugin(CarlaEngine* const engine, const uint id) { case ENGINE_PROCESS_MODE_SINGLE_CLIENT: case ENGINE_PROCESS_MODE_MULTIPLE_CLIENTS: - case ENGINE_PROCESS_MODE_CONTINUOUS_RACK: CARLA_SAFE_ASSERT(id < MAX_DEFAULT_PLUGINS); break; + case ENGINE_PROCESS_MODE_CONTINUOUS_RACK: + CARLA_SAFE_ASSERT(id < MAX_RACK_PLUGINS); + break; + + case ENGINE_PROCESS_MODE_PATCHBAY: + CARLA_SAFE_ASSERT(id < MAX_PATCHBAY_PLUGINS); + break; + case ENGINE_PROCESS_MODE_BRIDGE: CARLA_SAFE_ASSERT(id == 0); break; diff --git a/source/carla_backend.py b/source/carla_backend.py index 5f353b2e6..37d7952e3 100644 --- a/source/carla_backend.py +++ b/source/carla_backend.py @@ -144,6 +144,12 @@ def structToDict(struct): # Maximum default number of loadable plugins. MAX_DEFAULT_PLUGINS = 99 +# Maximum number of loadable plugins in rack mode. +MAX_RACK_PLUGINS = 16 + +# Maximum number of loadable plugins in patchbay mode. +MAX_PATCHBAY_PLUGINS = 255 + # Maximum default number of parameters allowed. # @see ENGINE_OPTION_MAX_PARAMETERS MAX_DEFAULT_PARAMETERS = 200 @@ -815,8 +821,11 @@ ENGINE_PROCESS_MODE_MULTIPLE_CLIENTS = 1 # Processes plugins in order of Id, with forced stereo always on. ENGINE_PROCESS_MODE_CONTINUOUS_RACK = 2 +# Single client, 'patchbay' mode. +ENGINE_PROCESS_MODE_PATCHBAY = 3 + # Special mode, used in plugin-bridges only. -ENGINE_PROCESS_MODE_BRIDGE = 3 +ENGINE_PROCESS_MODE_BRIDGE = 4 # ------------------------------------------------------------------------------------------------------------ # Engine Transport Mode @@ -1229,7 +1238,7 @@ class CarlaHostMeta(object): self.nsmOK = False # settings - self.processMode = ENGINE_PROCESS_MODE_CONTINUOUS_RACK + self.processMode = ENGINE_PROCESS_MODE_PATCHBAY self.transportMode = ENGINE_TRANSPORT_MODE_INTERNAL self.nextProcessMode = self.processMode self.processModeForced = False @@ -1372,7 +1381,7 @@ class CarlaHostMeta(object): # @param external Wherever to show external/hardware ports instead of internal ones. # Only valid in patchbay engine mode, other modes will ignore this. @abstractmethod - def patchbay_refresh(self): + def patchbay_refresh(self, external): raise NotImplementedError # Start playback of the engine transport. @@ -1926,7 +1935,7 @@ class CarlaHostNull(CarlaHostMeta): def patchbay_disconnect(self, connectionId): return False - def patchbay_refresh(self): + def patchbay_refresh(self, external): return False def transport_play(self): @@ -2208,7 +2217,7 @@ class CarlaHostDLL(CarlaHostMeta): self.lib.carla_patchbay_disconnect.argtypes = [c_uint] self.lib.carla_patchbay_disconnect.restype = c_bool - self.lib.carla_patchbay_refresh.argtypes = None + self.lib.carla_patchbay_refresh.argtypes = [c_bool] self.lib.carla_patchbay_refresh.restype = c_bool self.lib.carla_transport_play.argtypes = None @@ -2482,8 +2491,8 @@ class CarlaHostDLL(CarlaHostMeta): def patchbay_disconnect(self, connectionId): return bool(self.lib.carla_patchbay_disconnect(connectionId)) - def patchbay_refresh(self): - return bool(self.lib.carla_patchbay_refresh()) + def patchbay_refresh(self, external): + return bool(self.lib.carla_patchbay_refresh(external)) def transport_play(self): self.lib.carla_transport_play() @@ -2819,7 +2828,8 @@ class CarlaHostPlugin(CarlaHostMeta): def patchbay_disconnect(self, connectionId): return self.sendMsgAndSetError(["patchbay_disconnect", connectionId]) - def patchbay_refresh(self): + def patchbay_refresh(self, external): + # don't send external param, never used in plugins return self.sendMsgAndSetError(["patchbay_refresh"]) def transport_play(self): diff --git a/source/carla_host.py b/source/carla_host.py index 0e922d425..1daf98610 100644 --- a/source/carla_host.py +++ b/source/carla_host.py @@ -158,7 +158,10 @@ class HostWindow(QMainWindow): # Internal stuff (patchbay) self.fExportImage = QImage() + self.fPeaksCleared = True + + self.fExternalPatchbay = False self.fSelectedPlugins = [] self.fCanvasWidth = 0 @@ -216,6 +219,11 @@ class HostWindow(QMainWindow): self.ui.act_engine_stop.setEnabled(False) self.ui.act_plugin_remove_all.setEnabled(False) + self.ui.act_canvas_show_internal.setChecked(False) + self.ui.act_canvas_show_internal.setVisible(False) + self.ui.act_canvas_show_external.setChecked(False) + self.ui.act_canvas_show_external.setVisible(False) + self.ui.menu_PluginMacros.setEnabled(False) self.ui.menu_Canvas.setEnabled(False) @@ -223,6 +231,8 @@ class HostWindow(QMainWindow): self.ui.dockWidget.setTitleBarWidget(self.ui.dockWidgetTitleBar) if not withCanvas: + self.ui.act_canvas_show_internal.setVisible(False) + self.ui.act_canvas_show_external.setVisible(False) self.ui.act_canvas_arrange.setVisible(False) self.ui.act_canvas_refresh.setVisible(False) self.ui.act_canvas_save_image.setVisible(False) @@ -349,7 +359,6 @@ class HostWindow(QMainWindow): self.ui.act_file_quit.setMenuRole(QAction.QuitRole) self.ui.act_settings_configure.setMenuRole(QAction.PreferencesRole) self.ui.act_help_about.setMenuRole(QAction.AboutRole) - self.ui.act_help_about_juce.setMenuRole(QAction.ApplicationSpecificRole) self.ui.act_help_about_qt.setMenuRole(QAction.AboutQtRole) self.ui.menu_Settings.setTitle("Panels") self.ui.menu_Help.menuAction().setVisible(False) @@ -401,6 +410,8 @@ class HostWindow(QMainWindow): self.ui.act_plugins_expand.triggered.connect(self.slot_pluginsExpand) self.ui.act_plugins_panic.triggered.connect(self.slot_pluginsDisable) + self.ui.act_canvas_show_internal.triggered.connect(self.slot_canvasShowInternal) + self.ui.act_canvas_show_external.triggered.connect(self.slot_canvasShowExternal) self.ui.act_canvas_arrange.triggered.connect(self.slot_canvasArrange) self.ui.act_canvas_refresh.triggered.connect(self.slot_canvasRefresh) self.ui.act_canvas_zoom_fit.triggered.connect(self.slot_canvasZoomFit) @@ -498,6 +509,10 @@ class HostWindow(QMainWindow): if self.host.isPlugin: self.startTimers() + # Start in patchbay tab if using forced patchbay mode + if host.processModeForced and host.processMode == ENGINE_PROCESS_MODE_PATCHBAY and not host.isControl: + self.ui.tabWidget.setCurrentIndex(1) + # Load initial project file if set if not (self.host.isControl or self.host.isPlugin): projectFile = getInitialProjectFile(QApplication.instance()) @@ -745,6 +760,23 @@ class HostWindow(QMainWindow): self.ui.menu_PluginMacros.setEnabled(True) self.ui.menu_Canvas.setEnabled(True) + self.ui.act_canvas_show_internal.blockSignals(True) + self.ui.act_canvas_show_external.blockSignals(True) + + if processMode == ENGINE_PROCESS_MODE_PATCHBAY and not (self.host.isControl or self.host.isPlugin): + self.ui.act_canvas_show_internal.setChecked(True) + self.ui.act_canvas_show_internal.setVisible(True) + self.ui.act_canvas_show_external.setChecked(False) + self.ui.act_canvas_show_external.setVisible(True) + else: + self.ui.act_canvas_show_internal.setChecked(False) + self.ui.act_canvas_show_internal.setVisible(False) + self.ui.act_canvas_show_external.setChecked(False) + self.ui.act_canvas_show_external.setVisible(False) + + self.ui.act_canvas_show_internal.blockSignals(False) + self.ui.act_canvas_show_external.blockSignals(False) + if not (self.host.isControl or self.host.isPlugin): canSave = (self.fProjectFilename and os.path.exists(self.fProjectFilename)) or not self.fSessionManagerName self.ui.act_file_save.setEnabled(canSave) @@ -1166,6 +1198,28 @@ class HostWindow(QMainWindow): # -------------------------------------------------------------------------------------------------------- # Canvas (menu actions) + @pyqtSlot() + def slot_canvasShowInternal(self): + self.fExternalPatchbay = False + self.ui.act_canvas_show_internal.blockSignals(True) + self.ui.act_canvas_show_external.blockSignals(True) + self.ui.act_canvas_show_internal.setChecked(True) + self.ui.act_canvas_show_external.setChecked(False) + self.ui.act_canvas_show_internal.blockSignals(False) + self.ui.act_canvas_show_external.blockSignals(False) + self.slot_canvasRefresh() + + @pyqtSlot() + def slot_canvasShowExternal(self): + self.fExternalPatchbay = True + self.ui.act_canvas_show_internal.blockSignals(True) + self.ui.act_canvas_show_external.blockSignals(True) + self.ui.act_canvas_show_internal.setChecked(False) + self.ui.act_canvas_show_external.setChecked(True) + self.ui.act_canvas_show_internal.blockSignals(False) + self.ui.act_canvas_show_external.blockSignals(False) + self.slot_canvasRefresh() + @pyqtSlot() def slot_canvasArrange(self): patchcanvas.arrange() @@ -1178,7 +1232,7 @@ class HostWindow(QMainWindow): return if self.host.is_engine_running(): - self.host.patchbay_refresh() + self.host.patchbay_refresh(self.fExternalPatchbay) self.updateMiniCanvasLater() @@ -1502,7 +1556,7 @@ class HostWindow(QMainWindow): if self.host.processMode == ENGINE_PROCESS_MODE_CONTINUOUS_RACK and self.host.isPlugin: pass elif self.host.is_engine_running(): - self.host.patchbay_refresh() + self.host.patchbay_refresh(self.fExternalPatchbay) for pitem in self.fPluginList: if pitem is None: diff --git a/source/carla_shared.py b/source/carla_shared.py index de5f39b03..df379c558 100644 --- a/source/carla_shared.py +++ b/source/carla_shared.py @@ -293,7 +293,7 @@ if LINUX: CARLA_DEFAULT_PROCESS_MODE = ENGINE_PROCESS_MODE_MULTIPLE_CLIENTS CARLA_DEFAULT_TRANSPORT_MODE = ENGINE_TRANSPORT_MODE_JACK else: - CARLA_DEFAULT_PROCESS_MODE = ENGINE_PROCESS_MODE_CONTINUOUS_RACK + CARLA_DEFAULT_PROCESS_MODE = ENGINE_PROCESS_MODE_PATCHBAY CARLA_DEFAULT_TRANSPORT_MODE = ENGINE_TRANSPORT_MODE_INTERNAL # Wine diff --git a/source/native-plugins/_data.cpp b/source/native-plugins/_data.cpp index 17146c11f..bc174d465 100644 --- a/source/native-plugins/_data.cpp +++ b/source/native-plugins/_data.cpp @@ -259,6 +259,90 @@ static const NativePluginDescriptor sNativePluginDescriptors[] = { /* copyright */ "GNU GPL v2+", DESCFUNCS }, +{ + /* category */ NATIVE_PLUGIN_CATEGORY_OTHER, + /* hints */ static_cast(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(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+", + DESCFUNCS +}, +{ + /* category */ NATIVE_PLUGIN_CATEGORY_OTHER, + /* hints */ static_cast(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(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+", + DESCFUNCS +}, +{ + /* category */ NATIVE_PLUGIN_CATEGORY_OTHER, + /* hints */ static_cast(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(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+", + DESCFUNCS +}, +{ + /* category */ NATIVE_PLUGIN_CATEGORY_OTHER, + /* hints */ static_cast(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(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+", + DESCFUNCS +}, #endif // ----------------------------------------------------------------------- diff --git a/source/native-plugins/resources/carla-plugin b/source/native-plugins/resources/carla-plugin index 0831d894c..0c55dfaae 100755 --- a/source/native-plugins/resources/carla-plugin +++ b/source/native-plugins/resources/carla-plugin @@ -471,10 +471,17 @@ class CarlaEmbedW(QEmbedWidget): self.addShortcutActions(self.gui.ui.menu_Settings.actions()) self.addShortcutActions(self.gui.ui.menu_Help.actions()) + if self.host.processMode == ENGINE_PROCESS_MODE_PATCHBAY: + self.addShortcutActions(self.gui.ui.menu_Canvas.actions()) + self.addShortcutActions(self.gui.ui.menu_Canvas_Zoom.actions()) + self.addWidget(self.gui.ui.menubar) self.addLine() self.addWidget(self.gui.ui.toolBar) + if self.host.processMode == ENGINE_PROCESS_MODE_PATCHBAY: + self.addLine() + self.addWidget(self.gui.centralWidget()) self.finalSetup(self.gui, winId) @@ -564,7 +571,7 @@ if __name__ == '__main__': # Init host backend host = initHost("Carla-Plugin", None, False, True, True, PluginHost) - host.processMode = ENGINE_PROCESS_MODE_CONTINUOUS_RACK + host.processMode = ENGINE_PROCESS_MODE_PATCHBAY if sys.argv[0].lower().endswith("/carla-plugin-patchbay") else ENGINE_PROCESS_MODE_CONTINUOUS_RACK host.processModeForced = True host.nextProcessMode = host.processMode loadHostSettings(host)