Пример #1
0
class Ui(QMainWindow):
    shutdown_threads = pyqtSignal(name='shutdown_threads')

    def __init__(self):
        super(Ui, self).__init__()

        # Dictionary of methods to call in response to changes to
        # controls made directly on the amplifier.
        self.response_funcs = {
            'voice': self.voice_changed_on_amp,
            'gain': self.gain_changed_on_amp,
            'volume': self.volume_changed_on_amp,
            'bass': self.bass_changed_on_amp,
            'middle': self.middle_changed_on_amp,
            'treble': self.treble_changed_on_amp,
            'isf': self.isf_changed_on_amp,
            'tvp_switch': self.tvp_switch_changed_on_amp,
            'tvp_valve': self.tvp_valve_changed_on_amp,
            'mod_switch': self.mod_switch_changed_on_amp,
            'delay_switch': self.delay_switch_changed_on_amp,
            'reverb_switch': self.reverb_switch_changed_on_amp,
            'mod_type': self.mod_type_changed_on_amp,
            'mod_segval': self.mod_segval_changed_on_amp,
            'mod_level': self.mod_level_changed_on_amp,
            'mod_speed': self.mod_speed_changed_on_amp,
            'mod_manual': self.mod_manual_changed_on_amp,
            'delay_type': self.delay_type_changed_on_amp,
            'delay_feedback': self.delay_feedback_changed_on_amp,
            'delay_level': self.delay_level_changed_on_amp,
            'delay_time': self.delay_time_changed_on_amp,
            'reverb_type': self.reverb_type_changed_on_amp,
            'reverb_size': self.reverb_size_changed_on_amp,
            'reverb_level': self.reverb_level_changed_on_amp,
            'fx_focus': self.fx_focus_changed_on_amp,
            'preset': self.preset_changed_on_amp,
            'manual_mode': self.manual_mode_changed_on_amp,
            'tuner_mode': self.tuner_mode_changed_on_amp,
            'tuner_note': self.tuner_note_changed_on_amp,
            'tuner_delta': self.tuner_delta_changed_on_amp,
            'resonance': self.resonance_changed_on_amp,
            'presence': self.presence_changed_on_amp,
            'master_volume': self.master_volume_changed_on_amp,
            'preset_name': self.preset_name_from_amp,
            'preset_settings': self.preset_settings_from_amp,
        }

        uif = os.path.join(os.path.split(__file__)[0], 'outsider.ui')
        logger.debug('loading GUI file: {0}'.format(uif))
        uic.loadUi(uif, self)

        self.amp_mutex = None
        self.amp = BlackstarIDAmp()
        self.watcher_thread = None

        # For now we don't do anything with preset settings
        # information other than store them in this list
        self.preset_settings = [None] * 128

        self.controls_enabled(False)
        self.show()

    def controls_enabled(self, bool):
        # Disable/Enable all widgets except the connect button (always enabled) and the master controls (always disabled)
        if bool is True:
            widgets = self.findChildren(QGroupBox)  #(QObject)
            for w in widgets:
                if w == self.masterGroupBox:
                    pass
                elif w.objectName(
                ) == 'TVPGroupBox' and self.amp.model == 'id-core':
                    # self.TVPComboBox.setCurrentText('6L6')
                    # self.TVPRadioButton.setChecked(False)
                    # Don't enable as Core has  fixed TVP, probably with type 6L6
                    pass
                else:
                    w.setEnabled(bool)

        elif bool is False:
            widgets = self.findChildren(QGroupBox)  #(QObject)
            for w in widgets:
                w.setEnabled(bool)

            widgets = self.findChildren(QSlider)
            for w in widgets:
                w.blockSignals(True)
                w.setValue(0)
                w.blockSignals(False)

            widgets = self.findChildren(QLCDNumber)
            for w in widgets:
                w.blockSignals(True)  # Not nescessary
                w.display(0)
                w.blockSignals(False)  # Not nescessary

            widgets = self.findChildren(QRadioButton)
            for w in widgets:
                w.blockSignals(True)
                w.setChecked(False)
                w.blockSignals(False)

    def connect(self):
        try:
            self.amp.connect()
            self.amp.drain()
            self.start_amp_watcher_thread()
            self.amp.startup()
            self.amp.get_all_preset_names()
        except NotConnectedError:
            raise

    def disconnect(self):
        if self.watcher_thread is not None:
            logger.debug('Closing down amplifier watching thread')
            self.shutdown_threads.emit()
            self.watcher_thread.quit()
            self.watcher_thread.wait()
            logger.debug('Amplifier watching thread finished')

        if self.amp.connected is True:
            self.amp.disconnect()

    def start_amp_watcher_thread(self):
        # Set up thread to watch for manual changes of the amp
        # controls at the amp (rather than gui) so we can update the
        # gui controls as needed. The way this is done is inspired
        # by:
        # http://stackoverflow.com/questions/29243692/pyqt5-how-to-make-qthread-return-data-to-main-thread
        # https://mayaposch.wordpress.com/2011/11/01/how-to-really-truly-use-qthreads-the-full-explanation/
        self.watcher_thread = QThread()
        self.amp_mutex = QMutex()
        self.watcher = AmpControlWatcher(self.amp, self.amp_mutex)
        self.watcher.have_data.connect(self.new_data_from_amp)
        self.shutdown_threads.connect(self.watcher.stop_watching)
        self.watcher.moveToThread(self.watcher_thread)
        self.watcher_thread.started.connect(self.watcher.work)

        self.watcher_thread.start()

    def closeEvent(self, event):
        # Ran when the application is closed.
        self.disconnect()
        super(Ui, self).close()
        logger.debug('Exiting')

    @pyqtSlot(dict)
    def new_data_from_amp(self, settings):
        for control, value in settings.items():
            try:
                self.response_funcs[control](value)
            except KeyError:
                logger.error('Unrecognized control {0}'.format(control))

    ######################################################################
    # The following methods are called when data is received from the amp
    ######################################################################
    def voice_changed_on_amp(self, value):
        self.voiceComboBox.blockSignals(True)
        self.voiceComboBox.setCurrentIndex(value)
        self.voiceComboBox.blockSignals(False)

    def gain_changed_on_amp(self, value):
        self.gainSlider.blockSignals(True)
        self.gainSlider.setValue(value)
        self.gainLcdNumber.display(value)
        self.gainSlider.blockSignals(False)

    def volume_changed_on_amp(self, value):
        self.volumeSlider.blockSignals(True)
        self.volumeSlider.setValue(value)
        self.volumeLcdNumber.display(value)
        self.volumeSlider.blockSignals(False)

    def bass_changed_on_amp(self, value):
        self.bassSlider.blockSignals(True)
        self.bassSlider.setValue(value)
        self.bassLcdNumber.display(value)
        self.bassSlider.blockSignals(False)

    def middle_changed_on_amp(self, value):
        self.middleSlider.blockSignals(True)
        self.middleSlider.setValue(value)
        self.middleLcdNumber.display(value)
        self.middleSlider.blockSignals(False)

    def treble_changed_on_amp(self, value):
        self.trebleSlider.blockSignals(True)
        self.trebleSlider.setValue(value)
        self.trebleLcdNumber.display(value)
        self.trebleSlider.blockSignals(False)

    def isf_changed_on_amp(self, value):
        self.isfSlider.blockSignals(True)
        self.isfSlider.setValue(value)
        self.isfLcdNumber.display(value)
        self.isfSlider.blockSignals(False)

    def tvp_switch_changed_on_amp(self, value):
        value = bool(value)
        self.TVPRadioButton.blockSignals(True)
        self.TVPRadioButton.setChecked(value)
        self.TVPComboBox.setEnabled(value)
        self.TVPRadioButton.blockSignals(False)

    def tvp_valve_changed_on_amp(self, value):
        self.TVPComboBox.blockSignals(True)
        self.TVPComboBox.setCurrentIndex(value)
        self.TVPComboBox.blockSignals(False)

    def mod_switch_changed_on_amp(self, value):
        value = bool(value)
        self.modRadioButton.blockSignals(True)
        self.modRadioButton.setChecked(value)
        self.modComboBox.setEnabled(value)
        self.modSegValSlider.setEnabled(value)
        self.modSegValLabel.setEnabled(value)
        self.modSegValLcdNumber.setEnabled(value)
        self.modSpeedSlider.setEnabled(value)
        self.modSpeedLabel.setEnabled(value)
        self.modSpeedLcdNumber.setEnabled(value)
        self.modLevelSlider.setEnabled(value)
        self.modLevelLabel.setEnabled(value)
        self.modLevelLcdNumber.setEnabled(value)
        self.modRadioButton.blockSignals(False)
        self.assess_manual_enabled()

    def delay_switch_changed_on_amp(self, value):
        value = bool(value)
        self.delayRadioButton.blockSignals(True)
        self.delayRadioButton.setChecked(value)
        self.delayComboBox.setEnabled(value)
        self.delayFeedbackSlider.setEnabled(value)
        self.delayFeedbackLabel.setEnabled(value)
        self.delayFeedbackLcdNumber.setEnabled(value)
        self.delayTimeSlider.setEnabled(value)
        self.delayTimeLabel.setEnabled(value)
        self.delayTimeLcdNumber.setEnabled(value)
        self.delayLevelSlider.setEnabled(value)
        self.delayLevelLabel.setEnabled(value)
        self.delayLevelLcdNumber.setEnabled(value)
        self.delayRadioButton.blockSignals(False)

    def reverb_switch_changed_on_amp(self, value):
        value = bool(value)
        self.reverbRadioButton.blockSignals(True)
        self.reverbRadioButton.setChecked(value)
        self.reverbComboBox.setEnabled(value)
        self.reverbSizeSlider.setEnabled(value)
        self.reverbSizeLabel.setEnabled(value)
        self.reverbSizeLcdNumber.setEnabled(value)
        self.reverbLevelSlider.setEnabled(value)
        self.reverbLevelLabel.setEnabled(value)
        self.reverbLevelLcdNumber.setEnabled(value)
        self.reverbRadioButton.blockSignals(False)

    def mod_type_changed_on_amp(self, value):
        self.modComboBox.blockSignals(True)
        self.modComboBox.setCurrentIndex(value)
        self.modComboBox.blockSignals(False)
        self.mod_type_changed(value)

    def mod_segval_changed_on_amp(self, value):
        self.modSegValSlider.blockSignals(True)
        self.modSegValSlider.setValue(value)
        self.modSegValLcdNumber.display(value)
        self.modSegValSlider.blockSignals(False)

    def mod_level_changed_on_amp(self, value):
        self.modLevelSlider.blockSignals(True)
        self.modLevelSlider.setValue(value)
        self.modLevelLcdNumber.display(value)
        self.modLevelSlider.blockSignals(False)

    def mod_speed_changed_on_amp(self, value):
        self.modSpeedSlider.blockSignals(True)
        self.modSpeedSlider.setValue(value)
        self.modSpeedLcdNumber.display(value)
        self.modSpeedSlider.blockSignals(False)

    def mod_manual_changed_on_amp(self, value):
        self.modManualSlider.blockSignals(True)
        self.modManualSlider.setValue(value)
        self.modManualLcdNumber.display(value)
        self.modManualSlider.blockSignals(False)

    def delay_type_changed_on_amp(self, value):
        self.delayComboBox.blockSignals(True)
        self.delayComboBox.setCurrentIndex(value)
        self.delayComboBox.blockSignals(False)

    def delay_feedback_changed_on_amp(self, value):
        self.delayFeedbackSlider.blockSignals(True)
        self.delayFeedbackSlider.setValue(value)
        self.delayFeedbackLcdNumber.display(value)
        self.delayFeedbackSlider.blockSignals(False)

    def delay_level_changed_on_amp(self, value):
        self.delayLevelSlider.blockSignals(True)
        self.delayLevelSlider.setValue(value)
        self.delayLevelLcdNumber.display(value)
        self.delayLevelSlider.blockSignals(False)

    def delay_time_changed_on_amp(self, value):
        self.delayTimeSlider.blockSignals(True)
        self.delayTimeSlider.setValue(value)
        self.delayTimeLcdNumber.display(value)
        self.delayTimeSlider.blockSignals(False)

    def reverb_type_changed_on_amp(self, value):
        self.reverbComboBox.blockSignals(True)
        self.reverbComboBox.setCurrentIndex(value)
        self.reverbComboBox.blockSignals(False)

    def reverb_size_changed_on_amp(self, value):
        self.reverbSizeSlider.blockSignals(True)
        self.reverbSizeSlider.setValue(value)
        self.reverbSizeLcdNumber.display(value)
        self.reverbSizeSlider.blockSignals(False)

    def reverb_level_changed_on_amp(self, value):
        self.reverbLevelSlider.blockSignals(True)
        self.reverbLevelSlider.setValue(value)
        self.reverbLevelLcdNumber.display(value)
        self.reverbLevelSlider.blockSignals(False)

    def fx_focus_changed_on_amp(self, value):
        # This is a bit of a misnomer, as the amp doesn't emit data if
        # the user changes the effect focus on the amp. However, when
        # the user disables an effect in the GUI, if that effect had
        # focus, we want to move the focus to another effect, if one
        # is enabled. The way we do that, is in the slots associated
        # with toggling an effect (see below), if the effect is
        # disabled we issue a packet to query the state of all
        # controls, which gives us an opportunity to react in this
        # function here.
        if value == 1 and not self.modRadioButton.isChecked():
            if self.delayRadioButton.isChecked():
                self.amp.set_control('fx_focus', 2)
            elif self.reverbRadioButton.isChecked():
                self.amp.set_control('fx_focus', 3)

        elif value == 2 and not self.delayRadioButton.isChecked():
            if self.reverbRadioButton.isChecked():
                self.amp.set_control('fx_focus', 3)
            elif self.modRadioButton.isChecked():
                self.amp.set_control('fx_focus', 1)

        if value == 3 and not self.reverbRadioButton.isChecked():
            if self.modRadioButton.isChecked():
                self.amp.set_control('fx_focus', 1)
            elif self.delayRadioButton.isChecked():
                self.amp.set_control('fx_focus', 2)

    def preset_name_from_amp(self, namelist):
        idx = namelist[0] - 1  # Presets are numbered from 1
        name = str(namelist[0]) + '. ' + namelist[1]
        item = self.presetNamesList.item(idx)
        if item is None:
            self.presetNamesList.insertItem(idx, name)
        else:
            item.setText(name)

    def preset_settings_from_amp(self, settings):
        self.preset_settings[settings.preset_number] = settings

    def preset_changed_on_amp(self, value):
        # TODO: This function is a stub for now, but will need hooking
        # up to a combo box widget in the gui for selecting/indicating
        # preset
        logger.debug('preset changed on amp: {0}'.format(value))
        self.presetNamesList.setCurrentRow(value - 1)

    def manual_mode_changed_on_amp(self, value):
        # TODO: This also needs hooking up to the preset combo box in
        # the GUI
        if value == 1:
            # Here we'd need to set the combo box to "Manual"
            pass
        else:
            # We're no longer in manual mode, but since in this case
            # another packet will indicate the preset selected,
            # there's no need to do anything here.
            pass
        logger.debug('manual_mode changed on amp: {0}'.format(value))

    def tuner_mode_changed_on_amp(self, value):
        # TODO: Stub for now - needs hooking into a suitable tuner widget
        logger.debug('tuner_mode changed on amp: {0}'.format(value))

    def tuner_note_changed_on_amp(self, value):
        # TODO: Stub for now - needs hooking into a suitable tuner widget
        logger.debug('tuner_note changed on amp: {0}'.format(value))

    def tuner_delta_changed_on_amp(self, value):
        # TODO: Stub for now - needs hooking into a suitable tuner widget
        logger.debug('tuner_delta changed on amp: {0}'.format(value))

    def resonance_changed_on_amp(self, value):
        self.resonanceSlider.blockSignals(True)
        self.resonanceSlider.setValue(value)
        self.resonanceLcdNumber.display(value)
        self.resonanceSlider.blockSignals(False)
        logger.debug('resonance changed on amp: {0}'.format(value))

    def presence_changed_on_amp(self, value):
        self.presenceSlider.blockSignals(True)
        self.presenceSlider.setValue(value)
        self.presenceLcdNumber.display(value)
        self.presenceSlider.blockSignals(False)
        logger.debug('presence changed on amp: {0}'.format(value))

    def master_volume_changed_on_amp(self, value):
        self.masterVolumeSlider.blockSignals(True)
        self.masterVolumeSlider.setValue(value)
        self.masterVolumeLcdNumber.display(value)
        self.masterVolumeSlider.blockSignals(False)
        logger.debug('master_volume changed on amp: {0}'.format(value))

    ##################################################################
    # The following methods are the slots for changes made on the gui
    ##################################################################
    @pyqtSlot()
    def on_connectToAmpButton_clicked(self):
        if self.amp.connected is False:
            try:
                self.connect()
                # Enable widgets
                self.controls_enabled(True)
                self.connectToAmpButton.setText('Disconnect Amp')

            except NotConnectedError:
                QMessageBox.information(self, 'Outsider', 'No amplifier found')
        else:
            self.disconnect()
            self.controls_enabled(False)
            self.connectToAmpButton.setText('Connect to Amp')

    @pyqtSlot(int)
    def on_volumeSlider_valueChanged(self, value):
        logger.debug('Volume slider: {0}'.format(value))
        self.amp.set_control('volume', value)

    @pyqtSlot(int)
    def on_gainSlider_valueChanged(self, value):
        logger.debug('Gain slider: {0}'.format(value))
        self.amp.set_control('gain', value)

    @pyqtSlot(int)
    def on_bassSlider_valueChanged(self, value):
        logger.debug('Bass slider: {0}'.format(value))
        self.amp.set_control('bass', value)

    @pyqtSlot(int)
    def on_middleSlider_valueChanged(self, value):
        logger.debug('Middle slider: {0}'.format(value))
        self.amp.set_control('middle', value)

    @pyqtSlot(int)
    def on_trebleSlider_valueChanged(self, value):
        logger.debug('Treble slider: {0}'.format(value))
        self.amp.set_control('treble', value)

    @pyqtSlot(int)
    def on_isfSlider_valueChanged(self, value):
        logger.debug('ISF slider: {0}'.format(value))
        self.amp.set_control('isf', value)

    @pyqtSlot(int)
    def on_TVPComboBox_currentIndexChanged(self, idx):
        logger.debug('TVP selection: {0}'.format(idx))
        self.amp.set_control('tvp_valve', idx)

    @pyqtSlot(bool)
    def on_TVPRadioButton_toggled(self, state):
        logger.debug('TVP switch: {0}'.format(state))
        if state == True:
            self.amp.set_control('tvp_switch', 1)
        else:
            self.amp.set_control('tvp_switch', 0)

    @pyqtSlot(int)
    def on_voiceComboBox_currentIndexChanged(self, idx):
        logger.debug('Voice selection: {0}'.format(idx))
        self.amp.set_control('voice', idx)

    @pyqtSlot(bool)
    def on_modRadioButton_toggled(self, state):
        logger.debug('Mod switch: {0}'.format(state))
        self.amp.set_control('mod_switch', state)
        if state == 1:
            self.amp.set_control('fx_focus', 1)
        else:
            # Find out if the mod effect had focus before being
            # deactivated and shift focus to another effect if
            # possible. The only mechanism we have available to do
            # this is to get the status of all controls, sadly.
            self.amp.startup()
        self.assess_manual_enabled()

    @pyqtSlot(int)
    def on_modComboBox_currentIndexChanged(self, value):
        logger.debug('Mod Combo Box: {0}'.format(value))
        self.amp.set_control('mod_type', value)
        self.amp.set_control('fx_focus', 1)

    @pyqtSlot(int)
    def on_modSegValSlider_valueChanged(self, value):
        logger.debug('Mod SegVal slider: {0}'.format(value))
        self.amp.set_control('mod_segval', value)
        self.amp.set_control('fx_focus', 1)

    @pyqtSlot(int)
    def on_modLevelSlider_valueChanged(self, value):
        logger.debug('Mod Level slider: {0}'.format(value))
        self.amp.set_control('mod_level', value)
        self.amp.set_control('fx_focus', 1)

    @pyqtSlot(int)
    def on_modSpeedSlider_valueChanged(self, value):
        logger.debug('Mod Speed slider: {0}'.format(value))
        self.amp.set_control('mod_speed', value)
        self.amp.set_control('fx_focus', 1)

    @pyqtSlot(int)
    def on_modManualSlider_valueChanged(self, value):
        logger.debug('Mod Manual slider: {0}'.format(value))
        self.amp.set_control('mod_manual', value)
        self.amp.set_control('fx_focus', 1)

    @pyqtSlot(bool)
    def on_delayRadioButton_toggled(self, state):
        logger.debug('Delay switch: {0}'.format(state))
        self.amp.set_control('delay_switch', state)
        if state == 1:
            self.amp.set_control('fx_focus', 2)
        else:
            # Find out if the mod effect had focus before being
            # deactivated and shift focus to another effect if
            # possible. The only mechanism we have available to do
            # this is to get the status of all controls, sadly.
            self.amp.startup()

    @pyqtSlot(int)
    def on_delayComboBox_currentIndexChanged(self, value):
        logger.debug('Delay Combo Box: {0}'.format(value))
        self.amp.set_control('delay_type', value)
        self.amp.set_control('fx_focus', 2)

    @pyqtSlot(int)
    def on_delayFeedbackSlider_valueChanged(self, value):
        logger.debug('Delay feedback slider: {0}'.format(value))
        self.amp.set_control('delay_feedback', value)
        self.amp.set_control('fx_focus', 2)

    @pyqtSlot(int)
    def on_delayLevelSlider_valueChanged(self, value):
        logger.debug('Delay Level slider: {0}'.format(value))
        self.amp.set_control('delay_level', value)
        self.amp.set_control('fx_focus', 2)

    @pyqtSlot(int)
    def on_delayTimeSlider_valueChanged(self, value):
        logger.debug('Delay Time slider: {0}'.format(value))
        self.amp.set_control('delay_time', value)
        self.amp.set_control('fx_focus', 2)

    @pyqtSlot(bool)
    def on_reverbRadioButton_toggled(self, state):
        logger.debug('Reverb switch: {0}'.format(state))
        self.amp.set_control('reverb_switch', state)
        if state == 1:
            self.amp.set_control('fx_focus', 3)
        else:
            # Find out if the mod effect had focus before being
            # deactivated and shift focus to another effect if
            # possible. The only mechanism we have available to do
            # this is to get the status of all controls, sadly.
            self.amp.startup()

    @pyqtSlot(int)
    def on_reverbComboBox_currentIndexChanged(self, value):
        logger.debug('Reverb Combo Box: {0}'.format(value))
        self.amp.set_control('reverb_type', value)
        self.amp.set_control('fx_focus', 3)

    @pyqtSlot(int)
    def on_reverbSizeSlider_valueChanged(self, value):
        logger.debug('Reverb Size slider: {0}'.format(value))
        self.amp.set_control('reverb_size', value)
        self.amp.set_control('fx_focus', 3)

    @pyqtSlot(int)
    def on_reverbLevelSlider_valueChanged(self, value):
        logger.debug('Reverb Level slider: {0}'.format(value))
        self.amp.set_control('reverb_level', value)
        self.amp.set_control('fx_focus', 3)

    @pyqtSlot(QListWidgetItem)
    def on_presetNamesList_itemDoubleClicked(self, item):
        idx = self.presetNamesList.currentRow()
        preset = idx + 1  # Presets are numbered from 1
        self.amp.select_preset(preset)

    @pyqtSlot()
    def on_renamePresetPushButton_clicked(self):
        idx = self.presetNamesList.currentRow()
        preset = idx + 1  # Presets are numbered from 1

        name, ok = QInputDialog.getText(
            self, 'Input Dialog',
            'Enter new name for preset {0}:'.format(preset))
        if ok == True:
            # We need to grab the amp mutex to stop the amp watcher
            # thread from consuming the packets emitted by the amp in
            # the preset rename process
            self.amp_mutex.lock()
            self.amp.set_preset_name(preset, name)
            self.amp_mutex.unlock()

    # When the modulation type is changed, we want to change the label
    # associated with the segment value control. So, we need to define
    # a slot to trigger when the modulation type is changed, and a
    # signal to raise to communicate back to the UI what the label
    # should be changed to. The signal has argument type str, which is
    # automatically converted to type QString.
    mod_segval_label_update = pyqtSignal(str)

    @pyqtSlot(int)
    def mod_type_changed(self, value):
        if value == 0:
            self.mod_segval_label_update.emit('Mix')
        elif value == 1:
            self.mod_segval_label_update.emit('Feedback')
        elif value == 2:
            self.mod_segval_label_update.emit('Mix')
        elif value == 3:
            self.mod_segval_label_update.emit('FreqMod')
        self.assess_manual_enabled()

    @pyqtSlot(int)
    def on_resonanceSlider_valueChanged(self, value):
        logger.debug('Resonance slider: {0}'.format(value))
        self.amp.set_control('resonance', value)

    @pyqtSlot(int)
    def on_presenceSlider_valueChanged(self, value):
        logger.debug('Presence slider: {0}'.format(value))
        self.amp.set_control('presence', value)

    @pyqtSlot(int)
    def on_masterVolumeSlider_valueChanged(self, value):
        logger.debug('Master volume slider: {0}'.format(value))
        self.amp.set_control('master_volume', value)

    # When the modulation is enabled and the modution type is flanger, enable
    # the manual control.
    def assess_manual_enabled(self):
        value = self.modRadioButton.isChecked(
        ) and self.modComboBox.currentIndex() == 1
        self.modManualSlider.blockSignals(True)
        self.modManualSlider.setEnabled(value)
        self.modManualSlider.blockSignals(False)
Пример #2
0
class Ui(QMainWindow):
    shutdown_threads = pyqtSignal(name='shutdown_threads')

    def __init__(self):
        super(Ui, self).__init__()

        # Dictionary of methods to call in response to changes to
        # controls made directly on the amplifier.
        self.response_funcs = {
            'voice': self.voice_changed_on_amp,
            'gain': self.gain_changed_on_amp,
            'volume': self.volume_changed_on_amp,
            'bass': self.bass_changed_on_amp,
            'middle': self.middle_changed_on_amp,
            'treble': self.treble_changed_on_amp,
            'isf': self.isf_changed_on_amp,
            'tvp_switch': self.tvp_switch_changed_on_amp,
            'tvp_valve': self.tvp_valve_changed_on_amp,
            'mod_switch': self.mod_switch_changed_on_amp,
            'delay_switch': self.delay_switch_changed_on_amp,
            'reverb_switch': self.reverb_switch_changed_on_amp,
            'mod_type': self.mod_type_changed_on_amp,
            'mod_segval': self.mod_segval_changed_on_amp,
            'mod_level': self.mod_level_changed_on_amp,
            'mod_speed': self.mod_speed_changed_on_amp,
            'mod_manual': self.mod_manual_changed_on_amp,
            'delay_type': self.delay_type_changed_on_amp,
            'delay_feedback': self.delay_feedback_changed_on_amp,
            'delay_level': self.delay_level_changed_on_amp,
            'delay_time': self.delay_time_changed_on_amp,
            'reverb_type': self.reverb_type_changed_on_amp,
            'reverb_size': self.reverb_size_changed_on_amp,
            'reverb_level': self.reverb_level_changed_on_amp,
            'fx_focus': self.fx_focus_changed_on_amp,
            'preset': self.preset_changed_on_amp,
            'manual_mode': self.manual_mode_changed_on_amp,
            'tuner_mode': self.tuner_mode_changed_on_amp,
            'tuner_note': self.tuner_note_changed_on_amp,
            'tuner_delta': self.tuner_delta_changed_on_amp,
            'resonance': self.resonance_changed_on_amp,
            'presence': self.presence_changed_on_amp,
            'master_volume': self.master_volume_changed_on_amp,
            'preset_name': self.preset_name_from_amp,
            'preset_settings': self.preset_settings_from_amp,
        }

        uif = os.path.join(os.path.split(__file__)[0], 'outsider.ui')
        logger.debug('loading GUI file: {0}'.format(uif))
        uic.loadUi(uif, self)

        self.amp_mutex = None
        self.amp = BlackstarIDAmp()
        self.watcher_thread = None

        # For now we don't do anything with preset settings
        # information other than store them in this list
        self.preset_settings = [None] * 128

        self.controls_enabled(False)
        self.show()

    def controls_enabled(self, bool):
        # Disable/Enable all widgets except the connect button (always enabled) and the master controls (always disabled)
        if bool is True:
            widgets = self.findChildren(QGroupBox)#(QObject)
            for w in widgets:
                if w == self.masterGroupBox:
                   pass
                elif w.objectName() == 'TVPGroupBox' and self.amp.model == 'id-core':
                    # self.TVPComboBox.setCurrentText('6L6')
                    # self.TVPRadioButton.setChecked(False)
                    # Don't enable as Core has  fixed TVP, probably with type 6L6
                    pass
                else:
                    w.setEnabled(bool)

        elif bool is False:
            widgets = self.findChildren(QGroupBox)#(QObject)
            for w in widgets:
                w.setEnabled(bool)

            widgets = self.findChildren(QSlider)
            for w in widgets:
                w.blockSignals(True)
                w.setValue(0)
                w.blockSignals(False)

            widgets = self.findChildren(QLCDNumber)
            for w in widgets:
                w.blockSignals(True) # Not nescessary
                w.display(0)
                w.blockSignals(False) # Not nescessary

            widgets = self.findChildren(QRadioButton)
            for w in widgets:
                w.blockSignals(True)
                w.setChecked(False)
                w.blockSignals(False)

    def connect(self):
        try:
            self.amp.connect()
            self.amp.drain()
            self.start_amp_watcher_thread()
            self.amp.startup()
            self.amp.get_all_preset_names()
        except NotConnectedError:
            raise

    def disconnect(self):
        if self.watcher_thread is not None:
            logger.debug('Closing down amplifier watching thread')
            self.shutdown_threads.emit()
            self.watcher_thread.quit()
            self.watcher_thread.wait()
            logger.debug('Amplifier watching thread finished')

        if self.amp.connected is True:
            self.amp.disconnect()

    def start_amp_watcher_thread(self):
        # Set up thread to watch for manual changes of the amp
        # controls at the amp (rather than gui) so we can update the
        # gui controls as needed. The way this is done is inspired
        # by:
        # http://stackoverflow.com/questions/29243692/pyqt5-how-to-make-qthread-return-data-to-main-thread
        # https://mayaposch.wordpress.com/2011/11/01/how-to-really-truly-use-qthreads-the-full-explanation/
        self.watcher_thread = QThread()
        self.amp_mutex = QMutex()
        self.watcher = AmpControlWatcher(self.amp, self.amp_mutex)
        self.watcher.have_data.connect(self.new_data_from_amp)
        self.shutdown_threads.connect(self.watcher.stop_watching)
        self.watcher.moveToThread(self.watcher_thread)
        self.watcher_thread.started.connect(self.watcher.work)

        self.watcher_thread.start()

    def closeEvent(self, event):
        # Ran when the application is closed.
        self.disconnect()
        super(Ui, self).close()
        logger.debug('Exiting')

    @pyqtSlot(dict)
    def new_data_from_amp(self, settings):
        for control, value in settings.items():
            try:
                self.response_funcs[control](value)
            except KeyError:
                logger.error('Unrecognized control {0}'.format(control))

    ######################################################################
    # The following methods are called when data is received from the amp
    ######################################################################
    def voice_changed_on_amp(self, value):
        self.voiceComboBox.blockSignals(True)
        self.voiceComboBox.setCurrentIndex(value)
        self.voiceComboBox.blockSignals(False)

    def gain_changed_on_amp(self, value):
        self.gainSlider.blockSignals(True)
        self.gainSlider.setValue(value)
        self.gainLcdNumber.display(value)
        self.gainSlider.blockSignals(False)

    def volume_changed_on_amp(self, value):
        self.volumeSlider.blockSignals(True)
        self.volumeSlider.setValue(value)
        self.volumeLcdNumber.display(value)
        self.volumeSlider.blockSignals(False)

    def bass_changed_on_amp(self, value):
        self.bassSlider.blockSignals(True)
        self.bassSlider.setValue(value)
        self.bassLcdNumber.display(value)
        self.bassSlider.blockSignals(False)

    def middle_changed_on_amp(self, value):
        self.middleSlider.blockSignals(True)
        self.middleSlider.setValue(value)
        self.middleLcdNumber.display(value)
        self.middleSlider.blockSignals(False)

    def treble_changed_on_amp(self, value):
        self.trebleSlider.blockSignals(True)
        self.trebleSlider.setValue(value)
        self.trebleLcdNumber.display(value)
        self.trebleSlider.blockSignals(False)

    def isf_changed_on_amp(self, value):
        self.isfSlider.blockSignals(True)
        self.isfSlider.setValue(value)
        self.isfLcdNumber.display(value)
        self.isfSlider.blockSignals(False)

    def tvp_switch_changed_on_amp(self, value):
        value = bool(value)
        self.TVPRadioButton.blockSignals(True)
        self.TVPRadioButton.setChecked(value)
        self.TVPComboBox.setEnabled(value)
        self.TVPRadioButton.blockSignals(False)

    def tvp_valve_changed_on_amp(self, value):
        self.TVPComboBox.blockSignals(True)
        self.TVPComboBox.setCurrentIndex(value)
        self.TVPComboBox.blockSignals(False)

    def mod_switch_changed_on_amp(self, value):
        value = bool(value)
        self.modRadioButton.blockSignals(True)
        self.modRadioButton.setChecked(value)
        self.modComboBox.setEnabled(value)
        self.modSegValSlider.setEnabled(value)
        self.modSegValLabel.setEnabled(value)
        self.modSegValLcdNumber.setEnabled(value)
        self.modSpeedSlider.setEnabled(value)
        self.modSpeedLabel.setEnabled(value)
        self.modSpeedLcdNumber.setEnabled(value)
        self.modLevelSlider.setEnabled(value)
        self.modLevelLabel.setEnabled(value)
        self.modLevelLcdNumber.setEnabled(value)
        self.modRadioButton.blockSignals(False)
        self.assess_manual_enabled()

    def delay_switch_changed_on_amp(self, value):
        value = bool(value)
        self.delayRadioButton.blockSignals(True)
        self.delayRadioButton.setChecked(value)
        self.delayComboBox.setEnabled(value)
        self.delayFeedbackSlider.setEnabled(value)
        self.delayFeedbackLabel.setEnabled(value)
        self.delayFeedbackLcdNumber.setEnabled(value)
        self.delayTimeSlider.setEnabled(value)
        self.delayTimeLabel.setEnabled(value)
        self.delayTimeLcdNumber.setEnabled(value)
        self.delayLevelSlider.setEnabled(value)
        self.delayLevelLabel.setEnabled(value)
        self.delayLevelLcdNumber.setEnabled(value)
        self.delayRadioButton.blockSignals(False)

    def reverb_switch_changed_on_amp(self, value):
        value = bool(value)
        self.reverbRadioButton.blockSignals(True)
        self.reverbRadioButton.setChecked(value)
        self.reverbComboBox.setEnabled(value)
        self.reverbSizeSlider.setEnabled(value)
        self.reverbSizeLabel.setEnabled(value)
        self.reverbSizeLcdNumber.setEnabled(value)
        self.reverbLevelSlider.setEnabled(value)
        self.reverbLevelLabel.setEnabled(value)
        self.reverbLevelLcdNumber.setEnabled(value)
        self.reverbRadioButton.blockSignals(False)

    def mod_type_changed_on_amp(self, value):
        self.modComboBox.blockSignals(True)
        self.modComboBox.setCurrentIndex(value)
        self.modComboBox.blockSignals(False)
        self.mod_type_changed(value)

    def mod_segval_changed_on_amp(self, value):
        self.modSegValSlider.blockSignals(True)
        self.modSegValSlider.setValue(value)
        self.modSegValLcdNumber.display(value)
        self.modSegValSlider.blockSignals(False)

    def mod_level_changed_on_amp(self, value):
        self.modLevelSlider.blockSignals(True)
        self.modLevelSlider.setValue(value)
        self.modLevelLcdNumber.display(value)
        self.modLevelSlider.blockSignals(False)

    def mod_speed_changed_on_amp(self, value):
        self.modSpeedSlider.blockSignals(True)
        self.modSpeedSlider.setValue(value)
        self.modSpeedLcdNumber.display(value)
        self.modSpeedSlider.blockSignals(False)

    def mod_manual_changed_on_amp(self, value):
        self.modManualSlider.blockSignals(True)
        self.modManualSlider.setValue(value)
        self.modManualLcdNumber.display(value)
        self.modManualSlider.blockSignals(False)
        
    def delay_type_changed_on_amp(self, value):
        self.delayComboBox.blockSignals(True)
        self.delayComboBox.setCurrentIndex(value)
        self.delayComboBox.blockSignals(False)

    def delay_feedback_changed_on_amp(self, value):
        self.delayFeedbackSlider.blockSignals(True)
        self.delayFeedbackSlider.setValue(value)
        self.delayFeedbackLcdNumber.display(value)
        self.delayFeedbackSlider.blockSignals(False)

    def delay_level_changed_on_amp(self, value):
        self.delayLevelSlider.blockSignals(True)
        self.delayLevelSlider.setValue(value)
        self.delayLevelLcdNumber.display(value)
        self.delayLevelSlider.blockSignals(False)

    def delay_time_changed_on_amp(self, value):
        self.delayTimeSlider.blockSignals(True)
        self.delayTimeSlider.setValue(value)
        self.delayTimeLcdNumber.display(value)
        self.delayTimeSlider.blockSignals(False)

    def reverb_type_changed_on_amp(self, value):
        self.reverbComboBox.blockSignals(True)
        self.reverbComboBox.setCurrentIndex(value)
        self.reverbComboBox.blockSignals(False)

    def reverb_size_changed_on_amp(self, value):
        self.reverbSizeSlider.blockSignals(True)
        self.reverbSizeSlider.setValue(value)
        self.reverbSizeLcdNumber.display(value)
        self.reverbSizeSlider.blockSignals(False)

    def reverb_level_changed_on_amp(self, value):
        self.reverbLevelSlider.blockSignals(True)
        self.reverbLevelSlider.setValue(value)
        self.reverbLevelLcdNumber.display(value)
        self.reverbLevelSlider.blockSignals(False)

    def fx_focus_changed_on_amp(self, value):
        # This is a bit of a misnomer, as the amp doesn't emit data if
        # the user changes the effect focus on the amp. However, when
        # the user disables an effect in the GUI, if that effect had
        # focus, we want to move the focus to another effect, if one
        # is enabled. The way we do that, is in the slots associated
        # with toggling an effect (see below), if the effect is
        # disabled we issue a packet to query the state of all
        # controls, which gives us an opportunity to react in this
        # function here.
        if value == 1 and not self.modRadioButton.isChecked():
            if self.delayRadioButton.isChecked():
                self.amp.set_control('fx_focus', 2)
            elif self.reverbRadioButton.isChecked():
                self.amp.set_control('fx_focus', 3)

        elif value == 2 and not self.delayRadioButton.isChecked():
            if self.reverbRadioButton.isChecked():
                self.amp.set_control('fx_focus', 3)
            elif self.modRadioButton.isChecked():
                self.amp.set_control('fx_focus', 1)

        if value == 3 and not self.reverbRadioButton.isChecked():
            if self.modRadioButton.isChecked():
                self.amp.set_control('fx_focus', 1)
            elif self.delayRadioButton.isChecked():
                self.amp.set_control('fx_focus', 2)

    def preset_name_from_amp(self, namelist):
        idx = namelist[0] - 1 # Presets are numbered from 1
        name = str(namelist[0]) + '. ' + namelist[1]
        item = self.presetNamesList.item(idx)
        if item is None:
            self.presetNamesList.insertItem(idx, name)
        else:
            item.setText(name)

    def preset_settings_from_amp(self, settings):
        self.preset_settings[settings.preset_number] = settings

    def preset_changed_on_amp(self, value):
        # TODO: This function is a stub for now, but will need hooking
        # up to a combo box widget in the gui for selecting/indicating
        # preset
        logger.debug('preset changed on amp: {0}'.format(value))
        self.presetNamesList.setCurrentRow(value - 1)

    def manual_mode_changed_on_amp(self, value):
        # TODO: This also needs hooking up to the preset combo box in
        # the GUI
        if value == 1:
            # Here we'd need to set the combo box to "Manual"
            pass
        else:
            # We're no longer in manual mode, but since in this case
            # another packet will indicate the preset selected,
            # there's no need to do anything here.
            pass
        logger.debug('manual_mode changed on amp: {0}'.format(value))

    def tuner_mode_changed_on_amp(self, value):
        # TODO: Stub for now - needs hooking into a suitable tuner widget
        logger.debug('tuner_mode changed on amp: {0}'.format(value))

    def tuner_note_changed_on_amp(self, value):
        # TODO: Stub for now - needs hooking into a suitable tuner widget
        logger.debug('tuner_note changed on amp: {0}'.format(value))

    def tuner_delta_changed_on_amp(self, value):
        # TODO: Stub for now - needs hooking into a suitable tuner widget
        logger.debug('tuner_delta changed on amp: {0}'.format(value))

    def resonance_changed_on_amp(self, value):
        self.resonanceSlider.blockSignals(True)
        self.resonanceSlider.setValue(value)
        self.resonanceLcdNumber.display(value)
        self.resonanceSlider.blockSignals(False)
        logger.debug('resonance changed on amp: {0}'.format(value))

    def presence_changed_on_amp(self, value):
        self.presenceSlider.blockSignals(True)
        self.presenceSlider.setValue(value)
        self.presenceLcdNumber.display(value)
        self.presenceSlider.blockSignals(False)
        logger.debug('presence changed on amp: {0}'.format(value))

    def master_volume_changed_on_amp(self, value):
        self.masterVolumeSlider.blockSignals(True)
        self.masterVolumeSlider.setValue(value)
        self.masterVolumeLcdNumber.display(value)
        self.masterVolumeSlider.blockSignals(False)
        logger.debug('master_volume changed on amp: {0}'.format(value))

    ##################################################################
    # The following methods are the slots for changes made on the gui
    ##################################################################
    @pyqtSlot()
    def on_connectToAmpButton_clicked(self):
        if self.amp.connected is False:
            try:
                self.connect()
                # Enable widgets
                self.controls_enabled(True)
                self.connectToAmpButton.setText('Disconnect Amp')

            except NotConnectedError:
                QMessageBox.information(self,'Outsider', 'No amplifier found')
        else:
            self.disconnect()
            self.controls_enabled(False)
            self.connectToAmpButton.setText('Connect to Amp')

    @pyqtSlot(int)
    def on_volumeSlider_valueChanged(self, value):
        logger.debug('Volume slider: {0}'.format(value))
        self.amp.set_control('volume', value)

    @pyqtSlot(int)
    def on_gainSlider_valueChanged(self, value):
        logger.debug('Gain slider: {0}'.format(value))
        self.amp.set_control('gain', value)

    @pyqtSlot(int)
    def on_bassSlider_valueChanged(self, value):
        logger.debug('Bass slider: {0}'.format(value))
        self.amp.set_control('bass', value)

    @pyqtSlot(int)
    def on_middleSlider_valueChanged(self, value):
        logger.debug('Middle slider: {0}'.format(value))
        self.amp.set_control('middle', value)

    @pyqtSlot(int)
    def on_trebleSlider_valueChanged(self, value):
        logger.debug('Treble slider: {0}'.format(value))
        self.amp.set_control('treble', value)

    @pyqtSlot(int)
    def on_isfSlider_valueChanged(self, value):
        logger.debug('ISF slider: {0}'.format(value))
        self.amp.set_control('isf', value)

    @pyqtSlot(int)
    def on_TVPComboBox_currentIndexChanged(self, idx):
        logger.debug('TVP selection: {0}'.format(idx))
        self.amp.set_control('tvp_valve', idx)

    @pyqtSlot(bool)
    def on_TVPRadioButton_toggled(self, state):
        logger.debug('TVP switch: {0}'.format(state))
        if state == True:
            self.amp.set_control('tvp_switch', 1)
        else:
            self.amp.set_control('tvp_switch', 0)

    @pyqtSlot(int)
    def on_voiceComboBox_currentIndexChanged(self, idx):
        logger.debug('Voice selection: {0}'.format(idx))
        self.amp.set_control('voice', idx)

    @pyqtSlot(bool)
    def on_modRadioButton_toggled(self, state):
        logger.debug('Mod switch: {0}'.format(state))
        self.amp.set_control('mod_switch', state)
        if state == 1:
            self.amp.set_control('fx_focus', 1)
        else:
            # Find out if the mod effect had focus before being
            # deactivated and shift focus to another effect if
            # possible. The only mechanism we have available to do
            # this is to get the status of all controls, sadly.
            self.amp.startup()
        self.assess_manual_enabled()

    @pyqtSlot(int)
    def on_modComboBox_currentIndexChanged(self, value):
        logger.debug('Mod Combo Box: {0}'.format(value))
        self.amp.set_control('mod_type', value)
        self.amp.set_control('fx_focus', 1)

    @pyqtSlot(int)
    def on_modSegValSlider_valueChanged(self, value):
        logger.debug('Mod SegVal slider: {0}'.format(value))
        self.amp.set_control('mod_segval', value)
        self.amp.set_control('fx_focus', 1)

    @pyqtSlot(int)
    def on_modLevelSlider_valueChanged(self, value):
        logger.debug('Mod Level slider: {0}'.format(value))
        self.amp.set_control('mod_level', value)
        self.amp.set_control('fx_focus', 1)

    @pyqtSlot(int)
    def on_modSpeedSlider_valueChanged(self, value):
        logger.debug('Mod Speed slider: {0}'.format(value))
        self.amp.set_control('mod_speed', value)
        self.amp.set_control('fx_focus', 1)

    @pyqtSlot(int)
    def on_modManualSlider_valueChanged(self, value):
        logger.debug('Mod Manual slider: {0}'.format(value))
        self.amp.set_control('mod_manual', value)
        self.amp.set_control('fx_focus', 1)

    @pyqtSlot(bool)
    def on_delayRadioButton_toggled(self, state):
        logger.debug('Delay switch: {0}'.format(state))
        self.amp.set_control('delay_switch', state)
        if state == 1:
            self.amp.set_control('fx_focus', 2)
        else:
            # Find out if the mod effect had focus before being
            # deactivated and shift focus to another effect if
            # possible. The only mechanism we have available to do
            # this is to get the status of all controls, sadly.
            self.amp.startup()

    @pyqtSlot(int)
    def on_delayComboBox_currentIndexChanged(self, value):
        logger.debug('Delay Combo Box: {0}'.format(value))
        self.amp.set_control('delay_type', value)
        self.amp.set_control('fx_focus', 2)

    @pyqtSlot(int)
    def on_delayFeedbackSlider_valueChanged(self, value):
        logger.debug('Delay feedback slider: {0}'.format(value))
        self.amp.set_control('delay_feedback', value)
        self.amp.set_control('fx_focus', 2)

    @pyqtSlot(int)
    def on_delayLevelSlider_valueChanged(self, value):
        logger.debug('Delay Level slider: {0}'.format(value))
        self.amp.set_control('delay_level', value)
        self.amp.set_control('fx_focus', 2)

    @pyqtSlot(int)
    def on_delayTimeSlider_valueChanged(self, value):
        logger.debug('Delay Time slider: {0}'.format(value))
        self.amp.set_control('delay_time', value)
        self.amp.set_control('fx_focus', 2)

    @pyqtSlot(bool)
    def on_reverbRadioButton_toggled(self, state):
        logger.debug('Reverb switch: {0}'.format(state))
        self.amp.set_control('reverb_switch', state)
        if state == 1:
            self.amp.set_control('fx_focus', 3)
        else:
            # Find out if the mod effect had focus before being
            # deactivated and shift focus to another effect if
            # possible. The only mechanism we have available to do
            # this is to get the status of all controls, sadly.
            self.amp.startup()

    @pyqtSlot(int)
    def on_reverbComboBox_currentIndexChanged(self, value):
        logger.debug('Reverb Combo Box: {0}'.format(value))
        self.amp.set_control('reverb_type', value)
        self.amp.set_control('fx_focus', 3)

    @pyqtSlot(int)
    def on_reverbSizeSlider_valueChanged(self, value):
        logger.debug('Reverb Size slider: {0}'.format(value))
        self.amp.set_control('reverb_size', value)
        self.amp.set_control('fx_focus', 3)

    @pyqtSlot(int)
    def on_reverbLevelSlider_valueChanged(self, value):
        logger.debug('Reverb Level slider: {0}'.format(value))
        self.amp.set_control('reverb_level', value)
        self.amp.set_control('fx_focus', 3)

    @pyqtSlot(QListWidgetItem)
    def on_presetNamesList_itemDoubleClicked(self, item):
        idx = self.presetNamesList.currentRow()
        preset = idx + 1 # Presets are numbered from 1
        self.amp.select_preset(preset)

    @pyqtSlot()
    def on_renamePresetPushButton_clicked(self):
        idx = self.presetNamesList.currentRow()
        preset = idx + 1 # Presets are numbered from 1

        name, ok = QInputDialog.getText(
            self, 'Input Dialog',
            'Enter new name for preset {0}:'.format(preset)
        )
        if ok == True:
            # We need to grab the amp mutex to stop the amp watcher
            # thread from consuming the packets emitted by the amp in
            # the preset rename process
            self.amp_mutex.lock()
            self.amp.set_preset_name(preset, name)
            self.amp_mutex.unlock()

    # When the modulation type is changed, we want to change the label
    # associated with the segment value control. So, we need to define
    # a slot to trigger when the modulation type is changed, and a
    # signal to raise to communicate back to the UI what the label
    # should be changed to. The signal has argument type str, which is
    # automatically converted to type QString.
    mod_segval_label_update = pyqtSignal(str)

    @pyqtSlot(int)
    def mod_type_changed(self, value):
        if value == 0:
            self.mod_segval_label_update.emit('Mix')
        elif value == 1:
            self.mod_segval_label_update.emit('Feedback')
        elif value == 2:
            self.mod_segval_label_update.emit('Mix')
        elif value == 3:
            self.mod_segval_label_update.emit('FreqMod')
        self.assess_manual_enabled()

    @pyqtSlot(int)
    def on_resonanceSlider_valueChanged(self, value):
        logger.debug('Resonance slider: {0}'.format(value))
        self.amp.set_control('resonance', value)

    @pyqtSlot(int)
    def on_presenceSlider_valueChanged(self, value):
        logger.debug('Presence slider: {0}'.format(value))
        self.amp.set_control('presence', value)

    @pyqtSlot(int)
    def on_masterVolumeSlider_valueChanged(self, value):
        logger.debug('Master volume slider: {0}'.format(value))
        self.amp.set_control('master_volume', value)


    # When the modulation is enabled and the modution type is flanger, enable
    # the manual control.
    def assess_manual_enabled(self):
        value = self.modRadioButton.isChecked() and self.modComboBox.currentIndex() == 1
        self.modManualSlider.blockSignals(True)
        self.modManualSlider.setEnabled(value)
        self.modManualSlider.blockSignals(False)