diff --git a/resources/ui/carla.ui b/resources/ui/carla.ui index 9e75d6f62..eb13632d7 100644 --- a/resources/ui/carla.ui +++ b/resources/ui/carla.ui @@ -15,39 +15,30 @@ - - 0 - - - 2 - - - 0 - - - 2 - - - - Qt::ScrollBarAlwaysOn - - - Qt::ScrollBarAlwaysOff - - - QAbstractItemView::NoEditTriggers - - - false - - - QAbstractItemView::NoDragDrop + + + + 3 + + + 0 + + + + + + + + Qt::Vertical - - QAbstractItemView::NoSelection + + + 20 + 128 + - + diff --git a/source/backend/carla_standalone.hpp b/source/backend/carla_standalone.hpp index cd7ea7daf..cddf7f8dd 100644 --- a/source/backend/carla_standalone.hpp +++ b/source/backend/carla_standalone.hpp @@ -156,8 +156,15 @@ CARLA_EXPORT bool carla_engine_close(); CARLA_EXPORT void carla_engine_idle(); CARLA_EXPORT bool carla_is_engine_running(); +CARLA_EXPORT bool carla_load_project(const char* filename); +CARLA_EXPORT bool carla_save_project(const char* filename); + CARLA_EXPORT bool carla_add_plugin(CarlaBinaryType btype, CarlaPluginType ptype, const char* filename, const char* name, const char* label, void* extraPtr); CARLA_EXPORT bool carla_remove_plugin(unsigned int pluginId); +CARLA_EXPORT void carla_remove_all_plugins(); + +//CARLA_EXPORT bool carla_load_plugin_state(unsigned int pluginId, const char* filename); +//CARLA_EXPORT bool carla_save_plugin_state(unsigned int pluginId, const char* filename); CARLA_EXPORT const CarlaPluginInfo* carla_get_plugin_info(unsigned int pluginId); CARLA_EXPORT const CarlaPortCountInfo* carla_get_audio_port_count_info(unsigned int pluginId); diff --git a/source/backend/engine/carla_engine.cpp b/source/backend/engine/carla_engine.cpp index 966f49447..71d11eebe 100644 --- a/source/backend/engine/carla_engine.cpp +++ b/source/backend/engine/carla_engine.cpp @@ -712,8 +712,6 @@ bool CarlaEngine::addPlugin(const BinaryType btype, const PluginType ptype, cons CARLA_ASSERT(filename); CARLA_ASSERT(label); - qWarning("CarlaEngine::addPlugin() started"); - if (fData->curPluginCount == fData->maxPluginNumber) { setLastError("Maximum number of plugins reached"); @@ -834,7 +832,6 @@ bool CarlaEngine::addPlugin(const BinaryType btype, const PluginType ptype, cons fData->plugins[id].outsPeak[1] = 0.0f; fData->curPluginCount += 1; - qWarning("CarlaEngine::addPlugin() finished"); // FIXME callback(CALLBACK_PLUGIN_ADDED, id, 0, 0, 0.0f, nullptr); @@ -892,6 +889,9 @@ bool CarlaEngine::removePlugin(const unsigned int id) if (isRunning() && ! fData->aboutToClose) fData->thread.startNow(); + // FIXME + callback(CALLBACK_PLUGIN_REMOVED, id, 0, 0, 0.0f, nullptr); + return true; } diff --git a/source/backend/standalone/carla_standalone.cpp b/source/backend/standalone/carla_standalone.cpp index 606a40d38..4e59562b2 100644 --- a/source/backend/standalone/carla_standalone.cpp +++ b/source/backend/standalone/carla_standalone.cpp @@ -295,6 +295,20 @@ bool carla_is_engine_running() // ------------------------------------------------------------------------------------------------------------------- +bool carla_load_project(const char* filename) +{ + CARLA_ASSERT(standalone.engine != nullptr); + CARLA_ASSERT(filename != nullptr); +} + +bool carla_save_project(const char* filename) +{ + CARLA_ASSERT(standalone.engine != nullptr); + CARLA_ASSERT(filename != nullptr); +} + +// ------------------------------------------------------------------------------------------------------------------- + bool carla_add_plugin(CarlaBackend::BinaryType btype, CarlaBackend::PluginType ptype, const char* filename, const char* const name, const char* label, void* extraStuff) { qDebug("carla_add_plugin(%s, %s, \"%s\", \"%s\", \"%s\", %p)", CarlaBackend::BinaryType2Str(btype), CarlaBackend::PluginType2Str(ptype), filename, name, label, extraStuff); @@ -319,6 +333,15 @@ bool carla_remove_plugin(unsigned int pluginId) return false; } +CARLA_EXPORT void carla_remove_all_plugins() +{ + qDebug("carla_remove_all_plugins()"); + CARLA_ASSERT(standalone.engine != nullptr); + + if (standalone.engine != nullptr && standalone.engine->isRunning()) + standalone.engine->removeAllPlugins(); +} + // ------------------------------------------------------------------------------------------------------------------- const CarlaPluginInfo* carla_get_plugin_info(unsigned int pluginId) diff --git a/source/carla.py b/source/carla.py index 3b71297a6..3804489bb 100755 --- a/source/carla.py +++ b/source/carla.py @@ -63,14 +63,15 @@ class CarlaMainW(QMainWindow): self.fEngineStarted = False self.fFirstEngineInit = False + self.fProjectFilename = None + self.fProjectLoading = False + self.fPluginCount = 0 self.fPluginList = [] self.fIdleTimerFast = 0 self.fIdleTimerSlow = 0 - #self.m_project_filename = None - #self._nsmAnnounce2str = "" #self._nsmOpen1str = "" #self._nsmOpen2str = "" @@ -94,22 +95,24 @@ class CarlaMainW(QMainWindow): # ------------------------------------------------------------- # Connect actions to functions - #self.connect(self.act_file_new, SIGNAL("triggered()"), SLOT("slot_file_new()")) - #self.connect(self.act_file_open, SIGNAL("triggered()"), SLOT("slot_file_open()")) - #self.connect(self.act_file_save, SIGNAL("triggered()"), SLOT("slot_file_save()")) - #self.connect(self.act_file_save_as, SIGNAL("triggered()"), SLOT("slot_file_save_as()")) + self.connect(self.ui.act_file_new, SIGNAL("triggered()"), SLOT("slot_fileNew()")) + self.connect(self.ui.act_file_open, SIGNAL("triggered()"), SLOT("slot_fileOpen()")) + self.connect(self.ui.act_file_save, SIGNAL("triggered()"), SLOT("slot_fileSave()")) + self.connect(self.ui.act_file_save_as, SIGNAL("triggered()"), SLOT("slot_fileSaveAs()")) - self.connect(self.ui.act_engine_start, SIGNAL("triggered()"), SLOT("slot_startEngine()")) - self.connect(self.ui.act_engine_stop, SIGNAL("triggered()"), SLOT("slot_stopEngine()")) + self.connect(self.ui.act_engine_start, SIGNAL("triggered()"), SLOT("slot_engineStart()")) + self.connect(self.ui.act_engine_stop, SIGNAL("triggered()"), SLOT("slot_engineStop()")) - self.connect(self.ui.act_plugin_add, SIGNAL("triggered()"), SLOT("slot_addPlugin()")) - #self.connect(self.act_plugin_remove_all, SIGNAL("triggered()"), SLOT("slot_remove_all()")) + self.connect(self.ui.act_plugin_add, SIGNAL("triggered()"), SLOT("slot_pluginAdd()")) + self.connect(self.ui.act_plugin_remove_all, SIGNAL("triggered()"), SLOT("slot_pluginRemoveAll()")) - #self.connect(self.act_settings_configure, SIGNAL("triggered()"), SLOT("slot_configureCarla()")) + self.connect(self.ui.act_settings_configure, SIGNAL("triggered()"), SLOT("slot_configureCarla()")) self.connect(self.ui.act_help_about, SIGNAL("triggered()"), SLOT("slot_aboutCarla()")) self.connect(self.ui.act_help_about_qt, SIGNAL("triggered()"), app, SLOT("aboutQt()")) - #self.connect(self, SIGNAL("SIGUSR1()"), SLOT("slot_handleSIGUSR1()")) + self.connect(self, SIGNAL("SIGUSR1()"), SLOT("slot_handleSIGUSR1()")) + self.connect(self, SIGNAL("SIGTERM()"), SLOT("slot_handleSIGTERM()")) + self.connect(self, SIGNAL("DebugCallback(int, int, int, double, QString)"), SLOT("slot_handleDebugCallback(int, int, int, double, QString)")) self.connect(self, SIGNAL("PluginAddedCallback(int)"), SLOT("slot_handlePluginAddedCallback(int)")) self.connect(self, SIGNAL("PluginRemovedCallback(int)"), SLOT("slot_handlePluginRemovedCallback(int)")) @@ -139,7 +142,7 @@ class CarlaMainW(QMainWindow): #if NSM_URL: #Carla.host.nsm_announce(NSM_URL, os.getpid()) #else: - QTimer.singleShot(0, self, SLOT("slot_startEngine()")) + QTimer.singleShot(0, self, SLOT("slot_engineStart()")) def startEngine(self, clientName = "Carla"): # --------------------------------------------- @@ -185,8 +188,8 @@ class CarlaMainW(QMainWindow): self.fFirstEngineInit = False return - self.ui.act_engine_start.setEnabled(True) - self.ui.act_engine_stop.setEnabled(False) + #self.ui.act_engine_start.setEnabled(True) + #self.ui.act_engine_stop.setEnabled(False) audioError = cString(Carla.host.get_last_error()) @@ -225,7 +228,7 @@ class CarlaMainW(QMainWindow): if ask != QMessageBox.Yes: return - #self.slot_remove_all() + self.removeAllPlugins() if Carla.host.is_engine_running() and not Carla.host.engine_close(): print(cString(Carla.host.get_last_error())) @@ -237,6 +240,20 @@ class CarlaMainW(QMainWindow): self.killTimer(self.fIdleTimerFast) self.killTimer(self.fIdleTimerSlow) + def loadProject(self, filename): + self.fProjectLoading = True + self.fProjectFilename = filename + Carla.host.load_project(filename) + + def loadProjectLater(self, filename): + self.fProjectLoading = True + self.fProjectFilename = filename + self.setWindowTitle("Carla - %s" % os.path.basename(filename)) + QTimer.singleShot(0, self, SLOT("slot_loadProjectLater()")) + + def saveProject(self): + Carla.host.save_project(self.fProjectFilename) + def addPlugin(self, btype, ptype, filename, name, label, extraStuff): if not self.fEngineStarted: QMessageBox.warning(self, self.tr("Warning"), self.tr("Cannot add new plugins while engine is stopped")) @@ -248,8 +265,69 @@ class CarlaMainW(QMainWindow): return True + def removeAllPlugins(self): + while (self.ui.w_plugins.layout().takeAt(0)): + pass + + for i in range(self.fPluginCount): + pwidget = self.fPluginList[i] + + if pwidget is None: + break + + self.fPluginList[i] = None + + pwidget.ui.edit_dialog.close() + pwidget.close() + pwidget.deleteLater() + del pwidget + + self.fPluginCount = 0 + + @pyqtSlot() + def slot_fileNew(self): + self.removeAllPlugins() + self.fProjectFilename = None + self.fProjectLoading = False + self.setWindowTitle("Carla") + + @pyqtSlot() + def slot_fileOpen(self): + fileFilter = self.tr("Carla Project File (*.carxp)") + filenameTry = QFileDialog.getOpenFileName(self, self.tr("Open Carla Project File"), self.fSavedSettings["Main/DefaultProjectFolder"], filter=fileFilter) + + if filenameTry: + # FIXME - show dialog to user + self.removeAllPlugins() + self.loadProject(filenameTry) + self.setWindowTitle("Carla - %s" % os.path.basename(filenameTry)) + @pyqtSlot() - def slot_startEngine(self): + def slot_fileSave(self, saveAs=False): + if self.fProjectFilename and not saveAs: + return self.saveProject() + + fileFilter = self.tr("Carla Project File (*.carxp)") + filenameTry = QFileDialog.getSaveFileName(self, self.tr("Save Carla Project File"), self.fSavedSettings["Main/DefaultProjectFolder"], filter=fileFilter) + + if filenameTry: + if not filenameTry.endswith(".carxp"): + filenameTry += ".carxp" + + self.fProjectFilename = filenameTry + self.saveProject() + self.setWindowTitle("Carla - %s" % os.path.basename(filenameTry)) + + @pyqtSlot() + def slot_fileSaveAs(self): + self.slot_fileSave(True) + + @pyqtSlot() + def slot_loadProjectLater(self): + Carla.host.load_project(self.fProjectFilename) + + @pyqtSlot() + def slot_engineStart(self): self.startEngine() check = Carla.host.is_engine_running() self.ui.act_file_open.setEnabled(check) @@ -257,17 +335,45 @@ class CarlaMainW(QMainWindow): self.ui.act_engine_stop.setEnabled(check) @pyqtSlot() - def slot_stopEngine(self): + def slot_engineStop(self): self.stopEngine() check = Carla.host.is_engine_running() self.ui.act_file_open.setEnabled(check) self.ui.act_engine_start.setEnabled(not check) self.ui.act_engine_stop.setEnabled(check) + @pyqtSlot() + def slot_pluginAdd(self): + dialog = PluginDatabaseW(self) + if dialog.exec_(): + btype = dialog.fRetPlugin['build'] + ptype = dialog.fRetPlugin['type'] + filename = dialog.fRetPlugin['binary'] + label = dialog.fRetPlugin['label'] + extraStuff = self.getExtraStuff(dialog.fRetPlugin) + self.addPlugin(btype, ptype, filename, None, label, extraStuff) + + @pyqtSlot() + def slot_pluginRemoveAll(self): + self.removeAllPlugins() + + @pyqtSlot() + def slot_aboutCarla(self): + CarlaAboutW(self).exec_() + + @pyqtSlot() + def slot_configureCarla(self): + CarlaAboutW(self).exec_() + @pyqtSlot() def slot_handleSIGUSR1(self): print("Got SIGUSR1 -> Saving project now") - #QTimer.singleShot(0, self, SLOT("slot_file_save()")) + QTimer.singleShot(0, self, SLOT("slot_fileSave()")) + + @pyqtSlot() + def slot_handleSIGTERM(self): + print("Got SIGTERM -> Closing now") + self.close() @pyqtSlot(int, int, int, float, str) def slot_handleDebugCallback(self, pluginId, value1, value2, value3, valueStr): @@ -275,13 +381,10 @@ class CarlaMainW(QMainWindow): @pyqtSlot(int) def slot_handlePluginAddedCallback(self, pluginId, pluginName="todo"): - pwidgetItem = QListWidgetItem(self.ui.listWidget) - pwidgetItem.setSizeHint(QSize(pwidgetItem.sizeHint().width(), 48)) - - pwidget = PluginWidget(self, pwidgetItem, pluginId) + pwidget = PluginWidget(self, pluginId) pwidget.setRefreshRate(self.fSavedSettings["Main/RefreshInterval"]) - self.ui.listWidget.setItemWidget(pwidgetItem, pwidget) + self.ui.w_plugins.layout().addWidget(pwidget) self.fPluginCount += 1 self.fPluginList[pluginId] = pwidget @@ -289,6 +392,9 @@ class CarlaMainW(QMainWindow): if self.fPluginCount == 1: self.ui.act_plugin_remove_all.setEnabled(True) + if not self.fProjectLoading: + pwidget.setActive(True, True, True) + @pyqtSlot(int) def slot_handlePluginRemovedCallback(self, pluginId): pwidget = self.fPluginList[pluginId] @@ -298,12 +404,20 @@ class CarlaMainW(QMainWindow): self.fPluginList[pluginId] = None self.fPluginCount -= 1 + self.ui.w_plugins.layout().removeWidget(pwidget) + pwidget.ui.edit_dialog.close() pwidget.close() + pwidget.deleteLater() del pwidget - self.ui.listWidget.takeItem(pluginId) - #self.ui.listWidget.removeItemWidget(pwidget.getListWidgetItem()) + # push all plugins 1 slot back + for i in range(self.fPluginCount): + if i < pluginId: + continue + + self.fPluginList[i] = self.fPluginList[i+1] + self.fPluginList[i].setId(i) if self.fPluginCount == 0: self.ui.act_plugin_remove_all.setEnabled(False) @@ -438,21 +552,6 @@ class CarlaMainW(QMainWindow): self.tr("Engine has been stopped or crashed.\nPlease restart Carla"), self.tr("You may want to save your session now..."), QMessageBox.Ok, QMessageBox.Ok) - @pyqtSlot() - def slot_addPlugin(self): - dialog = PluginDatabaseW(self) - if dialog.exec_(): - btype = dialog.fRetPlugin['build'] - ptype = dialog.fRetPlugin['type'] - filename = dialog.fRetPlugin['binary'] - label = dialog.fRetPlugin['label'] - extraStuff = self.getExtraStuff(dialog.fRetPlugin) - self.addPlugin(btype, ptype, filename, None, label, extraStuff) - - @pyqtSlot() - def slot_aboutCarla(self): - CarlaAboutW(self).exec_() - def getExtraStuff(self, plugin): ptype = plugin['type'] @@ -474,19 +573,21 @@ class CarlaMainW(QMainWindow): # Save RDF info for later self.fLadspaRdfList = [] - if haveLRDF: - settingsDir = os.path.join(HOME, ".config", "Cadence") - frLadspaFile = os.path.join(settingsDir, "ladspa_rdf.db") + if not haveLRDF: + return + + settingsDir = os.path.join(HOME, ".config", "Cadence") + frLadspaFile = os.path.join(settingsDir, "ladspa_rdf.db") - if os.path.exists(frLadspaFile): - frLadspa = open(frLadspaFile, 'r') + if os.path.exists(frLadspaFile): + frLadspa = open(frLadspaFile, 'r') - try: - self.fLadspaRdfList = ladspa_rdf.get_c_ladspa_rdfs(json.load(frLadspa)) - except: - pass + try: + self.fLadspaRdfList = ladspa_rdf.get_c_ladspa_rdfs(json.load(frLadspa)) + except: + pass - frLadspa.close() + frLadspa.close() def saveSettings(self): settings = QSettings() @@ -542,13 +643,15 @@ class CarlaMainW(QMainWindow): Carla.host.engine_idle() for pwidget in self.fPluginList: - if pwidget is not None: - pwidget.idleFast() + if pwidget is None: + break + pwidget.idleFast() elif event.timerId() == self.fIdleTimerSlow: for pwidget in self.fPluginList: - if pwidget is not None: - pwidget.idleSlow() + if pwidget is None: + break + pwidget.idleSlow() QMainWindow.timerEvent(self, event) @@ -558,7 +661,7 @@ class CarlaMainW(QMainWindow): self.saveSettings() - #self.slot_remove_all() + self.removeAllPlugins() self.stopEngine() QMainWindow.closeEvent(self, event) @@ -702,16 +805,14 @@ if __name__ == '__main__': Carla.gui = CarlaMainW() # Set-up custom signal handling - #setUpSignals(Carla.gui) + setUpSignals(Carla.gui) # Show GUI Carla.gui.show() # Load project file if set - #if projectFilename: - #Carla.gui.m_project_filename = projectFilename - #Carla.gui.loadProjectLater() - #Carla.gui.setWindowTitle("Carla - %s" % os.path.basename(projectFilename)) # FIXME - put in loadProject + if projectFilename: + Carla.gui.loadProjectLater(projectFilename) # App-Loop ret = app.exec_() diff --git a/source/carla_backend.py b/source/carla_backend.py index a20fa3ec8..2d39a8234 100644 --- a/source/carla_backend.py +++ b/source/carla_backend.py @@ -178,12 +178,21 @@ class Host(object): self.lib.carla_is_engine_running.argtypes = None self.lib.carla_is_engine_running.restype = c_bool + self.lib.carla_load_project.argtypes = [c_char_p] + self.lib.carla_load_project.restype = c_bool + + self.lib.carla_save_project.argtypes = [c_char_p] + self.lib.carla_save_project.restype = c_bool + self.lib.carla_add_plugin.argtypes = [c_enum, c_enum, c_char_p, c_char_p, c_char_p, c_void_p] self.lib.carla_add_plugin.restype = c_bool self.lib.carla_remove_plugin.argtypes = [c_uint] self.lib.carla_remove_plugin.restype = c_bool + self.lib.carla_remove_all_plugins.argtypes = None + self.lib.carla_remove_all_plugins.restype = None + self.lib.carla_get_plugin_info.argtypes = [c_uint] self.lib.carla_get_plugin_info.restype = POINTER(CarlaPluginInfo) @@ -363,6 +372,12 @@ class Host(object): def is_engine_running(self): return self.lib.carla_is_engine_running() + def load_project(self, filename): + return self.lib.carla_load_project(filename.encode("utf-8")) + + def save_project(self, filename): + return self.lib.carla_save_project(filename.encode("utf-8")) + def add_plugin(self, btype, ptype, filename, name, label, extraStuff): cname = name.encode("utf-8") if name else c_nullptr return self.lib.carla_add_plugin(btype, ptype, filename.encode("utf-8"), cname, label.encode("utf-8"), cast(extraStuff, c_void_p)) @@ -370,6 +385,9 @@ class Host(object): def remove_plugin(self, pluginId): return self.lib.carla_remove_plugin(pluginId) + def remove_all_plugins(self): + self.lib.carla_remove_all_plugins() + def get_plugin_info(self, pluginId): return structToDict(self.lib.carla_get_plugin_info(pluginId).contents) diff --git a/source/carla_shared.py b/source/carla_shared.py index 053bad6f7..1f340532e 100644 --- a/source/carla_shared.py +++ b/source/carla_shared.py @@ -57,7 +57,7 @@ except: # Try Import Signal try: - from signal import signal, SIGINT, SIGTERM, SIGUSR1, SIGUSR2 + from signal import signal, SIGINT, SIGTERM, SIGUSR1 haveSignal = True except: haveSignal = False @@ -756,6 +756,23 @@ def uopen(filename, mode="r"): def getIcon(icon, size=16): return QIcon.fromTheme(icon, QIcon(":/%ix%i/%s.png" % (size, size, icon))) +# ------------------------------------------------------------------------------------------------------------ +# Signal handler + +def setUpSignals(self_): + if not haveSignal: + return + + signal(SIGINT, signalHandler) + signal(SIGTERM, signalHandler) + signal(SIGUSR1, signalHandler) + +def signalHandler(sig, frame): + if sig in (SIGINT, SIGTERM): + Carla.gui.emit(SIGNAL("SIGTERM()")) + elif sig == SIGUSR1: + Carla.gui.emit(SIGNAL("SIGUSR1()")) + # ------------------------------------------------------------------------------------------------------------ # Custom MessageBox @@ -1932,7 +1949,6 @@ class PluginEdit(QDialog): self.fRealParent.editClosed() def _createParameterWidgets(self, paramType, paramListFull, tabPageName): - print("createParameterWidgets()", paramType, tabPageName) i = 1 for paramList, width in paramListFull: if len(paramList) == 0: @@ -1974,7 +1990,7 @@ class PluginEdit(QDialog): # Plugin Widget class PluginWidget(QFrame): - def __init__(self, parent, listWidgetItem, pluginId): + def __init__(self, parent, pluginId): QFrame.__init__(self, parent) self.ui = ui_carla_plugin.Ui_PluginWidget() self.ui.setupUi(self) @@ -1992,8 +2008,6 @@ class PluginWidget(QFrame): self.fPluginInfo["maker"] = cString(self.fPluginInfo["maker"]) self.fPluginInfo["copyright"] = cString(self.fPluginInfo["copyright"]) - self.fListWidgetItem = listWidgetItem - if Carla.processMode == PROCESS_MODE_CONTINUOUS_RACK: self.fPeaksInputCount = 2 self.fPeaksOutputCount = 2 @@ -2131,9 +2145,6 @@ class PluginWidget(QFrame): # Update edit dialog self.ui.edit_dialog.idleSlow() - def getListWidgetItem(self): - return self.fListWidgetItem - def editClosed(self): self.ui.b_edit.setChecked(False)