Пример #1
0
class MainWindow(QtGui.QMainWindow):    
    
    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)
        self.midiOutputDevicesDict = {}
        self.midiInputDevicesDict = {}
        self.onValue = 64
        self.offValue = 0
        #control change values
        self.dialDict = {'resonanceDial': 48, 'cutoffDial': 49, 'lfoRateDial': 50, 'lfoDepthDial': 51,
        'envAmountDial': 52, 'glideAmountDial' : 53, 'oscPWMDial': 54, 'oscDetuneDial' :55,
        'filterDecaySlider': 58, 'filterAttackSlider' :59, 'ampDecaySlider' :60, 'ampAttackSlider': 61}
        
        self.buttonDict = {'oscFMOn':[65, self.onValue], 'oscFMOff':[65, self.offValue], 'lfoRandomOn':[66, self.onValue],
                      'lfoRandomOff':[66, self.offValue], 'lfoSquareOn':[67, self.onValue], 
                      'lfoTriangleOn':[67, self.offValue], 'lpOn':[68, self.offValue],'hpOn':[68, self.onValue],
                      'distortionOn':[69, self.onValue], 'distortionOff':[69, self.offValue], 
                      'lfoOn':[70, self.onValue], 'lfoOff':[70, self.offValue], 'lfoOscOn':[71, self.onValue],
                      'lfoFilterOn':[71, self.offValue], 'antiAliasOn':[72, self.onValue], 'antiAliasOff':[72, self.offValue],
                      'oscBOctaveOn':[73, self.onValue], 'oscBOctaveOff':[73, self.offValue],'oscBOn':[74, self.onValue], 
                      'oscBOff':[74, self.offValue], 'oscBWaveSquare':[75, self.onValue], 'oscBWaveTri':[75, self.offValue], 
                      'envSustainOn':[76, self.onValue], 'envSustainOff':[76, self.offValue], 'oscANoiseOn':[77, self.onValue], 
                      'oscANoiseOff':[77, self.offValue],'pwmSweepOn':[78, self.onValue], 'pwmSweepOff':[78, self.offValue], 
                      'oscAPWMOn':[79, self.onValue], 'oscASawOn':[79, self.offValue]}        
        
        self.buttonBoxDict = {'oscAWaveBox':'oscAWaveGroup', 'oscANoiseBox':'oscANoiseGroup', 'oscBEnableBox':'oscBEnableGroup', 
                                'oscBWaveBox':'oscBWaveGroup', 'oscBOctaveBox':'oscBOctaveGroup', 'oscFMBox':'oscFMGroup',
                                'lfoEnableBox':'lfoEnableGroup', 'lfoDestBox':'lfoDestGroup', 'lfoWaveBox':'lfoWaveGroup', 
                                'lfoRandomBox':'lfoRandomGroup', 'filterModeBox':'filterModeGroup', 'distortionBox':'distortionGroup',
                                'antiAliasBox':'antiAliasGroup', 'envSustainBox':'envSustainGroup', 'pwmSweepBox':'pwmSweepGroup', 
                                'oscAWaveBox':'oscAWaveGroup'}
        
        self.ui = Ui_MainWindow()        
        self.ui.setupUi(self)
        self.windowHandler = MainWindowHandler(self.dialDict, self.buttonDict) 
        self.settings = QtCore.QSettings('MeeblipControl', 'MeeblipControl')        
        #connect signals and slots
        for dial, cc, in self.dialDict.iteritems():
            currentDial = getattr(self.ui, dial)
            currentDial.setRange(0,127)
            dialChanged = partial(self.windowHandler.dialChanged, mainWindowInstance=self, cc=cc)
            currentDial.valueChanged.connect(dialChanged)
            currentDial.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
            currentDial.customContextMenuRequested.connect(partial(self.windowHandler.contextMenu, mainWindowInstance=self, widgetName=dial))
        for button, buttonList in self.buttonDict.iteritems():
            currentButton = getattr(self.ui, button)
            buttonFunc = partial(self.windowHandler.buttonChanged, mainWindowInstance=self, value=buttonList[1], cc=buttonList[0], button=currentButton)
            currentButton.toggled.connect(buttonFunc)
        for buttonGroupBox in self.buttonBoxDict.keys():
            currentGroup = getattr(self.ui, buttonGroupBox)
            currentGroup.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
            currentGroup.customContextMenuRequested.connect(partial(self.windowHandler.contextMenu, mainWindowInstance=self, 
                                                                    widgetName=buttonGroupBox))
        
        class _MidiInput(QtCore.QThread):
            dataReceivedSignal = QtCore.pyqtSignal(int, int)
            midiExceptionSignal = QtCore.pyqtSignal(str)
            
            def __init__(self, mainWindow, mainWindowHandler, parent=None):
                super(_MidiInput, self).__init__(parent)
                self.mainWindow = mainWindow
                self.mainWindowHandler = mainWindowHandler
                
            def run(self):
                while True:
                    if self.mainWindowHandler.midiSelectedInputDevicesDict:
                        try:
                            self.mainWindowHandler.midiInputMutex.lock()
                            for inputDevice in self.mainWindowHandler.midiSelectedInputDevicesDict.values():
                                        if inputDevice.poll():
                                            data = inputDevice.read(1)
                                            channel = (data[0][0][0] & 0xF) + 1
                                            if channel == self.mainWindowHandler.midiInputChannel:
                                                status = data[0][0][0] & 0xF0
                                                cc = data[0][0][1]
                                                #if a CC message arrives and is mapped
                                                if status == 0xB0 and cc in self.mainWindowHandler.currentPatch.patchMIDIMapDict:  
                                                    value = data[0][0][2]
                                                    self.dataReceivedSignal.emit(cc, value)
                                                else:
                                                    if self.mainWindowHandler.midiSelectedOutputDevice:
                                                        self.mainWindowHandler.midiSelectedOutputDevice.write(data)                    
                        except midi.MidiException as e:
                            self.midiExceptionSignal.emit(unicode(e))
                        finally:
                            self.mainWindowHandler.midiInputMutex.unlock()
                    self.usleep(200) #don't hog the processor in the polling loop!
        #initialize MIDI, start listening for incoming MIDI data       
        midi.init()
        self.getMIDIDevices()
        self.midiInputThread = _MidiInput(self, self.windowHandler)
        self.midiInputThread.dataReceivedSignal.connect(self.midiInputCallback)         
        self.midiInputThread.midiExceptionSignal.connect(lambda e: QtGui.QMessageBox.warning(self, "MIDI Error", unicode(e)))
        self.midiInputThread.start()
        self.ui.action_Save.setEnabled(False)
        self.restoreSettings(self.windowHandler)
        self.windowHandler.new(self)
        
    def midiInputCallback(self, cc, value):        
        widgetName = self.windowHandler.currentPatch.patchMIDIMapDict[cc]
        if widgetName in self.dialDict:
            getattr(self.ui, widgetName).setValue(value)
            self.windowHandler.dialChanged(value, self, self.dialDict[widgetName])
        elif widgetName in self.buttonBoxDict:
            buttonGroup = getattr(self.ui, self.buttonBoxDict[widgetName])
            checkedButton = buttonGroup.checkedButton()
            checkedButtonName = str(checkedButton.objectName())
            if value >= self.onValue and self.buttonDict[checkedButtonName][1] == self.offValue:
                for button in buttonGroup.buttons():
                    if button != checkedButton:
                        button.toggle()
            elif value < self.onValue and self.buttonDict[checkedButtonName][1] == self.onValue:
                for button in buttonGroup.buttons():
                    if button != checkedButton:
                        button.toggle()

    def getMIDIDevices(self):
        midiOutputDevices = []
        midiInputDevices = []
        for index in xrange(0, midi.get_count()):
            device = midi.get_device_info(index)
            deviceName = device[1]
            if device[3] == 1 and device[4] == 0: #if the device is an output and not opened
                setattr(self, deviceName, QtGui.QAction(QtGui.QIcon(''), deviceName, self))
                deviceWidget = getattr(self, deviceName)
                deviceWidget.setCheckable(True)
                midiOutputDevices.append(deviceWidget)
                self.midiOutputDevicesDict[deviceWidget] = index
            elif device[2] == 1 and device[4] == 0: #if devices is an input and not opened
                deviceName = device[1]
                setattr(self, deviceName, QtGui.QAction(QtGui.QIcon(''), deviceName, self))
                deviceWidget = getattr(self, deviceName)
                deviceWidget.setCheckable(True)
                midiInputDevices.append(deviceWidget)
                self.midiInputDevicesDict[deviceWidget] = index
                
        if midiOutputDevices:
            self.ui.midiOutputDevicesMenu = self.ui.menubar.addMenu("&Midi Output Device")
            self.ui.midiOutputDevicesMenu.addActions(midiOutputDevices)
        if midiInputDevices:
            self.ui.midiInputDevicesMenu = self.ui.menubar.addMenu("&Midi Input Devices")
            self.ui.midiInputDevicesMenu.addActions(midiInputDevices)
        
        for device in midiOutputDevices:
            outputFunction = partial(self.windowHandler.midiOutputSelect, mainWindowInstance=self, device=device)
            device.triggered.connect(outputFunction)

        for device in midiInputDevices:
            inputFunction = partial(self.windowHandler.midiInputSelect, mainWindowInstance=self, device=device)
            device.triggered.connect(inputFunction)
    
    def restoreSettings(self, mainWindowHandler):
        mainWindowHandler.midiInputChannel = self.settings.value('midiInputChannel').toInt()[0]
        if not mainWindowHandler.midiInputChannel:
            self.midiInputChannel = 1
        self.midiOutputChannel = self.settings.value('midiOutputChannel').toInt()[0]
        if not mainWindowHandler.midiOutputChannel:
            mainWindowHandler.midiOutputChannel = 1
        
        registryInputDeviceList = []    
        for inputDeviceHash in self.settings.value('midiInputDevices', []).toList():
            inputDeviceHash = str(inputDeviceHash.toString())
            for deviceWidget, index in self.midiInputDevicesDict.iteritems():
                deviceName = midi.get_device_info(index)[1]
                deviceHash = hashlib.md5(deviceName).hexdigest()
                if deviceHash == inputDeviceHash:
                    deviceWidget.setChecked(True)
                    self.windowHandler.midiInputSelect(self, deviceWidget)
                    registryInputDeviceList.append(deviceHash)            
        self.settings.setValue('midiInputDevices', registryInputDeviceList) 
        #update the registry so unplugged devices aren't 
        #reselected when plugged back in at some later time
        outputDeviceHash = str(self.settings.value('midiOutputDevice').toString())
        registryOutputDevice = None
        for deviceWidget, index in self.midiOutputDevicesDict.iteritems():
                deviceName = midi.get_device_info(index)[1]
                deviceHash = hashlib.md5(deviceName).hexdigest()
                if deviceHash == outputDeviceHash:
                    deviceWidget.setChecked(True)
                    self.windowHandler.midiOutputSelect(self, deviceWidget)
                    registryOutputDevice = deviceHash
        self.settings.setValue('midiOutputDevice', registryOutputDevice)
            
    @QtCore.pyqtSignature("")
    def on_action_MIDI_Channel_triggered(self):
        self.windowHandler.menuOptions(self)
        
    @QtCore.pyqtSignature("")
    def on_action_Save_as_triggered(self):
        self.windowHandler.saveAs(self)
    
    @QtCore.pyqtSignature("")
    def on_action_Load_triggered(self):
        self.windowHandler.load(self)
        
    @QtCore.pyqtSignature("")
    def on_action_Save_triggered(self):
        self.windowHandler.save(self)
            
    @QtCore.pyqtSignature("")
    def on_action_New_triggered(self):
        self.windowHandler.new(self)
    
    @QtCore.pyqtSignature("")
    def on_action_Export_patch_as_MIDI_triggered(self):
        self.windowHandler.midiExport(self)
    
    @QtCore.pyqtSignature("")
    def on_action_Import_MIDI_patch_triggered(self):
        self.windowHandler.midiImport(self)
