diff --git a/source/backend/CarlaBackend.hpp b/source/backend/CarlaBackend.hpp index f35992688..d8fc714b4 100644 --- a/source/backend/CarlaBackend.hpp +++ b/source/backend/CarlaBackend.hpp @@ -97,6 +97,18 @@ const unsigned int PARAMETER_USES_SCALEPOINTS = 0x40; //!< Parameter uses scalep const unsigned int PARAMETER_USES_CUSTOM_TEXT = 0x80; //!< Parameter uses custom text for displaying its value.\see CarlaPlugin::getParameterText() /**@}*/ +/*! + * @defgroup PatchbayPortHints Patchbay Port Hints + * + * Various patchbay port hints. + * @{ + */ +const unsigned int PATCHBAY_PORT_IS_INPUT = 0x1; //!< Patchbay port is input. +const unsigned int PATCHBAY_PORT_IS_OUTPUT = 0x2; //!< Patchbay port is output. +const unsigned int PATCHBAY_PORT_IS_AUDIO = 0x4; //!< Patchbay port is of Audio type. +const unsigned int PATCHBAY_PORT_IS_MIDI = 0x8; //!< Patchbay port is of MIDI type. +/**@}*/ + /*! * @defgroup CustomDataTypes Custom Data types * diff --git a/source/backend/engine/CarlaEngineJack.cpp b/source/backend/engine/CarlaEngineJack.cpp index a3d478228..472496ffd 100644 --- a/source/backend/engine/CarlaEngineJack.cpp +++ b/source/backend/engine/CarlaEngineJack.cpp @@ -497,7 +497,10 @@ public: #ifdef BUILD_BRIDGE fHasQuit(false) #else - fRackPorts{nullptr} + fRackPorts{nullptr}, + fLastGroupId(0), + fLastPortId(0), + fLastConnectionId(0) #endif { carla_debug("CarlaEngineJack::CarlaEngineJack()"); @@ -513,6 +516,12 @@ public: { carla_debug("CarlaEngineJack::~CarlaEngineJack()"); CARLA_ASSERT(fClient == nullptr); + +#ifndef BUILD_BRIDGE + fUsedGroupNames.clear(); + fUsedPortNames.clear(); + fUsedConnections.clear(); +#endif } // ------------------------------------------------------------------- @@ -547,6 +556,14 @@ public: carla_zeroStruct(fTransportPos); #ifndef BUILD_BRIDGE + fLastGroupId = 0; + fLastPortId = 0; + fLastConnectionId = 0; + + fUsedGroupNames.clear(); + fUsedPortNames.clear(); + fUsedConnections.clear(); + fClient = jackbridge_client_open(clientName, JackNullOption, nullptr); if (fClient != nullptr) @@ -571,6 +588,14 @@ public: fRackPorts[rackPortEventOut] = jackbridge_port_register(fClient, "events-out", JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput, 0); } + // TODO - update jackbridge + jack_set_client_registration_callback(fClient, carla_jack_client_registration_callback, this); + jack_set_port_registration_callback(fClient, carla_jack_port_registration_callback, this); + jack_set_port_connect_callback(fClient, carla_jack_port_connect_callback, this); + + if (jack_set_port_rename_callback) + jack_set_port_rename_callback(fClient, carla_jack_port_rename_callback, this); + if (jackbridge_activate(fClient) == 0) { const char* const jackClientName = jackbridge_get_client_name(fClient); @@ -639,6 +664,10 @@ public: setLastError("Failed to deactivate the JACK client"); fClient = nullptr; + + fUsedGroupNames.clear(); + fUsedPortNames.clear(); + fUsedConnections.clear(); #endif return false; } @@ -691,9 +720,6 @@ public: } #endif - static int test = 0; - callback(CALLBACK_PATCHBAY_CLIENT_ADDED, 0, test++, 0, 0, plugin->name()); - return new CarlaEngineJackClient(kEngineTypeJack, fOptions.processMode, client); } @@ -1047,6 +1073,113 @@ protected: } } +#ifndef BUILD_BRIDGE + void handleJackClientRegistrationCallback(const char* name, bool reg) + { + if (reg) + { + GroupNameToId groupNameToId; + groupNameToId.id = fLastGroupId; + groupNameToId.name = name; + + callback(CALLBACK_PATCHBAY_CLIENT_ADDED, 0, fLastGroupId, 0, 0.0f, name); + fUsedGroupNames.append(groupNameToId); + fLastGroupId++; + } + else + { + for (int i=0, count=fUsedGroupNames.count(); i < count; i++) + { + if (fUsedGroupNames[i].name == name) + { + callback(CALLBACK_PATCHBAY_CLIENT_REMOVED, 0, fUsedGroupNames[i].id, 0, 0.0f, nullptr); + fUsedGroupNames.takeAt(i); + break; + } + } + } + } + + void handleJackPortRegistrationCallback(jack_port_id_t port, bool reg) + { + jack_port_t* jackPort = jack_port_by_id(fClient, port); + + QString fullName(jack_port_name(jackPort)); + QString groupName = fullName.split(":").at(0); + int groupId = getGroupId(groupName); + + const char* portName = jack_port_short_name(jackPort); + + if (reg) + { + bool portIsInput = (jack_port_flags(jackPort) & JackPortIsInput); + bool portIsAudio = (std::strcmp(jack_port_type(jackPort), JACK_DEFAULT_AUDIO_TYPE) == 0); + + unsigned int portFlags = 0x0; + portFlags |= portIsInput ? PATCHBAY_PORT_IS_INPUT : PATCHBAY_PORT_IS_OUTPUT; + portFlags |= portIsAudio ? PATCHBAY_PORT_IS_AUDIO : PATCHBAY_PORT_IS_MIDI; + + PortNameToId portNameToId; + portNameToId.groupId = groupId; + portNameToId.portId = fLastPortId; + portNameToId.name = portName; + + fUsedPortNames.append(portNameToId); + callback(CALLBACK_PATCHBAY_PORT_ADDED, 0, groupId, fLastPortId, portFlags, portName); + fLastPortId++; + } + else + { + for (int i=0, count=fUsedPortNames.count(); i < count; i++) + { + if (fUsedPortNames[i].groupId == groupId && fUsedPortNames[i].name == portName) + { + callback(CALLBACK_PATCHBAY_PORT_REMOVED, 0, fUsedPortNames[i].portId, 0, 0.0f, nullptr); + fUsedPortNames.takeAt(i); + break; + } + } + } + } + + void handleJackPortConnectCallback(jack_port_id_t a, jack_port_id_t b, bool connect) + { + jack_port_t* jackPortA = jack_port_by_id(fClient, a); + jack_port_t* jackPortB = jack_port_by_id(fClient, b); + + int portIdA = getPortId(QString(jack_port_name(jackPortA))); + int portIdB = getPortId(QString(jack_port_name(jackPortB))); + + if (connect) + { + ConnectionToId connectionToId; + connectionToId.id = fLastConnectionId; + connectionToId.portOut = portIdA; + connectionToId.portIn = portIdB; + + fUsedConnections.append(connectionToId); + callback(CALLBACK_PATCHBAY_CONNECTION_ADDED, 0, fLastConnectionId, portIdA, portIdB, nullptr); + fLastConnectionId++; + } + else + { + for (int i=0, count=fUsedConnections.count(); i < count; i++) + { + if (fUsedConnections[i].portOut == portIdA && fUsedConnections[i].portIn == portIdB) + { + callback(CALLBACK_PATCHBAY_CONNECTION_REMOVED, 0, fUsedConnections[i].id, 0, 0.0f, nullptr); + fUsedConnections.takeAt(i); + break; + } + } + } + } + + int handleJackPortRenameCallback(jack_port_id_t port, const char* oldName, const char* newName) + { + } +#endif + void handleJackShutdownCallback() { for (unsigned int i=0; i < kData->curPluginCount; i++) @@ -1085,6 +1218,61 @@ private: }; jack_port_t* fRackPorts[rackPortCount]; + + struct GroupNameToId { + int id; + QString name; + }; + + struct PortNameToId { + int groupId; + int portId; + QString name; + }; + + struct ConnectionToId { + int id; + int portOut; + int portIn; + }; + + int fLastGroupId; + int fLastPortId; + int fLastConnectionId ; + + QList fUsedGroupNames; + QList fUsedPortNames; + QList fUsedConnections; + + int getGroupId(QString groupName) + { + for (int i=0, count=fUsedGroupNames.count(); i < count; i++) + { + if (fUsedGroupNames[i].name == groupName) + { + return fUsedGroupNames[i].id; + } + } + return -1; + } + + int getPortId(QString fullPortName) + { + QString groupName = fullPortName.split(":").at(0); + QString portName = fullPortName.replace(groupName+":", ""); + + int groupId = getGroupId(groupName); + + for (int i=0, count=fUsedPortNames.count(); i < count; i++) + { + if (fUsedPortNames[i].groupId == groupId && fUsedPortNames[i].name == portName) + { + return fUsedPortNames[i].portId; + } + } + + return -1; + } #endif // ------------------------------------- @@ -1217,6 +1405,29 @@ private: handlePtr->handleJackLatencyCallback(mode); } +#ifndef BUILD_BRIDGE + static void carla_jack_client_registration_callback(const char* name, int reg, void* arg) + { + handlePtr->handleJackClientRegistrationCallback(name, (reg != 0)); + } + + static void carla_jack_port_registration_callback(jack_port_id_t port, int reg, void* arg) + { + handlePtr->handleJackPortRegistrationCallback(port, (reg != 0)); + } + + static void carla_jack_port_connect_callback(jack_port_id_t a, jack_port_id_t b, int connect, void* arg) + { + handlePtr->handleJackPortConnectCallback(a, b, (connect != 0)); + } + + static int carla_jack_port_rename_callback(jack_port_id_t port, const char* oldName, const char* newName, void* arg) + { + handlePtr->handleJackPortRenameCallback(port, oldName, newName); + return 0; + } +#endif + static void carla_jack_shutdown_callback(void* arg) { handlePtr->handleJackShutdownCallback(); diff --git a/source/carla.py b/source/carla.py index 12142895f..572c04ed0 100755 --- a/source/carla.py +++ b/source/carla.py @@ -660,11 +660,11 @@ class CarlaMainW(QMainWindow): self.connect(self, SIGNAL("PatchbayClientAddedCallback(int, QString)"), SLOT("slot_handlePatchbayClientAddedCallback(int, QString)")) self.connect(self, SIGNAL("PatchbayClientRemovedCallback(int)"), SLOT("slot_handlePatchbayClientRemovedCallback(int)")) self.connect(self, SIGNAL("PatchbayClientRenamedCallback(int, QString)"), SLOT("slot_handlePatchbayClientRenamedCallback(int, QString)")) - self.connect(self, SIGNAL("PatchbayPortAddedCallback(int, int, QString)"), SLOT("slot_handlePatchbayPortAddedCallback(int, int, QString)")) + self.connect(self, SIGNAL("PatchbayPortAddedCallback(int, int, int, QString)"), SLOT("slot_handlePatchbayPortAddedCallback(int, int, int, QString)")) self.connect(self, SIGNAL("PatchbayPortRemovedCallback(int)"), SLOT("slot_handlePatchbayPortRemovedCallback(int)")) self.connect(self, SIGNAL("PatchbayPortRenamedCallback(int, QString)"), SLOT("slot_handlePatchbayPortRenamedCallback(int, QString)")) - self.connect(self, SIGNAL("PatchbayConnectionAddedCallback(int, int)"), SLOT("slot_handlePatchbayConnectionAddedCallback(int, int)")) - self.connect(self, SIGNAL("PatchbayConnectionRemovedCallback(int, int)"), SLOT("slot_handlePatchbayConnectionRemovedCallback(int, int)")) + self.connect(self, SIGNAL("PatchbayConnectionAddedCallback(int, int, int)"), SLOT("slot_handlePatchbayConnectionAddedCallback(int, int, int)")) + self.connect(self, SIGNAL("PatchbayConnectionRemovedCallback(int)"), SLOT("slot_handlePatchbayConnectionRemovedCallback(int)")) #self.connect(self, SIGNAL("NSM_AnnounceCallback()"), SLOT("slot_handleNSM_AnnounceCallback()")) #self.connect(self, SIGNAL("NSM_Open1Callback()"), SLOT("slot_handleNSM_Open1Callback()")) #self.connect(self, SIGNAL("NSM_Open2Callback()"), SLOT("slot_handleNSM_Open2Callback()")) @@ -839,6 +839,8 @@ class CarlaMainW(QMainWindow): self.ui.ch_transport.clear() self.ui.ch_transport.blockSignals(False) + patchcanvas.clear() + def loadProject(self, filename): self.fProjectFilename = filename self.setWindowTitle("Carla - %s" % os.path.basename(filename)) @@ -1232,10 +1234,23 @@ class CarlaMainW(QMainWindow): def slot_handlePatchbayClientRenamedCallback(self, clientId, newClientName): patchcanvas.renameGroup(clientId, newClientName) - @pyqtSlot(int, int, str) - def slot_handlePatchbayPortAddedCallback(self, clientId, portId, portName): - # FIXME - needs mode and type - patchcanvas.addPort(clientId, portId, portName, 0, 0) + @pyqtSlot(int, int, int, str) + def slot_handlePatchbayPortAddedCallback(self, clientId, portId, portFlags, portName): + if (portFlags & PATCHBAY_PORT_IS_INPUT): + portMode = patchcanvas.PORT_MODE_INPUT + elif (portFlags & PATCHBAY_PORT_IS_OUTPUT): + portMode = patchcanvas.PORT_MODE_OUTPUT + else: + portMode = patchcanvas.PORT_MODE_NULL + + if (portFlags & PATCHBAY_PORT_IS_AUDIO): + portType = patchcanvas.PORT_TYPE_AUDIO_JACK + elif (portFlags & PATCHBAY_PORT_IS_MIDI): + portType = patchcanvas.PORT_TYPE_MIDI_JACK + else: + portType = patchcanvas.PORT_TYPE_NULL + + patchcanvas.addPort(clientId, portId, portName, portMode, portType) @pyqtSlot(int) def slot_handlePatchbayPortRemovedCallback(self, portId): @@ -1245,14 +1260,13 @@ class CarlaMainW(QMainWindow): def slot_handlePatchbayPortRenamedCallback(self, portId, newPortName): patchcanvas.renamePort(portId, newPortName) - @pyqtSlot(int, int) - def slot_handlePatchbayConnectionAddedCallback(self, portOutId, portInId): - patchcanvas.connectPorts(0, portOutId, portInId) + @pyqtSlot(int, int, int) + def slot_handlePatchbayConnectionAddedCallback(self, connectionId, portOutId, portInId): + patchcanvas.connectPorts(connectionId, portOutId, portInId) - @pyqtSlot(int, int) - def slot_handlePatchbayConnectionRemovedCallback(self, portOutId, portInId): - # FIXME - patchcanvas.disconnectPorts(0) + @pyqtSlot(int) + def slot_handlePatchbayConnectionRemovedCallback(self, connectionId): + patchcanvas.disconnectPorts(connectionId) @pyqtSlot(str) def slot_handleErrorCallback(self, error): @@ -1411,7 +1425,6 @@ class CarlaMainW(QMainWindow): self.stopEngine() - patchcanvas.clear() QMainWindow.closeEvent(self, event) # ------------------------------------------------------------------------------------------------ @@ -1464,15 +1477,15 @@ def engineCallback(ptr, action, pluginId, value1, value2, value3, valueStr): elif action == CALLBACK_PATCHBAY_CLIENT_RENAMED: Carla.gui.emit(SIGNAL("PatchbayClientRenamedCallback(int, QString)"), value1, cString(valueStr)) elif action == CALLBACK_PATCHBAY_PORT_ADDED: - Carla.gui.emit(SIGNAL("PatchbayPortAddedCallback(int, int, QString)"), value1, value2, cString(valueStr)) + Carla.gui.emit(SIGNAL("PatchbayPortAddedCallback(int, int, int, QString)"), value1, value2, value3, cString(valueStr)) elif action == CALLBACK_PATCHBAY_PORT_REMOVED: Carla.gui.emit(SIGNAL("PatchbayPortRemovedCallback(int)"), value1) elif action == CALLBACK_PATCHBAY_PORT_RENAMED: Carla.gui.emit(SIGNAL("PatchbayPortRenamedCallback(int, QString)"), value1, cString(valueStr)) elif action == CALLBACK_PATCHBAY_CONNECTION_ADDED: - Carla.gui.emit(SIGNAL("PatchbayConnectionAddedCallback(int, int)"), value1, value2) + Carla.gui.emit(SIGNAL("PatchbayConnectionAddedCallback(int, int, int)"), value1, value2, value3) elif action == CALLBACK_PATCHBAY_CONNECTION_REMOVED: - Carla.gui.emit(SIGNAL("PatchbayConnectionRemovedCallback(int, int)"), value1, value2) + Carla.gui.emit(SIGNAL("PatchbayConnectionRemovedCallback(int)"), value1) #elif action == CALLBACK_NSM_ANNOUNCE: #Carla.gui._nsmAnnounce2str = cString(Carla.host.get_last_error()) #Carla.gui.emit(SIGNAL("NSM_AnnounceCallback()")) diff --git a/source/carla_shared.py b/source/carla_shared.py index 9c3e8fdc5..bd783eecd 100644 --- a/source/carla_shared.py +++ b/source/carla_shared.py @@ -189,6 +189,12 @@ PARAMETER_USES_SAMPLERATE = 0x20 PARAMETER_USES_SCALEPOINTS = 0x40 PARAMETER_USES_CUSTOM_TEXT = 0x80 +# Patchbay Port Hints +PATCHBAY_PORT_IS_INPUT = 0x1 +PATCHBAY_PORT_IS_OUTPUT = 0x2 +PATCHBAY_PORT_IS_AUDIO = 0x4 +PATCHBAY_PORT_IS_MIDI = 0x8 + # Custom Data types CUSTOM_DATA_INVALID = None CUSTOM_DATA_CHUNK = "http://kxstudio.sf.net/ns/carla/chunk" diff --git a/source/utils/CarlaBackendUtils.hpp b/source/utils/CarlaBackendUtils.hpp index ef4c5c025..a659988a9 100644 --- a/source/utils/CarlaBackendUtils.hpp +++ b/source/utils/CarlaBackendUtils.hpp @@ -295,6 +295,22 @@ const char* CallbackType2Str(const CallbackType& type) return "CALLBACK_SHOW_GUI"; case CALLBACK_UPDATE: return "CALLBACK_UPDATE"; + case CALLBACK_PATCHBAY_CLIENT_ADDED: + return "CALLBACK_PATCHBAY_CLIENT_ADDED"; + case CALLBACK_PATCHBAY_CLIENT_REMOVED: + return "CALLBACK_PATCHBAY_CLIENT_REMOVED"; + case CALLBACK_PATCHBAY_CLIENT_RENAMED: + return "CALLBACK_PATCHBAY_CLIENT_RENAMED"; + case CALLBACK_PATCHBAY_PORT_ADDED: + return "CALLBACK_PATCHBAY_PORT_ADDED"; + case CALLBACK_PATCHBAY_PORT_REMOVED: + return "CALLBACK_PATCHBAY_PORT_REMOVED"; + case CALLBACK_PATCHBAY_PORT_RENAMED: + return "CALLBACK_PATCHBAY_PORT_RENAMED"; + case CALLBACK_PATCHBAY_CONNECTION_ADDED: + return "CALLBACK_PATCHBAY_CONNECTION_ADDED"; + case CALLBACK_PATCHBAY_CONNECTION_REMOVED: + return "CALLBACK_PATCHBAY_CONNECTION_REMOVED"; case CALLBACK_RELOAD_INFO: return "CALLBACK_RELOAD_INFO"; case CALLBACK_RELOAD_PARAMETERS: