diff --git a/src/carla.py b/src/carla.py index 2a6202d..ead56b2 100755 --- a/src/carla.py +++ b/src/carla.py @@ -81,7 +81,7 @@ save_state_custom_data = { 'value': "" } -def getStateSaveDictFromXML(xml_node): +def getStateDictFromXML(xml_node): x_save_state_dict = deepcopy(save_state_dict) node = xml_node.firstChild() @@ -1292,8 +1292,8 @@ class PluginEdit(QDialog, ui_carla_edit.Ui_PluginEdit): self.tab_icon_count = 0 self.tab_icon_timers = [] - #self.connect(self.b_save_state, SIGNAL("clicked()"), self.save_state) - #self.connect(self.b_load_state, SIGNAL("clicked()"), self.load_state) + self.connect(self.b_save_state, SIGNAL("clicked()"), SLOT("slot_saveState()")) + self.connect(self.b_load_state, SIGNAL("clicked()"), SLOT("slot_loadState()")) self.connect(self.keyboard, SIGNAL("noteOn(int)"), SLOT("slot_noteOn(int)")) self.connect(self.keyboard, SIGNAL("noteOff(int)"), SLOT("slot_noteOff(int)")) @@ -1340,24 +1340,21 @@ class PluginEdit(QDialog, ui_carla_edit.Ui_PluginEdit): self.cb_midi_programs.setCurrentIndex(midi_program_id) QTimer.singleShot(0, self, SLOT("slot_checkInputControlParameters()")) - #def do_update(self): - #self.checkInputControlParameters() - #self.checkOutputControlParameters() + def do_update(self): + # Update current program text + if (self.cb_programs.count() > 0): + pindex = self.cb_programs.currentIndex() + pname = toString(CarlaHost.get_program_name(self.plugin_id, pindex)) + self.cb_programs.setItemText(pindex, pname) - ## Update current program text - #if (self.cb_programs.count() > 0): - #pindex = self.cb_programs.currentIndex() - #pname = CarlaHost.get_program_name(self.plugin_id, pindex) - #if (not pname): pname = "" - #self.cb_programs.setItemText(pindex, pname) + # Update current midi program text + if (self.cb_midi_programs.count() > 0): + mpindex = self.cb_midi_programs.currentIndex() + mpname = "%s %s" % (self.cb_midi_programs.currentText().split(" ", 1)[0], toString(CarlaHost.get_midi_program_name(self.plugin_id, pindex))) + self.cb_midi_programs.setItemText(pindex, mpname) - ## Update current midi program text - #if (self.cb_midi_programs.count() > 0): - #mpindex = self.cb_midi_programs.currentIndex() - #mpname = CarlaHost.get_midi_program_name(self.plugin_id, pindex) - #if (not mpname): mpname = "" - #self.cb_midi_programs.setItemText(pindex, mpname) - ## FIXME - leave 001:001 alone + QTimer.singleShot(0, self, SLOT("slot_checkInputControlParameters()")) + QTimer.singleShot(0, self, SLOT("slot_checkOutputControlParameters()")) def do_reload_all(self): self.pinfo = CarlaHost.get_plugin_info(self.plugin_id) @@ -1380,7 +1377,7 @@ class PluginEdit(QDialog, ui_carla_edit.Ui_PluginEdit): self.do_reload_info() self.do_reload_parameters() - #self.do_reload_programs() + self.do_reload_programs() def do_reload_info(self): if (self.ptype == PLUGIN_NONE and self.pinfo['type'] in (PLUGIN_DSSI, PLUGIN_SF2)): @@ -1587,14 +1584,14 @@ class PluginEdit(QDialog, ui_carla_edit.Ui_PluginEdit): self.createParameterWidgets(p_fake, self.tr("Information"), PARAMETER_UNKNOWN) - #def do_reload_programs(self): - ## Programs + def do_reload_programs(self): + # Programs #old_current = self.cur_program_index #program_count = CarlaHost.get_program_count(self.plugin_id) #if (self.cb_programs.count() > 0): #self.cur_program_index = -1 - #self.set_program(-1) + #self.cb_programs.setCurrentIndex(-1) #self.cb_programs.clear() #if (program_count > 0): @@ -1602,18 +1599,19 @@ class PluginEdit(QDialog, ui_carla_edit.Ui_PluginEdit): #self.cur_program_index = 0 #for i in range(program_count): - #pname = CarlaHost.get_program_name(self.plugin_id, i) - #if (not pname): pname = "" + #pname = toString(CarlaHost.get_program_name(self.plugin_id, i)) #self.cb_programs.addItem(pname) #if (old_current < 0): #old_current = 0 #self.cur_program_index = old_current - #self.set_program(old_current) + #self.cb_programs.setCurrentIndex(old_current) + + # TODO - request for current program? need to handle this better #else: - #self.cb_programs.setEnabled(False) + self.cb_programs.setEnabled(False) ## MIDI Programs #old_midi_current = self.cur_midi_program_index @@ -1658,70 +1656,47 @@ class PluginEdit(QDialog, ui_carla_edit.Ui_PluginEdit): #self.set_midi_program(old_midi_current) #else: - #self.cb_midi_programs.setEnabled(False) - - #def save_state(self): - ## TODO - LV2 and VST native formats - #if (self.state_filename == None): - #file_filter = self.tr("Carla State File (*.carxs)") - #filename_try = QFileDialog.getSaveFileName(self, self.tr("Save Carla State File"), filter=file_filter) - - #if (not filename_try.isEmpty()): - #self.state_filename = QStringStr(filename_try) - #self.save_state_InternalFormat() - #else: - #self.state_filename = None - - #else: - #ask_try = QMessageBox.question(self, self.tr("Overwrite?"), self.tr("Overwrite previously created file?"), QMessageBox.Ok|QMessageBox.Cancel) - - #if (ask_try == QMessageBox.Ok): - #self.save_state_InternalFormat() - #else: - #self.state_filename = None - #self.saveState() + self.cb_midi_programs.setEnabled(False) - #def load_state(self): - ## TODO - LV2 and VST native formats - #file_filter = self.tr("Carla State File (*.carxs)") - #filename_try = QFileDialog.getOpenFileName(self, self.tr("Open Carla State File"), filter=file_filter) - - #if (not filename_try.isEmpty()): - #self.state_filename = QStringStr(filename_try) - #self.load_state_InternalFormat() + def saveState_InternalFormat(self): + content = ("\n" + "\n" + "\n") % (VERSION) - #def save_state_InternalFormat(self): - #content = ("\n" - #"\n" - #"\n") % (VERSION) + content += self.parent().getSaveXMLContent() - #content += self.parent().getSaveXMLContent() + content += "\n" - #content += "\n" + try: + open(self.state_filename, "w").write(content) + except: + QMessageBox.critical(self, self.tr("Error"), self.tr("Failed to save state file")) - #if (open(self.state_filename, "w").write(content)): - #QMessageBox.critical(self, self.tr("Error"), self.tr("Failed to save state file")) + def saveState_Lv2Format(self): + pass - #def load_state_InternalFormat(self): - #state_read = open(self.state_filename, "r").read() + def saveState_VstFormat(self): + pass - #if not state_read: - #QMessageBox.critical(self, self.tr("Error"), self.tr("Failed to load state file")) - #return + def loadState_InternalFormat(self): + try: + state_read = open(self.state_filename, "r").read() + except: + QMessageBox.critical(self, self.tr("Error"), self.tr("Failed to load state file")) + return - #xml = QDomDocument() - #xml.setContent(state_read) + xml = QDomDocument() + xml.setContent(state_read) - #xml_node = xml.documentElement() - #if (xml_node.tagName() != "CARLA-PRESET"): - #QMessageBox.critical(self, self.tr("Error"), self.tr("Not a valid Carla state file")) - #return + xml_node = xml.documentElement() - #x_save_state_dict = getStateSaveDictFromXML(xml_node) + if (xml_node.tagName() != "CARLA-PRESET"): + QMessageBox.critical(self, self.tr("Error"), self.tr("Not a valid Carla state file")) + return - ## TODO - verify plugin + x_save_state_dict = getStateDictFromXML(xml_node) - #self.parent().load_save_state_dict(x_save_state_dict) + self.parent().loadStateDict(x_save_state_dict) def createParameterWidgets(self, p_list_full, tab_name, ptype): for i in range(len(p_list_full)): @@ -1792,6 +1767,36 @@ class PluginEdit(QDialog, ui_carla_edit.Ui_PluginEdit): # Update output parameters QTimer.singleShot(0, self, SLOT("slot_checkOutputControlParameters()")) + @pyqtSlot() + def slot_saveState(self): + # TODO - LV2 and VST native formats + if (self.state_filename): + ask_try = QMessageBox.question(self, self.tr("Overwrite?"), self.tr("Overwrite previously created file?"), QMessageBox.Ok|QMessageBox.Cancel) + + if (ask_try == QMessageBox.Ok): + self.saveState_InternalFormat() + else: + self.state_filename = None + self.slot_saveState() + + else: + file_filter = self.tr("Carla State File (*.carxs)") + filename_try = QFileDialog.getSaveFileName(self, self.tr("Save Carla State File"), filter=file_filter) + + if (filename_try): + self.state_filename = filename_try + self.saveState_InternalFormat() + + @pyqtSlot() + def slot_loadState(self): + # TODO - LV2 and VST native formats + file_filter = self.tr("Carla State File (*.carxs)") + filename_try = QFileDialog.getOpenFileName(self, self.tr("Open Carla State File"), filter=file_filter) + + if (filename_try): + self.state_filename = filename_try + self.loadState_InternalFormat() + @pyqtSlot(int, float) def slot_parameterValueChanged(self, parameter_id, value): CarlaHost.set_parameter_value(self.plugin_id, parameter_id, value) @@ -2516,7 +2521,7 @@ class PluginWidget(QFrame, ui_carla_plugin.Ui_PluginWidget): self.b_edit.setChecked(False) @pyqtSlot(bool) - def slot_guiClicked(self): + def slot_guiClicked(self, show): if (self.gui_dialog_type in (GUI_INTERNAL_QT4, GUI_INTERNAL_X11)): if (show): if (self.gui_dialog_geometry): @@ -2680,21 +2685,21 @@ class CarlaMainW(QMainWindow, ui_carla.Ui_CarlaMainW): self.connect(self.act_help_about, SIGNAL("triggered()"), SLOT("slot_aboutCarla()")) self.connect(self.act_help_about_qt, SIGNAL("triggered()"), app, SLOT("aboutQt()")) - #self.connect(self, SIGNAL("SIGUSR1()"), self.handleSIGUSR1) - #self.connect(self, SIGNAL("DebugCallback(int, int, int, double)"), self.handleDebugCallback) - #self.connect(self, SIGNAL("ParameterCallback(int, int, double)"), self.handleParameterCallback) - #self.connect(self, SIGNAL("ProgramCallback(int, int)"), self.handleProgramCallback) - #self.connect(self, SIGNAL("MidiProgramCallback(int, int)"), self.handleMidiProgramCallback) - #self.connect(self, SIGNAL("NoteOnCallback(int, int, int)"), self.handleNoteOnCallback) - #self.connect(self, SIGNAL("NoteOffCallback(int, int, int)"), self.handleNoteOffCallback) - #self.connect(self, SIGNAL("ShowGuiCallback(int, int)"), self.handleShowGuiCallback) - #self.connect(self, SIGNAL("ResizeGuiCallback(int, int, int)"), self.handleResizeGuiCallback) - #self.connect(self, SIGNAL("UpdateCallback(int)"), self.handleUpdateCallback) - #self.connect(self, SIGNAL("ReloadInfoCallback(int)"), self.handleReloadInfoCallback) - #self.connect(self, SIGNAL("ReloadParametersCallback(int)"), self.handleReloadParametersCallback) - #self.connect(self, SIGNAL("ReloadProgramsCallback(int)"), self.handleReloadProgramsCallback) - #self.connect(self, SIGNAL("ReloadAllCallback(int)"), self.handleReloadAllCallback) - #self.connect(self, SIGNAL("QuitCallback()"), self.handleQuitCallback) + self.connect(self, SIGNAL("SIGUSR1()"), SLOT("slot_handleSIGUSR1()")) + self.connect(self, SIGNAL("DebugCallback(int, int, int, double)"), SLOT("slot_handleDebugCallback(int, int, int, double)")) + self.connect(self, SIGNAL("ParameterCallback(int, int, double)"), SLOT("slot_handleParameterCallback(int, int, double)")) + self.connect(self, SIGNAL("ProgramCallback(int, int)"), SLOT("slot_handleProgramCallback(int, int)")) + self.connect(self, SIGNAL("MidiProgramCallback(int, int)"), SLOT("slot_handleMidiProgramCallback(int, int)")) + self.connect(self, SIGNAL("NoteOnCallback(int, int, int)"), SLOT("slot_handleNoteOnCallback(int, int, int)")) + self.connect(self, SIGNAL("NoteOffCallback(int, int, int)"), SLOT("slot_handleNoteOffCallback(int, int, int)")) + self.connect(self, SIGNAL("ShowGuiCallback(int, int)"), SLOT("slot_handleShowGuiCallback(int, int)")) + self.connect(self, SIGNAL("ResizeGuiCallback(int, int, int)"), SLOT("slot_handleResizeGuiCallback(int, int, int)")) + self.connect(self, SIGNAL("UpdateCallback(int)"), SLOT("slot_handleUpdateCallback(int)")) + self.connect(self, SIGNAL("ReloadInfoCallback(int)"), SLOT("slot_handleReloadInfoCallback(int)")) + self.connect(self, SIGNAL("ReloadParametersCallback(int)"), SLOT("slot_handleReloadParametersCallback(int)")) + self.connect(self, SIGNAL("ReloadProgramsCallback(int)"), SLOT("slot_handleReloadProgramsCallback(int)")) + self.connect(self, SIGNAL("ReloadAllCallback(int)"), SLOT("slot_handleReloadAllCallback(int)")) + self.connect(self, SIGNAL("QuitCallback()"), SLOT("slot_handleQuitCallback()")) self.TIMER_GUI_STUFF = self.startTimer(50) # Peaks self.TIMER_GUI_STUFF2 = self.startTimer(50*2) # LEDs and edit dialog @@ -2703,128 +2708,143 @@ class CarlaMainW(QMainWindow, ui_carla.Ui_CarlaMainW): if (plugin_id < 0 or plugin_id >= MAX_PLUGINS): return - #if (action == CALLBACK_DEBUG): - #self.emit(SIGNAL("DebugCallback(int, int, int, double)"), plugin_id, value1, value2, value3) - #elif (action == CALLBACK_PARAMETER_CHANGED): - #self.emit(SIGNAL("ParameterCallback(int, int, double)"), plugin_id, value1, value3) - #elif (action == CALLBACK_PROGRAM_CHANGED): - #self.emit(SIGNAL("ProgramCallback(int, int)"), plugin_id, value1) - #elif (action == CALLBACK_MIDI_PROGRAM_CHANGED): - #self.emit(SIGNAL("MidiProgramCallback(int, int)"), plugin_id, value1) - #elif (action == CALLBACK_NOTE_ON): - #self.emit(SIGNAL("NoteOnCallback(int, int, int)"), plugin_id, value1, value2) - #elif (action == CALLBACK_NOTE_OFF): - #self.emit(SIGNAL("NoteOffCallback(int, int, int)"), plugin_id, value1, value2) - #elif (action == CALLBACK_SHOW_GUI): - #self.emit(SIGNAL("ShowGuiCallback(int, int)"), plugin_id, value1) - #elif (action == CALLBACK_RESIZE_GUI): - #self.emit(SIGNAL("ResizeGuiCallback(int, int, int)"), plugin_id, value1, value2) - #elif (action == CALLBACK_UPDATE): - #self.emit(SIGNAL("UpdateCallback(int)"), plugin_id) - #elif (action == CALLBACK_RELOAD_INFO): - #self.emit(SIGNAL("ReloadInfoCallback(int)"), plugin_id) - #elif (action == CALLBACK_RELOAD_PARAMETERS): - #self.emit(SIGNAL("ReloadParametersCallback(int)"), plugin_id) - #elif (action == CALLBACK_RELOAD_PROGRAMS): - #self.emit(SIGNAL("ReloadProgramsCallback(int)"), plugin_id) - #elif (action == CALLBACK_RELOAD_ALL): - #self.emit(SIGNAL("ReloadAllCallback(int)"), plugin_id) - #elif (action == CALLBACK_QUIT): - #self.emit(SIGNAL("QuitCallback()")) - - def handleSIGUSR1(self): + if (action == CALLBACK_DEBUG): + self.emit(SIGNAL("DebugCallback(int, int, int, double)"), plugin_id, value1, value2, value3) + elif (action == CALLBACK_PARAMETER_CHANGED): + self.emit(SIGNAL("ParameterCallback(int, int, double)"), plugin_id, value1, value3) + elif (action == CALLBACK_PROGRAM_CHANGED): + self.emit(SIGNAL("ProgramCallback(int, int)"), plugin_id, value1) + elif (action == CALLBACK_MIDI_PROGRAM_CHANGED): + self.emit(SIGNAL("MidiProgramCallback(int, int)"), plugin_id, value1) + elif (action == CALLBACK_NOTE_ON): + self.emit(SIGNAL("NoteOnCallback(int, int, int)"), plugin_id, value1, value2) + elif (action == CALLBACK_NOTE_OFF): + self.emit(SIGNAL("NoteOffCallback(int, int, int)"), plugin_id, value1, value2) + elif (action == CALLBACK_SHOW_GUI): + self.emit(SIGNAL("ShowGuiCallback(int, int)"), plugin_id, value1) + elif (action == CALLBACK_RESIZE_GUI): + self.emit(SIGNAL("ResizeGuiCallback(int, int, int)"), plugin_id, value1, value2) + elif (action == CALLBACK_UPDATE): + self.emit(SIGNAL("UpdateCallback(int)"), plugin_id) + elif (action == CALLBACK_RELOAD_INFO): + self.emit(SIGNAL("ReloadInfoCallback(int)"), plugin_id) + elif (action == CALLBACK_RELOAD_PARAMETERS): + self.emit(SIGNAL("ReloadParametersCallback(int)"), plugin_id) + elif (action == CALLBACK_RELOAD_PROGRAMS): + self.emit(SIGNAL("ReloadProgramsCallback(int)"), plugin_id) + elif (action == CALLBACK_RELOAD_ALL): + self.emit(SIGNAL("ReloadAllCallback(int)"), plugin_id) + elif (action == CALLBACK_QUIT): + self.emit(SIGNAL("QuitCallback()")) + + @pyqtSlot() + def slot_handleSIGUSR1(self): print("Got SIGUSR1 -> Saving project now") - #QTimer.singleShot(0, self, SLOT("slot_file_save()")) - - #def handleDebugCallback(self, plugin_id, value1, value2, value3): - #print "DEBUG ::", plugin_id, value1, value2, value3 - - #def handleParameterCallback(self, plugin_id, parameter_id, value): - #pwidget = self.plugin_list[plugin_id] - #if (pwidget): - #pwidget.parameter_activity_timer = ICON_STATE_ON - - #if (parameter_id == PARAMETER_ACTIVE): - #pwidget.set_active((value > 0.0), True, False) - #elif (parameter_id == PARAMETER_DRYWET): - #pwidget.set_drywet(value*1000, True, False) - #elif (parameter_id == PARAMETER_VOLUME): - #pwidget.set_vol(value*1000, True, False) - #elif (parameter_id == PARAMETER_BALANCE_LEFT): - #pwidget.set_balance_left(value*1000, True, False) - #elif (parameter_id == PARAMETER_BALANCE_RIGHT): - #pwidget.set_balance_right(value*1000, True, False) - #elif (parameter_id >= 0): - #pwidget.edit_dialog.set_parameter_value(parameter_id, value) - - #def handleProgramCallback(self, plugin_id, program_id): - #pwidget = self.plugin_list[plugin_id] - #if (pwidget): - #pwidget.edit_dialog.set_program(program_id) - - #def handleMidiProgramCallback(self, plugin_id, midi_program_id): - #pwidget = self.plugin_list[plugin_id] - #if (pwidget): - #pwidget.edit_dialog.set_midi_program(midi_program_id) - - #def handleNoteOnCallback(self, plugin_id, note, velo): - #pwidget = self.plugin_list[plugin_id] - #if (pwidget): - #pwidget.edit_dialog.keyboard.noteOn(note, False) - - #def handleNoteOffCallback(self, plugin_id, note, velo): - #pwidget = self.plugin_list[plugin_id] - #if (pwidget): - #pwidget.edit_dialog.keyboard.noteOff(note, False) - - #def handleShowGuiCallback(self, plugin_id, show): - #pwidget = self.plugin_list[plugin_id] - #if (pwidget): - #if (show == 0): - #pwidget.b_gui.setChecked(False) - #pwidget.b_gui.setEnabled(True) - #elif (show == 1): - #pwidget.b_gui.setChecked(True) - #pwidget.b_gui.setEnabled(True) - #elif (show == -1): - #pwidget.b_gui.setChecked(False) - #pwidget.b_gui.setEnabled(False) - - #def handleResizeGuiCallback(self, plugin_id, width, height): - #pwidget = self.plugin_list[plugin_id] - #if (pwidget): - #gui_dialog = pwidget.gui_dialog - #if (gui_dialog): - #gui_dialog.set_new_size(width, height) - - #def handleUpdateCallback(self, plugin_id): - #pwidget = self.plugin_list[plugin_id] - #if (pwidget): - #pwidget.edit_dialog.do_update() - - #def handleReloadInfoCallback(self, plugin_id): - #pwidget = self.plugin_list[plugin_id] - #if (pwidget): - #pwidget.edit_dialog.do_reload_info() - - #def handleReloadParametersCallback(self, plugin_id): - #pwidget = self.plugin_list[plugin_id] - #if (pwidget): - #pwidget.edit_dialog.do_reload_parameters() - - #def handleReloadProgramsCallback(self, plugin_id): - #pwidget = self.plugin_list[plugin_id] - #if (pwidget): - #pwidget.edit_dialog.do_reload_programs() - - #def handleReloadAllCallback(self, plugin_id): - #pwidget = self.plugin_list[plugin_id] - #if (pwidget): - #pwidget.edit_dialog.do_reload_all() - - #def handleQuitCallback(self): - #CustomMessageBox(self, QMessageBox.Warning, self.tr("Warning"), self.tr("JACK has been stopped or crashed.\nPlease start JACK and restart Carla"), - #"You may want to save your session now...", QMessageBox.Ok, QMessageBox.Ok) + QTimer.singleShot(0, self, SLOT("slot_file_save()")) + + @pyqtSlot(int, int, int, float) + def slot_handleDebugCallback(self, plugin_id, value1, value2, value3): + print("DEBUG :: %i, %i, %i, %f)" % (plugin_id, value1, value2, value3)) + + @pyqtSlot(int, int, float) + def slot_handleParameterCallback(self, plugin_id, parameter_id, value): + pwidget = self.m_plugin_list[plugin_id] + if (pwidget): + pwidget.parameter_activity_timer = ICON_STATE_ON + + if (parameter_id == PARAMETER_ACTIVE): + pwidget.set_active((value > 0.0), True, False) + elif (parameter_id == PARAMETER_DRYWET): + pwidget.set_drywet(value*1000, True, False) + elif (parameter_id == PARAMETER_VOLUME): + pwidget.set_volume(value*1000, True, False) + elif (parameter_id == PARAMETER_BALANCE_LEFT): + pwidget.set_balance_left(value*1000, True, False) + elif (parameter_id == PARAMETER_BALANCE_RIGHT): + pwidget.set_balance_right(value*1000, True, False) + elif (parameter_id >= 0): + pwidget.edit_dialog.set_parameter_value(parameter_id, value) + + @pyqtSlot(int, int) + def slot_handleProgramCallback(self, plugin_id, program_id): + pwidget = self.m_plugin_list[plugin_id] + if (pwidget): + pwidget.edit_dialog.set_program(program_id) + + @pyqtSlot(int, int) + def slot_handleMidiProgramCallback(self, plugin_id, midi_program_id): + pwidget = self.m_plugin_list[plugin_id] + if (pwidget): + pwidget.edit_dialog.set_midi_program(midi_program_id) + + @pyqtSlot(int, int, int) + def slot_handleNoteOnCallback(self, plugin_id, note, velo): + pwidget = self.m_plugin_list[plugin_id] + if (pwidget): + pwidget.edit_dialog.keyboard.noteOn(note, False) + + @pyqtSlot(int, int, int) + def slot_handleNoteOffCallback(self, plugin_id, note, velo): + pwidget = self.m_plugin_list[plugin_id] + if (pwidget): + pwidget.edit_dialog.keyboard.noteOff(note, False) + + @pyqtSlot(int, int) + def slot_handleShowGuiCallback(self, plugin_id, show): + pwidget = self.m_plugin_list[plugin_id] + if (pwidget): + if (show == 0): + pwidget.b_gui.setChecked(False) + pwidget.b_gui.setEnabled(True) + elif (show == 1): + pwidget.b_gui.setChecked(True) + pwidget.b_gui.setEnabled(True) + elif (show == -1): + pwidget.b_gui.setChecked(False) + pwidget.b_gui.setEnabled(False) + + @pyqtSlot(int, int, int) + def slot_handleResizeGuiCallback(self, plugin_id, width, height): + pwidget = self.m_plugin_list[plugin_id] + if (pwidget): + gui_dialog = pwidget.gui_dialog + if (gui_dialog): + gui_dialog.set_new_size(width, height) + + @pyqtSlot(int) + def slot_handleUpdateCallback(self, plugin_id): + pwidget = self.m_plugin_list[plugin_id] + if (pwidget): + pwidget.edit_dialog.do_update() + + @pyqtSlot(int) + def slot_handleReloadInfoCallback(self, plugin_id): + pwidget = self.m_plugin_list[plugin_id] + if (pwidget): + pwidget.edit_dialog.do_reload_info() + + @pyqtSlot(int) + def slot_handleReloadParametersCallback(self, plugin_id): + pwidget = self.m_plugin_list[plugin_id] + if (pwidget): + pwidget.edit_dialog.do_reload_parameters() + + @pyqtSlot(int) + def slot_handleReloadProgramsCallback(self, plugin_id): + pwidget = self.m_plugin_list[plugin_id] + if (pwidget): + pwidget.edit_dialog.do_reload_programs() + + @pyqtSlot(int) + def slot_handleReloadAllCallback(self, plugin_id): + pwidget = self.m_plugin_list[plugin_id] + if (pwidget): + pwidget.edit_dialog.do_reload_all() + + @pyqtSlot() + def slot_handleQuitCallback(self): + CustomMessageBox(self, QMessageBox.Warning, self.tr("Warning"), self.tr("JACK has been stopped or crashed.\nPlease start JACK and restart Carla"), + "You may want to save your session now...", QMessageBox.Ok, QMessageBox.Ok) def add_plugin(self, btype, ptype, filename, label, extra_stuff, activate): new_plugin_id = CarlaHost.add_plugin(btype, ptype, filename, label, extra_stuff) @@ -2964,7 +2984,7 @@ class CarlaMainW(QMainWindow, ui_carla.Ui_CarlaMainW): node = xml_node.firstChild() while not node.isNull(): if (node.toElement().tagName() == "Plugin"): - x_save_state_dict = getStateSaveDictFromXML(node) + x_save_state_dict = getStateDictFromXML(node) x_save_state_dicts.append(x_save_state_dict) node = node.nextSibling() @@ -2996,7 +3016,7 @@ class CarlaMainW(QMainWindow, ui_carla.Ui_CarlaMainW): x_plugins = x_sf2_plugins else: - failed_plugins.append(x_save_state_dict['Name']) + x_failed_plugins.append(x_save_state_dict['Name']) continue # Try UniqueID -> Label -> Binary (full) -> Binary (short) diff --git a/src/carla/Makefile b/src/carla/Makefile index bf082f3..63c0ddf 100644 --- a/src/carla/Makefile +++ b/src/carla/Makefile @@ -12,8 +12,8 @@ CARLA_BUILD_FLAGS += -DDEBUG -O0 -g # CARLA_BUILD_FLAGS += -DNDEBUG -DQT_NO_DEBUG -DQT_NO_DEBUG_STREAM -DQT_NO_DEBUG_OUTPUT -O2 -fvisibility=hidden -ffast-math -fomit-frame-pointer -mtune=generic -msse CARLA_LINK_FLAGS = -shared -fPIC -ldl `pkg-config --libs jack fluidsynth liblo QtCore QtGui` $(LDFLAGS) -OBJS = carla_backend.o jack.o ladspa.o -# osc.o misc.o dssi.o lv2.o vst.o winvst.o sf2.o lv2-rtmempool/rtmempool.o +OBJS = carla_backend.o carla_threads.o jack.o ladspa.o +# osc.o dssi.o lv2.o vst.o sf2.o lv2-rtmempool/rtmempool.o all: carla_backend.so diff --git a/src/carla/carla_backend.cpp b/src/carla/carla_backend.cpp index f703c86..639dcc9 100644 --- a/src/carla/carla_backend.cpp +++ b/src/carla/carla_backend.cpp @@ -16,12 +16,12 @@ */ #include "carla_backend.h" +#include "carla_threads.h" #include "carla_plugin.h" #include #include -#include #include // Global variables @@ -31,6 +31,7 @@ const char* carla_client_name = nullptr; QMutex carla_proc_lock_var; QMutex carla_midi_lock_var; +CarlaCheckThread carla_check_thread; // Global variables (shared) const char* unique_names[MAX_PLUGINS] = { nullptr }; @@ -123,7 +124,7 @@ bool carla_init(const char* client_name) //osc_init(); - //check_thread.start(); + carla_check_thread.start(QThread::HighPriority); set_last_error("no error"); } @@ -158,10 +159,10 @@ bool carla_close() remove_plugin(i); } - //check_thread.quit(); + carla_check_thread.quit(); - //if (!check_thread.wait(1000000)) // 1 sec - // qWarning("Failed to properly stop global check thread"); + if (carla_check_thread.wait(2000)) // 2 secs + qWarning("Failed to properly stop global check thread"); //osc_send_exit(&global_osc_data); //osc_close(); @@ -659,9 +660,9 @@ uint32_t get_custom_data_count(unsigned short plugin_id) for (unsigned short i=0; iid() == plugin_id) -// return plugin->custom.count(); + CarlaPlugin* plugin = CarlaPlugins[i]; + if (plugin && plugin->id() == plugin_id) + return 0; //plugin->custom.count(); } qCritical("get_custom_data_count(%i) - could not find plugin", plugin_id); @@ -916,7 +917,6 @@ void set_parameter_value(unsigned short plugin_id, uint32_t parameter_id, double plugin->set_parameter_value(parameter_id, value, true, true, false); else qCritical("set_parameter_value(%i, %i, %f) - parameter_id out of bounds", plugin_id, parameter_id, value); - return; } } @@ -939,16 +939,10 @@ void set_parameter_midi_channel(unsigned short plugin_id, uint32_t parameter_id, CarlaPlugin* plugin = CarlaPlugins[i]; if (plugin && plugin->id() == plugin_id) { -// if (parameter_id < plugin->param.count) -// { -// plugin->param.data[parameter_id].midi_channel = channel; - -// if (plugin->hints & PLUGIN_IS_BRIDGE) -// osc_send_set_parameter_midi_channel(&plugin->osc.data, plugin->id, parameter_id, channel); -// } -// else -// qCritical("set_parameter_midi_channel(%i, %i, %i) - parameter_id out of bounds", plugin_id, parameter_id, channel); - + if (parameter_id < plugin->param_count()) + plugin->set_parameter_midi_channel(parameter_id, channel); + else + qCritical("set_parameter_midi_channel(%i, %i, %i) - parameter_id out of bounds", plugin_id, parameter_id, channel); return; } } @@ -975,16 +969,10 @@ void set_parameter_midi_cc(unsigned short plugin_id, uint32_t parameter_id, int1 CarlaPlugin* plugin = CarlaPlugins[i]; if (plugin && plugin->id() == plugin_id) { -// if (parameter_id < plugin->param.count) -// { -// plugin->param.data[parameter_id].midi_cc = midi_cc; - -// if (plugin->hints & PLUGIN_IS_BRIDGE) -// osc_send_set_parameter_midi_cc(&plugin->osc.data, plugin->id, parameter_id, midi_cc); -// } -// else -// qCritical("set_parameter_midi_cc(%i, %i, %i) - parameter_id out of bounds", plugin_id, parameter_id, midi_cc); - + if (parameter_id < plugin->param_count()) + plugin->set_parameter_midi_cc(parameter_id, midi_cc); + else + qCritical("set_parameter_midi_cc(%i, %i, %i) - parameter_id out of bounds", plugin_id, parameter_id, midi_cc); return; } } @@ -1378,17 +1366,6 @@ void send_plugin_midi_note(unsigned short /*plugin_id*/, bool /*onoff*/, uint8_t #if 0 -#include "misc.h" - -#include -#include -#include - -#include - -QMutex carla_midi_lock_var; -CarlaCheckThread check_thread; - // Global OSC stuff lo_server_thread global_osc_server_thread = nullptr; const char* global_osc_server_path = nullptr; diff --git a/src/carla/carla_plugin.h b/src/carla/carla_plugin.h index 8d8e379..d992c94 100644 --- a/src/carla/carla_plugin.h +++ b/src/carla/carla_plugin.h @@ -26,11 +26,24 @@ #include #include +#include + #define CARLA_PROCESS_CONTINUE_CHECK if (m_id != plugin_id) { return callback_action(CALLBACK_DEBUG, plugin_id, m_id, 0, 0.0f); } // Global JACK client extern jack_client_t* carla_jack_client; +const unsigned short MAX_POSTEVENTS = 128; + +enum PluginPostEventType { + PostEventDebug, + PostEventParameterChange, + PostEventProgramChange, + PostEventMidiProgramChange, + PostEventNoteOn, + PostEventNoteOff +}; + struct PluginAudioData { uint32_t count; uint32_t* rindexes; @@ -50,6 +63,13 @@ struct PluginParameterData { jack_port_t* port_cout; }; +struct PluginPostEvent { + bool valid; + PluginPostEventType type; + int32_t index; + double value; +}; + class CarlaPlugin { public: @@ -57,8 +77,8 @@ public: { qDebug("CarlaPlugin::CarlaPlugin()"); - m_type = PLUGIN_NONE; - m_id = -1; + m_type = PLUGIN_NONE; + m_id = -1; m_hints = 0; m_active = false; @@ -94,6 +114,9 @@ public: param.ranges = nullptr; param.port_cin = nullptr; param.port_cout = nullptr; + + for (unsigned short i=0; i < MAX_POSTEVENTS; i++) + post_events.data[i].valid = false; } virtual ~CarlaPlugin() @@ -416,6 +439,22 @@ public: //x_set_parameter_value(parameter_id, value, gui_send); } + void set_parameter_midi_channel(uint32_t index, uint8_t channel) + { + param.data[index].midi_channel = channel; + + //if (plugin->hints & PLUGIN_IS_BRIDGE) + // osc_send_set_parameter_midi_channel(&plugin->osc.data, plugin->id, parameter_id, channel); + } + + void set_parameter_midi_cc(uint32_t index, int16_t midi_cc) + { + param.data[index].midi_cc = midi_cc; + + //if (plugin->hints & PLUGIN_IS_BRIDGE) + // osc_send_set_parameter_midi_cc(&plugin->osc.data, plugin->id, parameter_id, midi_cc); + } + virtual void set_chunk_data(const char* string_data) { Q_UNUSED(string_data); @@ -463,6 +502,37 @@ public: // virtual void x_set_midi_program(uint32_t midi_program_id, bool gui_send, bool block) = 0; // virtual void x_set_custom_data(CustomDataType dtype, const char* key, const char* value, bool gui_send) = 0; + void postpone_event(PluginPostEventType type, int32_t index, double value) + { + post_events.lock.lock(); + + for (unsigned short i=0; i + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * For a full copy of the GNU General Public License see the COPYING file + */ + +#include "carla_threads.h" +#include "carla_plugin.h" + +#include + +// -------------------------------------------------------------------------------------------------------- +// CarlaCheckThread + +CarlaCheckThread::CarlaCheckThread(QObject *parent) : + QThread(parent) +{ + qDebug("CarlaCheckThread::CarlaCheckThread(%p)", parent); +} + +void CarlaCheckThread::run() +{ + qDebug("CarlaCheckThread::run()"); + + uint32_t j; + PluginPostEvent post_events[MAX_POSTEVENTS]; + + while (carla_is_engine_running()) + { + for (unsigned short i=0; iid() >= 0) + { + // -------------------------------------------------------------------------------------------------------- + // Process postponed events + + // Make a safe copy of events, and clear them + plugin->post_events_copy(post_events); + + // Process events now + for (j=0; j < MAX_POSTEVENTS; j++) + { + if (post_events[j].valid) + { + switch (post_events[j].type) + { + case PostEventDebug: + callback_action(CALLBACK_DEBUG, plugin->id(), post_events[j].index, 0, post_events[j].value); + break; + + case PostEventParameterChange: + //osc_send_set_parameter_value(&global_osc_data, plugin->id, post_events[j].index, post_events[j].value); + callback_action(CALLBACK_PARAMETER_CHANGED, plugin->id(), post_events[j].index, 0, post_events[j].value); + + //if (plugin->hints & PLUGIN_IS_BRIDGE) + // osc_send_control(&plugin->osc.data, post_events[j].index, post_events[j].value); + + break; + + case PostEventProgramChange: + //osc_send_set_program(&global_osc_data, plugin->id, post_events[j].index); + callback_action(CALLBACK_PROGRAM_CHANGED, plugin->id(), post_events[j].index, 0, 0.0); + + //if (plugin->hints & PLUGIN_IS_BRIDGE) + // osc_send_program(&plugin->osc.data, post_events[j].index); + + //for (uint32_t k=0; k < plugin->param.count; k++) + // osc_send_set_default_value(&global_osc_data, plugin->id, k, plugin->param.ranges[k].def); + + break; + + case PostEventMidiProgramChange: + //osc_send_set_midi_program(&global_osc_data, plugin->id, post_events[j].index); + callback_action(CALLBACK_MIDI_PROGRAM_CHANGED, plugin->id(), post_events[j].index, 0, 0.0); + + //if (plugin->type == PLUGIN_DSSI) + // osc_send_program_as_midi(&plugin->osc.data, plugin->midiprog.data[post_events[j].index].bank, plugin->midiprog.data[post_events[j].index].program); + + //if (plugin->hints & PLUGIN_IS_BRIDGE) + // osc_send_midi_program(&plugin->osc.data, plugin->midiprog.data[post_events[j].index].bank, plugin->midiprog.data[post_events[j].index].program); + + //for (uint32_t k=0; k < plugin->param.count; k++) + // osc_send_set_default_value(&global_osc_data, plugin->id, k, plugin->param.ranges[k].def); + + break; + + case PostEventNoteOn: + //osc_send_note_on(&global_osc_data, plugin->id, post_events[j].index, post_events[j].value); + callback_action(CALLBACK_NOTE_ON, plugin->id(), post_events[j].index, post_events[j].value, 0.0); + + //if (plugin->hints & PLUGIN_IS_BRIDGE) + // osc_send_note_on(&plugin->osc.data, plugin->id, post_events[j].index, post_events[j].value); + + break; + + case PostEventNoteOff: + //osc_send_note_off(&global_osc_data, plugin->id, post_events[j].index, post_events[j].value); + callback_action(CALLBACK_NOTE_OFF, plugin->id(), post_events[j].index, post_events[j].value, 0.0); + + //if (plugin->hints & PLUGIN_IS_BRIDGE) + // osc_send_note_off(&plugin->osc.data, plugin->id, post_events[j].index, post_events[j].value); + + break; + + default: + break; + } + } + } + + // -------------------------------------------------------------------------------------------------------- + // Idle plugin +// if (plugin->gui.visible && plugin->gui.type != GUI_EXTERNAL_OSC) +// plugin->idle_gui(); + + // -------------------------------------------------------------------------------------------------------- + // Update ports + + // Check if it needs update +// bool update_ports_gui = (plugin->gui.visible && plugin->gui.type == GUI_EXTERNAL_OSC && plugin->osc.data.target); + +// if (!global_osc_data.target && !update_ports_gui) +// continue; + +// // Update +// for (j=0; j < plugin->param.count; j++) +// { +// if (plugin->param.data[j].type == PARAMETER_OUTPUT && (plugin->param.data[j].hints & PARAMETER_IS_AUTOMABLE) > 0) +// { +// if (update_ports_gui) +// osc_send_control(&plugin->osc.data, plugin->param.data[j].rindex, plugin->param.buffers[j]); + +// osc_send_set_parameter_value(&global_osc_data, plugin->id, j, plugin->param.buffers[j]); +// } +// } + + // -------------------------------------------------------------------------------------------------------- + // Send peak values (OSC) +// if (global_osc_data.target) +// { +// if (plugin->ain.count > 0) +// { +// osc_send_set_input_peak_value(&global_osc_data, plugin->id, 1, ains_peak[(plugin->id*2)+0]); +// osc_send_set_input_peak_value(&global_osc_data, plugin->id, 2, ains_peak[(plugin->id*2)+1]); +// } +// if (plugin->aout.count > 0) +// { +// osc_send_set_output_peak_value(&global_osc_data, plugin->id, 1, aouts_peak[(plugin->id*2)+0]); +// osc_send_set_output_peak_value(&global_osc_data, plugin->id, 2, aouts_peak[(plugin->id*2)+1]); +// } +// } + } + } + usleep(50000); // 50 ms + } +} + +// -------------------------------------------------------------------------------------------------------- +// CarlaPluginThread + +CarlaPluginThread::CarlaPluginThread(QObject *parent) : + QThread(parent) +{ + qDebug("CarlaPluginThread::CarlaPluginThread(%p)", parent); + //m_process = new QProcess(parent); +} + +CarlaPluginThread::~CarlaPluginThread() +{ + // TODO - kill process + //delete m_process; +} + +void CarlaPluginThread::set_plugin(CarlaPlugin* plugin) +{ + m_plugin = plugin; +} + +void CarlaPluginThread::run() +{ +} diff --git a/src/carla/carla_threads.h b/src/carla/carla_threads.h new file mode 100644 index 0000000..6a82db1 --- /dev/null +++ b/src/carla/carla_threads.h @@ -0,0 +1,53 @@ +/* + * JACK Backend code for Carla + * Copyright (C) 2011-2012 Filipe Coelho + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * For a full copy of the GNU General Public License see the COPYING file + */ + +#ifndef CARLA_THREADS_H +#define CARLA_THREADS_H + +#include + +class QProcess; +class CarlaPlugin; + +// -------------------------------------------------------------------------------------------------------- +// CarlaCheckThread + +class CarlaCheckThread : public QThread +{ +public: + CarlaCheckThread(QObject* parent = 0); + void run(); +}; + +// -------------------------------------------------------------------------------------------------------- +// CarlaPluginThread + +class CarlaPluginThread : public QThread +{ +public: + CarlaPluginThread(QObject* parent=0); + ~CarlaPluginThread(); + + void set_plugin(CarlaPlugin* plugin); + void run(); + +private: + CarlaPlugin* m_plugin; + QProcess* m_process; +}; + +#endif // CARLA_THREADS_H diff --git a/src/carla/ladspa.cpp b/src/carla/ladspa.cpp index 642845e..e1ec56f 100644 --- a/src/carla/ladspa.cpp +++ b/src/carla/ladspa.cpp @@ -652,8 +652,8 @@ public: if (mode == 0xB0) { unsigned char status = pin_event.buffer[1] & 0x7F; - //unsigned char velo = pin_event.buffer[2] & 0x7F; - //double value, velo_per = double(velo)/127; + unsigned char velo = pin_event.buffer[2] & 0x7F; + double value, velo_per = double(velo)/127; // Control GUI stuff (channel 0 only) if (channel == 0) @@ -661,49 +661,49 @@ public: if (status == 0x78) { // All Sound Off - //set_active(false, false, false); - //postpone_event(PostEventParameter, PARAMETER_ACTIVE, 0.0); + set_active(false, false, false); + postpone_event(PostEventParameterChange, PARAMETER_ACTIVE, 0.0); break; } else if (status == 0x09 && (m_hints & PLUGIN_CAN_DRYWET) > 0) { // Dry/Wet (using '0x09', undefined) - //set_drywet(velo_per, false, false); - //postpone_event(PostEventParameter, PARAMETER_DRYWET, velo_per); + set_drywet(velo_per, false, false); + postpone_event(PostEventParameterChange, PARAMETER_DRYWET, velo_per); } else if (status == 0x07 && (m_hints & PLUGIN_CAN_VOL) > 0) { // Volume - //value = double(velo)/100; - //set_vol(value, false, false); - //postpone_event(PostEventParameter, PARAMETER_VOLUME, value); + value = double(velo)/100; + set_volume(value, false, false); + postpone_event(PostEventParameterChange, PARAMETER_VOLUME, value); } else if (status == 0x08 && (m_hints & PLUGIN_CAN_BALANCE) > 0) { // Balance -// double left, right; -// value = (double(velo)-63.5)/63.5; - -// if (value < 0) -// { -// left = -1.0; -// right = (value*2)+1.0; -// } -// else if (value > 0) -// { -// left = (value*2)-1.0; -// right = 1.0; -// } -// else -// { -// left = -1.0; -// right = 1.0; -// } - - //set_balance_left(left, false, false); - //set_balance_right(right, false, false); - //postpone_event(PostEventParameter, PARAMETER_BALANCE_LEFT, left); - //postpone_event(PostEventParameter, PARAMETER_BALANCE_RIGHT, right); + double left, right; + value = (double(velo)-63.5)/63.5; + + if (value < 0) + { + left = -1.0; + right = (value*2)+1.0; + } + else if (value > 0) + { + left = (value*2)-1.0; + right = 1.0; + } + else + { + left = -1.0; + right = 1.0; + } + + set_balance_left(left, false, false); + set_balance_right(right, false, false); + postpone_event(PostEventParameterChange, PARAMETER_BALANCE_LEFT, left); + postpone_event(PostEventParameterChange, PARAMETER_BALANCE_RIGHT, right); } } @@ -713,9 +713,9 @@ public: if (param.data[k].type == PARAMETER_INPUT && (param.data[k].hints & PARAMETER_IS_AUTOMABLE) > 0 && param.data[k].midi_channel == channel && param.data[k].midi_cc == status) { - //value = (velo_per * (param.ranges[k].max - param.ranges[k].min)) + param.ranges[k].min; - //set_parameter_value(k, value, false, false, false); - //postpone_event(PostEventParameter, k, value); + value = (velo_per * (param.ranges[k].max - param.ranges[k].min)) + param.ranges[k].min; + set_parameter_value(k, value, false, false, false); + postpone_event(PostEventParameterChange, k, value); } } } diff --git a/src/carla_backend.py b/src/carla_backend.py index f439e48..d0609a6 100644 --- a/src/carla_backend.py +++ b/src/carla_backend.py @@ -305,7 +305,7 @@ print("SF2 ->", DEFAULT_SF2_PATH) # ------------------------------------------------------------------------------------------------ # Plugin Query (helper functions) -def findBinaries(PATH, OS): +def findBinaries(bPATH, OS): binaries = [] if (OS == "WINDOWS"): @@ -315,32 +315,32 @@ def findBinaries(PATH, OS): else: extensions = (".so", ".sO", ".SO", ".So") - for root, dirs, files in os.walk(PATH): + for root, dirs, files in os.walk(bPATH): for name in [name for name in files if name.endswith(extensions)]: binaries.append(os.path.join(root, name)) return binaries -def findSoundFonts(PATH): +def findSoundFonts(bPATH): soundfonts = [] extensions = (".sf2", ".sF2", ".SF2", ".Sf2") - for root, dirs, files in os.walk(PATH): + for root, dirs, files in os.walk(bPATH): for name in [name for name in files if name.endswith(extensions)]: soundfonts.append(os.path.join(root, name)) return soundfonts -#def findLV2Bundles(PATH): - #bundles = [] - #extensions = (".lv2", ".lV2", ".LV2", ".Lv2") +def findLV2Bundles(bPATH): + bundles = [] + extensions = (".lv2", ".lV2", ".LV2", ".Lv2") - #for root, dirs, files in os.walk(PATH): - #for dir_ in [dir_ for dir_ in dirs if dir_.endswith(extensions)]: - #bundles.append(os.path.join(root, dir_)) + for root, dirs, files in os.walk(bPATH): + for dir_ in [dir_ for dir_ in dirs if dir_.endswith(extensions)]: + bundles.append(os.path.join(root, dir_)) - #return bundles + return bundles def findDSSIGUI(filename, name, label): plugin_dir = filename.rsplit(".", 1)[0]