Пример #2
0
 def __init__(self, parent=None):
     super(MainWindow, self).__init__(parent)
     self.midiOutputDevicesDict = {}
     self.midiInputDevicesDict = {}
     self.onValue = 64
     self.offValue = 0
     #control change values
     self.dialDict = {'resonanceDial': 48, 'cutoffDial': 49, 'lfoRateDial': 50, 'lfoDepthDial': 51,
     'envAmountDial': 52, 'glideAmountDial' : 53, 'oscPWMDial': 54, 'oscDetuneDial' :55,
     'filterDecaySlider': 58, 'filterAttackSlider' :59, 'ampDecaySlider' :60, 'ampAttackSlider': 61}
     
     self.buttonDict = {'oscFMOn':[65, self.onValue], 'oscFMOff':[65, self.offValue], 'lfoRandomOn':[66, self.onValue],
                   'lfoRandomOff':[66, self.offValue], 'lfoSquareOn':[67, self.onValue], 
                   'lfoTriangleOn':[67, self.offValue], 'lpOn':[68, self.offValue],'hpOn':[68, self.onValue],
                   'distortionOn':[69, self.onValue], 'distortionOff':[69, self.offValue], 
                   'lfoOn':[70, self.onValue], 'lfoOff':[70, self.offValue], 'lfoOscOn':[71, self.onValue],
                   'lfoFilterOn':[71, self.offValue], 'antiAliasOn':[72, self.onValue], 'antiAliasOff':[72, self.offValue],
                   'oscBOctaveOn':[73, self.onValue], 'oscBOctaveOff':[73, self.offValue],'oscBOn':[74, self.onValue], 
                   'oscBOff':[74, self.offValue], 'oscBWaveSquare':[75, self.onValue], 'oscBWaveTri':[75, self.offValue], 
                   'envSustainOn':[76, self.onValue], 'envSustainOff':[76, self.offValue], 'oscANoiseOn':[77, self.onValue], 
                   'oscANoiseOff':[77, self.offValue],'pwmSweepOn':[78, self.onValue], 'pwmSweepOff':[78, self.offValue], 
                   'oscAPWMOn':[79, self.onValue], 'oscASawOn':[79, self.offValue]}        
     
     self.buttonBoxDict = {'oscAWaveBox':'oscAWaveGroup', 'oscANoiseBox':'oscANoiseGroup', 'oscBEnableBox':'oscBEnableGroup', 
                             'oscBWaveBox':'oscBWaveGroup', 'oscBOctaveBox':'oscBOctaveGroup', 'oscFMBox':'oscFMGroup',
                             'lfoEnableBox':'lfoEnableGroup', 'lfoDestBox':'lfoDestGroup', 'lfoWaveBox':'lfoWaveGroup', 
                             'lfoRandomBox':'lfoRandomGroup', 'filterModeBox':'filterModeGroup', 'distortionBox':'distortionGroup',
                             'antiAliasBox':'antiAliasGroup', 'envSustainBox':'envSustainGroup', 'pwmSweepBox':'pwmSweepGroup', 
                             'oscAWaveBox':'oscAWaveGroup'}
     
     self.ui = Ui_MainWindow()        
     self.ui.setupUi(self)
     self.windowHandler = MainWindowHandler(self.dialDict, self.buttonDict) 
     self.settings = QtCore.QSettings('MeeblipControl', 'MeeblipControl')        
     #connect signals and slots
     for dial, cc, in self.dialDict.iteritems():
         currentDial = getattr(self.ui, dial)
         currentDial.setRange(0,127)
         dialChanged = partial(self.windowHandler.dialChanged, mainWindowInstance=self, cc=cc)
         currentDial.valueChanged.connect(dialChanged)
         currentDial.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
         currentDial.customContextMenuRequested.connect(partial(self.windowHandler.contextMenu, mainWindowInstance=self, widgetName=dial))
     for button, buttonList in self.buttonDict.iteritems():
         currentButton = getattr(self.ui, button)
         buttonFunc = partial(self.windowHandler.buttonChanged, mainWindowInstance=self, value=buttonList[1], cc=buttonList[0], button=currentButton)
         currentButton.toggled.connect(buttonFunc)
     for buttonGroupBox in self.buttonBoxDict.keys():
         currentGroup = getattr(self.ui, buttonGroupBox)
         currentGroup.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
         currentGroup.customContextMenuRequested.connect(partial(self.windowHandler.contextMenu, mainWindowInstance=self, 
                                                                 widgetName=buttonGroupBox))
     
     class _MidiInput(QtCore.QThread):
         dataReceivedSignal = QtCore.pyqtSignal(int, int)
         midiExceptionSignal = QtCore.pyqtSignal(str)
         
         def __init__(self, mainWindow, mainWindowHandler, parent=None):
             super(_MidiInput, self).__init__(parent)
             self.mainWindow = mainWindow
             self.mainWindowHandler = mainWindowHandler
             
         def run(self):
             while True:
                 if self.mainWindowHandler.midiSelectedInputDevicesDict:
                     try:
                         self.mainWindowHandler.midiInputMutex.lock()
                         for inputDevice in self.mainWindowHandler.midiSelectedInputDevicesDict.values():
                                     if inputDevice.poll():
                                         data = inputDevice.read(1)
                                         channel = (data[0][0][0] & 0xF) + 1
                                         if channel == self.mainWindowHandler.midiInputChannel:
                                             status = data[0][0][0] & 0xF0
                                             cc = data[0][0][1]
                                             #if a CC message arrives and is mapped
                                             if status == 0xB0 and cc in self.mainWindowHandler.currentPatch.patchMIDIMapDict:  
                                                 value = data[0][0][2]
                                                 self.dataReceivedSignal.emit(cc, value)
                                             else:
                                                 if self.mainWindowHandler.midiSelectedOutputDevice:
                                                     self.mainWindowHandler.midiSelectedOutputDevice.write(data)                    
                     except midi.MidiException as e:
                         self.midiExceptionSignal.emit(unicode(e))
                     finally:
                         self.mainWindowHandler.midiInputMutex.unlock()
                 self.usleep(200) #don't hog the processor in the polling loop!
     #initialize MIDI, start listening for incoming MIDI data       
     midi.init()
     self.getMIDIDevices()
     self.midiInputThread = _MidiInput(self, self.windowHandler)
     self.midiInputThread.dataReceivedSignal.connect(self.midiInputCallback)         
     self.midiInputThread.midiExceptionSignal.connect(lambda e: QtGui.QMessageBox.warning(self, "MIDI Error", unicode(e)))
     self.midiInputThread.start()
     self.ui.action_Save.setEnabled(False)
     self.restoreSettings(self.windowHandler)
     self.windowHandler.new(self)
Пример #3
0
    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)
        self.midiOutputDevicesDict = {}
        self.midiInputDevicesDict = {}
        self.midiPassthruDevicesDict = {}
        self.midiEnable = 0
        self.onValue = 64
        self.offValue = 0
        #control change values
        self.dialDict = {
                         'lfo2Freq_Dial':           14,   # LFO2FREQ        = MIDICC + $0E ; Knob 1    14 ; 0..255	 (PULSE_KNOB)
                         'pwmDepth_Dial':           15,   # PWMDEPTH        = MIDICC + $0F ; Knob 2    15 ; 0..255	 (=LFO2LEVEL)
                         'fmDepth_Dial':            16,   # FMDEPTH         = MIDICC + $10 ; Knob 3    16 ; 0..255
                         'midiKnob4_Dial':          17,   # MIDI_KNOB_4     = MIDICC + $11 ; Knob 4    17 ; 0..255
                         'midiKnob5_Dial':          18,   # MIDI_KNOB_5     = MIDICC + $12 ; Knob 5    18 ; 0..255
                         'midiKnob6_Dial':          19,   # MIDI_KNOB_6     = MIDICC + $13 ; Knob 6    19 ; 0..255
                         'mixerBalance_Dial':       20,   # MIXER_BALANCE   = MIDICC + $14 ; Knob 7    20 ; -128..+127
                         'masterVolume_Dial':       21,   # MASTER_VOLUME   = MIDICC + $15 ; Knob 8    21 ; 0..255

                         'dcfAttackTime_Slider':    22,   # ATTACKTIME2     = MIDICC + $16 ; _Slider 1 22 ; DCF envelope
                         'dcfDecayTime_Slider':     23,   # DECAYTIME2      = MIDICC + $17 ; _Slider 2 23
                         'dcfSustainLevel_Slider':  24,   # SUSTAINLEVEL2   = MIDICC + $18 ; _Slider 3 24
                         'dcfReleaseTime_Slider':   25,   # RELEASETIME2    = MIDICC + $19 ; _Slider 4 25
                         'dcaAttackTime_Slider':    26,   # ATTACKTIME      = MIDICC + $1A ; _Slider 5 26 ; DCA envelope
                         'dcaDecayTime_Slider':     27,   # DECAYTIME       = MIDICC + $1B ; _Slider 6 27
                         'dcaSustainLevel_Slider':  28,   # SUSTAINLEVEL    = MIDICC + $1C ; _Slider 7 28
                         'dcaReleaseTime_Slider':   29,   # RELEASETIME     = MIDICC + $1D ; _Slider 8 29

#                        'sw1':                     44,   # SW1             = MIDICC + $2C ; Reserved
#                        'sw2':                     45,   # SW2             = MIDICC + $2D ; Reserved
#                        'sw3':                     46,   # SW3             = MIDICC + $2E ; Reserved
#                        'sw4':                     47,   # SW4             = MIDICC + $2F ; Reserved

                         'resonanceDial':           48,   # RESONANCE       = MIDICC + $30
                         'cutoffDial':              49,   # CUTOFF          = MIDICC + $31
                         'lfoRateDial':             50,   # LFOFREQ         = MIDICC + $32
                         'lfoDepthDial':            51,   # PANEL_LFOLEVEL  = MIDICC + $33
                         'envAmountDial':           52,   # VCFENVMOD       = MIDICC + $34
                         'glideAmountDial':         53,   # PORTAMENTO      = MIDICC + $35
                         'oscPWMDial':              54,   # PULSE_KNOB      = MIDICC + $36 ; (=LFO2FREQ)
                         'oscDetuneDial':           55,   # OSC_DETUNE      = MIDICC + $37

                                                          # X               = MIDICC + $38 ; Undefined
                                                          # X               = MIDICC + $39 ; Undefined
                         'filterDecaySlider':       58,   # KNOB_DCF_DECAY  = MIDICC + $3A
                         'filterAttackSlider':      59,   # KNOB_DCF_ATTACK = MIDICC + $3B
                         'ampDecaySlider':          60,   # KNOB_AMP_DECAY  = MIDICC + $3C
                         'ampAttackSlider':         61}   # KNOB_AMP_ATTACK = MIDICC + $3D
                                                          # X               = MIDICC + $3E ; Undefined
                                                          # X               = MIDICC + $3F ; Undefined

        self.buttonDict = {                                                                                     # S_KNOB_SHIFT      = MIDICC + $40
                           'oscFMOn':           [65, self.onValue], 'oscFMOff':          [65, self.offValue],   # S_OSC_FM          = MIDICC + $41
                           'lfoRandomOn':       [66, self.onValue], 'lfoRandomOff':      [66, self.offValue],   # S_LFO_RANDOM      = MIDICC + $42
                           'lfoSquareOn':       [67, self.onValue], 'lfoTriangleOn':     [67, self.offValue],   # S_LFO_WAVE        = MIDICC + $43
                           'lpOn':              [68, self.offValue],'hpOn':              [68, self.onValue],    # S_FILTER_MODE     = MIDICC + $44
                           'distortionOn':      [69, self.onValue], 'distortionOff':     [69, self.offValue],   # S_DISTORTION      = MIDICC + $45
                           'lfoOn':             [70, self.onValue], 'lfoOff':            [70, self.offValue],   # S_LFO_ENABLE      = MIDICC + $46
                           'lfoOscOn':          [71, self.onValue], 'lfoFilterOn':       [71, self.offValue],   # S_LFO_DEST        = MIDICC + $47

                           'antiAliasOn':       [72, self.onValue], 'antiAliasOff':      [72, self.offValue],   # S_ANTI_ALIAS      = MIDICC + $48
                           'oscBOctaveOn':      [73, self.onValue], 'oscBOctaveOff':     [73, self.offValue],   # S_OSCB_OCT        = MIDICC + $49
                           'oscBOn':            [74, self.onValue], 'oscBOff':           [74, self.offValue],   # S_OSCB_ENABLE     = MIDICC + $4A
                           'oscBWaveSquare':    [75, self.onValue], 'oscBWaveTri':       [75, self.offValue],   # S_OSCB_WAVE       = MIDICC + $4B
                           'envSustainOn':      [76, self.onValue], 'envSustainOff':     [76, self.offValue],   # S_SUSTAIN         = MIDICC + $4C
                           'oscANoiseOn':       [77, self.onValue], 'oscANoiseOff':      [77, self.offValue],   # S_OSCA_NOISE      = MIDICC + $4D
                           'pwmSweepOn':        [78, self.onValue], 'pwmSweepOff':       [78, self.offValue],   # S_PWM_SWEEP       = MIDICC + $4E
                           'oscAPWMOn':         [79, self.onValue], 'oscASawOn':         [79, self.offValue],   # S_OSCA_WAVE       = MIDICC + $4F

                           'switch30On':        [80, self.onValue], 'switch30Off':       [80, self.offValue],   # S_MIX_RING        = MIDICC + $50
                           'switch31On':        [81, self.onValue], 'switch31Off':       [81, self.offValue],   # S_VELOCITY        = MIDICC + $51
                           'switch32On':        [82, self.onValue], 'switch32Off':       [82, self.offValue],   # S_DCA_MODE        = MIDICC + $52
                           'switch33On':        [83, self.onValue], 'switch33Off':       [83, self.offValue],   # S_DCA_OPEN        = MIDICC + $53
                           'switch34On':        [84, self.onValue], 'switch34Off':       [84, self.offValue],   # S_TRANSPOSE       = MIDICC + $54
                           'switch35On':        [85, self.onValue], 'switch35Off':       [85, self.offValue],   # S_MODWHEEL_ENA    = MIDICC + $55
                           'switch36On':        [86, self.onValue], 'switch36Off':       [86, self.offValue],   # S_LFO_KBD_SYNC    = MIDICC + $56
                           'switch37On':        [87, self.onValue], 'switch37Off':       [87, self.offValue],   # S_DCF_KBD_TRACK   = MIDICC + $57

                           'switch40On':        [88, self.onValue], 'switch40Off':       [88, self.offValue],   # S_USER_4_0        = MIDICC + $58
                           'switch41On':        [89, self.onValue], 'switch41Off':       [89, self.offValue],   # S_USER_4_1        = MIDICC + $59
                           'switch42On':        [90, self.onValue], 'switch42Off':       [90, self.offValue],   # S_ARP_ENABLE        = MIDICC + $5A
                           'switch43On':        [91, self.onValue], 'switch43Off':       [91, self.offValue],   # S_ARP_REPEAT        = MIDICC + $5B
                           'switch44On':        [92, self.onValue], 'switch44Off':       [92, self.offValue],   # S_USER_4_4         = MIDICC + $5C
                           'switch45On':        [93, self.onValue], 'switch45Off':       [93, self.offValue],   # S_USER_4_5         = MIDICC + $5D
                           'switch46On':        [94, self.onValue], 'switch46Off':       [94, self.offValue],   # S_LFO2_WAVE       = MIDICC + $5E
                           'switch47On':        [95, self.onValue], 'switch47Off':       [95, self.offValue]}   # S_LFO2_RANDOM     = MIDICC + $5F

        self.buttonBoxDict = {
                              'oscAWaveBox':    'oscAWaveGroup',
                              'oscANoiseBox':   'oscANoiseGroup',
                              'oscBEnableBox':  'oscBEnableGroup',
                              'oscBWaveBox':    'oscBWaveGroup',
                              'oscBOctaveBox':  'oscBOctaveGroup',
                              'oscFMBox':       'oscFMGroup',
                              'lfoEnableBox':   'lfoEnableGroup',
                              'lfoDestBox':     'lfoDestGroup',
                              'lfoWaveBox':     'lfoWaveGroup',
                              'lfoRandomBox':   'lfoRandomGroup',
                              'filterModeBox':  'filterModeGroup',
                              'distortionBox':  'distortionGroup',
                              'antiAliasBox':   'antiAliasGroup',
                              'envSustainBox':  'envSustainGroup',
                              'pwmSweepBox':    'pwmSweepGroup',
                              'oscAWaveBox':    'oscAWaveGroup',
                              'switch30Box':    'switch30Group',
                              'switch31Box':    'switch31Group',
                              'switch32Box':    'switch32Group',
                              'switch33Box':    'switch33Group',
                              'switch34Box':    'switch34Group',
                              'switch35Box':    'switch35Group',
                              'switch36Box':    'switch36Group',
                              'switch37Box':    'switch37Group',
                              'switch40Box':    'switch40Group',
                              'switch41Box':    'switch41Group',
                              'switch42Box':    'switch42Group',
                              'switch43Box':    'switch43Group',
                              'switch44Box':    'switch44Group',
                              'switch45Box':    'switch45Group',
                              'switch46Box':    'switch46Group',
                              'switch47Box':    'switch47Group' }

# ---------------------------------------------------------------------------
#
# ---------------------------------------------------------------------------

        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)
        self.windowHandler = MainWindowHandler(self.dialDict, self.buttonDict)
        self.settings = QtCore.QSettings('MeeblipControl', 'MeeblipControl')

        #connect signals and slots
        for dial, cc, in self.dialDict.iteritems():
            currentDial = getattr(self.ui, dial)
            currentDial.setRange(0,127)
            dialChanged = partial(self.windowHandler.dialChanged, mainWindowInstance=self, cc=cc)
            currentDial.valueChanged.connect(dialChanged)
            currentDial.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
            currentDial.customContextMenuRequested.connect(partial(self.windowHandler.contextMenu, mainWindowInstance=self, widgetName=dial))

        for button, buttonList in self.buttonDict.iteritems():
            currentButton = getattr(self.ui, button)
            buttonFunc = partial(self.windowHandler.buttonChanged, mainWindowInstance=self, value=buttonList[1], cc=buttonList[0], button=currentButton)
            currentButton.toggled.connect(buttonFunc)

        for buttonGroupBox in self.buttonBoxDict.keys():
            currentGroup = getattr(self.ui, buttonGroupBox)
            currentGroup.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
            currentGroup.customContextMenuRequested.connect(partial(self.windowHandler.contextMenu, mainWindowInstance=self,
                                                                    widgetName=buttonGroupBox))

# ---------------------------------------------------------------------------
#
# ---------------------------------------------------------------------------

        class _MidiInput(QtCore.QThread):
            dataReceivedSignal = QtCore.pyqtSignal(int, int, int)
            midiExceptionSignal = QtCore.pyqtSignal(str)

            def __init__(self, mainWindow, mainWindowHandler, parent=None):
                super(_MidiInput, self).__init__(parent)
                self.mainWindow = mainWindow
                self.mainWindowHandler = mainWindowHandler

            def run(self):
                while True:
                    if self.mainWindowHandler.midiSelectedInputDevicesDict:
                        try:
                            self.mainWindowHandler.midiInputMutex.lock()
                            for inputDeviceIndex in self.mainWindowHandler.midiSelectedInputDevicesDict.keys():
                                inputDevice = self.mainWindowHandler.midiSelectedInputDevicesDict[ inputDeviceIndex]
                                if inputDevice.poll():
                                    data = inputDevice.read(1)
                                    channel = (data[0][0][0] & 0xF) + 1

                                    # check for midi passthru
                                    enable_midi = 0
                                    if self.mainWindowHandler.midiSelectedPassthruDevicesDict:
                                        for passthruDeviceIndex in self.mainWindowHandler.midiSelectedPassthruDevicesDict.keys():
                                            if passthruDeviceIndex == inputDeviceIndex :
                                                enable_midi = 1

                                    if channel == self.mainWindowHandler.midiInputChannel:
                                        status = data[0][0][0] & 0xF0
                                        cc = data[0][0][1]
                                        #if a CC message arrives and is mapped
                                        if status == 0xB0 and cc in self.mainWindowHandler.currentPatch.patchMIDIMapDict:
                                            value = data[0][0][2]
                                            self.dataReceivedSignal.emit(cc, value, enable_midi)
                                        else:
                                            if self.mainWindowHandler.midiSelectedOutputDevice and enable_midi != 0:
                                                self.mainWindowHandler.midiSelectedOutputDevice.write(data)

# here we got an exception when closing application
#  Traceback (most recent call last):
#    File "..\source\meeblipControl.py", line 209, in run
#      except midi.MidiException as e:
#  close failed in file object destructor:
#  Error in sys.excepthook:

                        except midi.MidiException as e:
                            self.midiExceptionSignal.emit(unicode(e))
                        finally:
                            self.mainWindowHandler.midiInputMutex.unlock()
                    self.usleep(200) #don't hog the processor in the polling loop!

# ---------------------------------------------------------------------------
#
# ---------------------------------------------------------------------------

        #initialize MIDI, start listening for incoming MIDI data
        midi.init()
        self.getMIDIDevices()
        self.midiInputThread = _MidiInput(self, self.windowHandler)
        self.midiInputThread.dataReceivedSignal.connect(self.midiInputCallback)
        self.midiInputThread.midiExceptionSignal.connect(lambda e: QtGui.QMessageBox.warning(self, "MIDI Error", unicode(e)))
        self.midiInputThread.start()
        self.ui.action_Save.setEnabled(False)
        self.restoreSettings(self.windowHandler)
        self.midiEnable = 1
        self.windowHandler.new(self)
        self.windowHandler.load_default(self, "default.mee")