| 1 | # Copyright (C) 2006 by Patrick Stinson |
|---|
| 2 | # patrickkidd@gmail.com |
|---|
| 3 | # |
|---|
| 4 | # This program is free software; you can redistribute it and/or modify |
|---|
| 5 | # it under the terms of the GNU General Public License as published by |
|---|
| 6 | # the Free Software Foundation; either version 2 of the License, or |
|---|
| 7 | # (at your option) any later version. |
|---|
| 8 | # |
|---|
| 9 | # This program is distributed in the hope that it will be useful, |
|---|
| 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|---|
| 12 | # GNU General Public License for more details. |
|---|
| 13 | # |
|---|
| 14 | # You should have received a copy of the GNU General Public License |
|---|
| 15 | # along with this program; if not, write to the |
|---|
| 16 | # Free Software Foundation, Inc., |
|---|
| 17 | # 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
|---|
| 18 | # |
|---|
| 19 | |
|---|
| 20 | from PyQt4.QtCore import QCoreApplication, QObject, SIGNAL, QThread, Qt |
|---|
| 21 | from PyQt4.QtGui import QApplication, QWidget, QLabel, QGridLayout |
|---|
| 22 | import rtmidi |
|---|
| 23 | import pk.widgets |
|---|
| 24 | |
|---|
| 25 | |
|---|
| 26 | class MidiInput(QThread): |
|---|
| 27 | def __init__(self, devid, parent=None): |
|---|
| 28 | QThread.__init__(self, parent) |
|---|
| 29 | self.running = False |
|---|
| 30 | try: |
|---|
| 31 | self.device = rtmidi.RtMidiIn() |
|---|
| 32 | except rtmidi.RtError, e: |
|---|
| 33 | print 'Error polling midi input devices',e |
|---|
| 34 | return |
|---|
| 35 | |
|---|
| 36 | if self.device.getPortCount() == 0: |
|---|
| 37 | return |
|---|
| 38 | |
|---|
| 39 | try: |
|---|
| 40 | self.device.openPort(devid, True) |
|---|
| 41 | self.running = True |
|---|
| 42 | except rtmidi.RtError, e: |
|---|
| 43 | print 'Could not open MIDI device %i' % devid |
|---|
| 44 | |
|---|
| 45 | def run(self): |
|---|
| 46 | while self.running: |
|---|
| 47 | msg = self.device.getMessage() |
|---|
| 48 | if msg: |
|---|
| 49 | self.msg = msg |
|---|
| 50 | self.emit(SIGNAL('midiMessage(PyObject *)'), self.msg) |
|---|
| 51 | self.emit(SIGNAL('midiMessage()')) |
|---|
| 52 | |
|---|
| 53 | |
|---|
| 54 | class ControlMapper(QLabel): |
|---|
| 55 | """ Grabs and maps all input. """ |
|---|
| 56 | def __init__(self, device, parent=None): |
|---|
| 57 | QLabel.__init__(self, parent) |
|---|
| 58 | self.controls = {} |
|---|
| 59 | self.keys = {} |
|---|
| 60 | |
|---|
| 61 | self.midiin = MidiInput(0, self) |
|---|
| 62 | QObject.connect(self.midiin, SIGNAL('midiMessage()'), |
|---|
| 63 | self._midi_message) |
|---|
| 64 | |
|---|
| 65 | def _midi_message(self): |
|---|
| 66 | self.midiEvent(self.midiin.msg) |
|---|
| 67 | |
|---|
| 68 | def midiEvent(self, msg): |
|---|
| 69 | channel = msg[0] |
|---|
| 70 | self.controls.get(channel, self._default)(self.midiin.msg) |
|---|
| 71 | |
|---|
| 72 | def keyPressEvent(self, e): |
|---|
| 73 | self.keys.get(e.key(), self._default)(e.key()) |
|---|
| 74 | |
|---|
| 75 | def _default(self, msg=None): |
|---|
| 76 | print 'UNBOUND MIDI:',msg |
|---|
| 77 | |
|---|
| 78 | def set_default(self, callback): |
|---|
| 79 | self._default = callback |
|---|
| 80 | |
|---|
| 81 | def set_midi(self, channel, callback): |
|---|
| 82 | self.controls[channel] = callback |
|---|
| 83 | |
|---|
| 84 | def set_key(self, key, callback): |
|---|
| 85 | self.keys[key] = callback |
|---|
| 86 | |
|---|
| 87 | def clear_midi(self): |
|---|
| 88 | self.controls = {} |
|---|
| 89 | |
|---|
| 90 | def clear_keys(self): |
|---|
| 91 | self.keys = {} |
|---|
| 92 | |
|---|
| 93 | |
|---|
| 94 | class InputWidget(ControlMapper): |
|---|
| 95 | def __init__(self, device=1, parent=None): |
|---|
| 96 | ControlMapper.__init__(self, device, parent) |
|---|
| 97 | |
|---|
| 98 | self.midiLabel = QLabel('midi', self) |
|---|
| 99 | self.midiLabel.setAlignment(Qt.AlignCenter) |
|---|
| 100 | self.midiLED = pk.widgets.LED(self) |
|---|
| 101 | self.midiLED.setFixedSize(20, 10) |
|---|
| 102 | |
|---|
| 103 | self.kbLabel = QLabel('kb', self) |
|---|
| 104 | self.kbLabel.setAlignment(Qt.AlignCenter) |
|---|
| 105 | self.kbLED = pk.widgets.LED(self) |
|---|
| 106 | self.kbLED.setFixedSize(20, 10) |
|---|
| 107 | |
|---|
| 108 | Layout = QGridLayout(self) |
|---|
| 109 | Layout.setRowStretch(0, 1) |
|---|
| 110 | Layout.addWidget(self.kbLabel, 2, 0) |
|---|
| 111 | Layout.addWidget(self.kbLED, 1, 0) |
|---|
| 112 | Layout.addWidget(self.midiLabel, 2, 1) |
|---|
| 113 | Layout.addWidget(self.midiLED, 1, 1) |
|---|
| 114 | Layout.setRowStretch(3, 1) |
|---|
| 115 | |
|---|
| 116 | def midiEvent(self, msg): |
|---|
| 117 | self.midiLED.blink() |
|---|
| 118 | ControlMapper.midiEvent(self, msg) |
|---|
| 119 | |
|---|
| 120 | def keyPressEvent(self, e): |
|---|
| 121 | self.kbLED.blink() |
|---|
| 122 | ControlMapper.keyPressEvent(self, e) |
|---|
| 123 | |
|---|
| 124 | |
|---|
| 125 | def _main1(): |
|---|
| 126 | from PyQt4.QtGui import QApplication, QLabel |
|---|
| 127 | app = QApplication([]) |
|---|
| 128 | label = QLabel('no message yet') |
|---|
| 129 | mapper = InputWidget() |
|---|
| 130 | |
|---|
| 131 | def cb_message(msg): |
|---|
| 132 | print 'message',msg |
|---|
| 133 | def channel_180(msg): |
|---|
| 134 | label.setText(str(msg)) |
|---|
| 135 | |
|---|
| 136 | mapper.set_default(cb_message) |
|---|
| 137 | mapper.set_midi(180, channel_180) |
|---|
| 138 | mapper.midiin.start() |
|---|
| 139 | mapper.show() |
|---|
| 140 | # label.show() |
|---|
| 141 | app.exec_() |
|---|
| 142 | |
|---|
| 143 | def _main2(): |
|---|
| 144 | import time |
|---|
| 145 | midiin = MidiInput(1) |
|---|
| 146 | midiin.start() |
|---|
| 147 | time.sleep(1) |
|---|
| 148 | |
|---|
| 149 | if __name__ == '__main__': |
|---|
| 150 | _main1() |
|---|
| 151 | |
|---|