Fix build with gcc 4.7
[cadence.git] / src / pixmapkeyboard.py
blob1fab6087ae3f0bb63cf2cc6d4c86d0bd4ef091ed
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
4 # Imports (Global)
5 from PyQt4.QtCore import qCritical, Qt, QPointF, QRectF, QTimer, SIGNAL
6 from PyQt4.QtGui import QFont, QPainter, QPixmap, QWidget
8 # Imports (Custom Stuff)
9 import icons_rc
11 midi_key2rect_map_horizontal = {
12 '0': QRectF( 0, 0, 18, 64), # C
13 '1': QRectF( 13, 0, 11, 42), # C#
14 '2': QRectF( 18, 0, 25, 64), # D
15 '3': QRectF( 37, 0, 11, 42), # D#
16 '4': QRectF( 42, 0, 18, 64), # E
17 '5': QRectF( 60, 0, 18, 64), # F
18 '6': QRectF( 73, 0, 11, 42), # F#
19 '7': QRectF( 78, 0, 25, 64), # G
20 '8': QRectF( 97, 0, 11, 42), # G#
21 '9': QRectF(102, 0, 25, 64), # A
22 '10': QRectF(121, 0, 11, 42), # A#
23 '11': QRectF(126, 0, 18, 64) # B
26 midi_key2rect_map_vertical = {
27 '11': QRectF(0, 0, 64, 18), # B
28 '10': QRectF(0, 14, 42, 7), # A#
29 '9': QRectF(0, 18, 64, 24), # A
30 '8': QRectF(0, 38, 42, 7), # G#
31 '7': QRectF(0, 42, 64, 24), # G
32 '6': QRectF(0, 62, 42, 7), # F#
33 '5': QRectF(0, 66, 64, 18), # F
34 '4': QRectF(0, 84, 64, 18), # E
35 '3': QRectF(0, 98, 42, 7), # D#
36 '2': QRectF(0, 102, 64, 24), # D
37 '1': QRectF(0, 122, 42, 7), # C#
38 '0': QRectF(0, 126, 64, 18) # C
41 midi_keyboard2key_map = {
42 # 4th octave
43 '%i' % (Qt.Key_Z): 48,
44 '%i' % (Qt.Key_S): 49,
45 '%i' % (Qt.Key_X): 50,
46 '%i' % (Qt.Key_D): 51,
47 '%i' % (Qt.Key_C): 52,
48 '%i' % (Qt.Key_V): 53,
49 '%i' % (Qt.Key_G): 54,
50 '%i' % (Qt.Key_B): 55,
51 '%i' % (Qt.Key_H): 56,
52 '%i' % (Qt.Key_N): 57,
53 '%i' % (Qt.Key_J): 58,
54 '%i' % (Qt.Key_M): 59,
55 # 5th octave
56 '%i' % (Qt.Key_Q): 60,
57 '%i' % (Qt.Key_2): 61,
58 '%i' % (Qt.Key_W): 62,
59 '%i' % (Qt.Key_3): 63,
60 '%i' % (Qt.Key_E): 64,
61 '%i' % (Qt.Key_R): 65,
62 '%i' % (Qt.Key_5): 66,
63 '%i' % (Qt.Key_T): 67,
64 '%i' % (Qt.Key_6): 68,
65 '%i' % (Qt.Key_Y): 69,
66 '%i' % (Qt.Key_7): 70,
67 '%i' % (Qt.Key_U): 71,
70 def note_is_black(note):
71 base_key = note % 12
72 if (base_key in (1, 3, 6, 8, 10)):
73 return True
74 else:
75 return False
77 # MIDI Keyboard, using a pixmap for painting
78 class PixmapKeyboard(QWidget):
80 COLOR_CLASSIC = 0
81 COLOR_ORANGE = 1
83 HORIZONTAL = 0
84 VERTICAL = 1
86 def __init__(self, parent):
87 super(PixmapKeyboard, self).__init__(parent)
89 self.keys_to_enable = []
90 self.rect_to_enable = []
91 self.last_mouse_note = -1
92 self.needs_update = False
94 self.send_midi_data = False
95 self.midi_output_data = []
97 self.p_width = 1
98 self.p_height = 1
99 self.octaves = 4
101 self.font = QFont("Monospace", 8, QFont.Normal)
102 self.pixmap = QPixmap("")
103 self.setMode(self.HORIZONTAL)
105 def runSingleUpdate(self):
106 if (self.needs_update):
107 self.update()
108 self.needs_update = False
110 def get_rect_from_key(self, midi_key):
111 return self.midi_map.get(str(midi_key % 12))
113 def setMode(self, mode, color=COLOR_ORANGE):
114 if (color == self.COLOR_CLASSIC):
115 self.color_str = "classic"
116 elif (color == self.COLOR_ORANGE):
117 self.color_str = "orange"
118 else:
119 qCritical("Wrong Keyboard Color!")
120 self.setMidiMap(mode, self.COLOR_ORANGE)
121 return
123 if (mode == self.HORIZONTAL):
124 self.midi_map = midi_key2rect_map_horizontal
125 self.pixmap.load(":/bitmaps/kbd_h_%s.png" % (self.color_str))
126 self.pixmap_mode = self.HORIZONTAL
128 elif (mode == self.VERTICAL):
129 self.midi_map = midi_key2rect_map_vertical
130 self.pixmap.load(":/bitmaps/kbd_v_%s.png" % (self.color_str))
131 self.pixmap_mode = self.VERTICAL
133 else:
134 qCritical("Wrong Keyboard Mode!")
135 self.setMode(self.HORIZONTAL)
136 return
138 if (self.pixmap_mode == self.HORIZONTAL):
139 self.p_width = self.pixmap.width()
140 self.p_height = self.pixmap.height()/2
141 #self.setMinimumHeight(self.p_height)
142 #self.setMaximumHeight(self.p_height)
143 elif (self.pixmap_mode == self.VERTICAL):
144 self.p_width = self.pixmap.width()/2
145 self.p_height = self.pixmap.height()
146 #self.setMinimumWidth(self.p_width)
147 #self.setMaximumWidth(self.p_width)
149 self.setOctaves(self.octaves)
151 def setOctaves(self, n):
152 if (n < 1):
153 n = 1
154 elif (n > 6):
155 n = 6
157 self.octaves = n
159 if (self.pixmap_mode == self.HORIZONTAL):
160 self.setMinimumSize(self.p_width*self.octaves, self.p_height)
161 self.setMaximumSize(self.p_width*self.octaves, self.p_height)
162 elif (self.pixmap_mode == self.VERTICAL):
163 self.setMinimumSize(self.p_width, self.p_height*self.octaves)
164 self.setMaximumSize(self.p_width, self.p_height*self.octaves)
166 self.update()
168 def noteOn(self, note, send=True):
169 if (note >= 0 and note <= 127 and note not in self.keys_to_enable):
170 self.keys_to_enable.append(note)
171 self.rect_to_enable.append(self.get_rect_from_key(note))
172 if (send):
173 self.emit(SIGNAL("noteOn(int)"), note)
175 self.needs_update = True
176 QTimer.singleShot(0, self.runSingleUpdate)
178 if (len(self.keys_to_enable) == 1):
179 self.emit(SIGNAL("notesOn()"))
181 def noteOff(self, note, send=True):
182 if (note >= 0 and note <= 127 and note in self.keys_to_enable):
183 self.keys_to_enable.remove(note)
184 self.rect_to_enable.remove(self.get_rect_from_key(note))
185 if (send):
186 self.emit(SIGNAL("noteOff(int)"), note)
188 self.needs_update = True
189 QTimer.singleShot(0, self.runSingleUpdate)
191 if (len(self.keys_to_enable) == 0):
192 self.emit(SIGNAL("notesOff()"))
194 def keyPressEvent(self, event):
195 qt_key = str(event.key())
197 if (qt_key in midi_keyboard2key_map.keys()):
198 midi_key = midi_keyboard2key_map.get(qt_key)
199 self.noteOn(midi_key)
201 return QWidget.keyPressEvent(self, event)
203 def keyReleaseEvent(self, event):
204 qt_key = str(event.key())
206 if (qt_key in midi_keyboard2key_map.keys()):
207 midi_key = midi_keyboard2key_map.get(qt_key)
208 self.noteOff(midi_key)
210 return QWidget.keyReleaseEvent(self, event)
212 def mousePressEvent(self, event):
213 self.last_mouse_note = -1
214 self.handleMousePos(event.pos())
215 self.setFocus()
216 return QWidget.mousePressEvent(self, event)
218 def mouseMoveEvent(self, event):
219 self.handleMousePos(event.pos())
220 return QWidget.mousePressEvent(self, event)
222 def mouseReleaseEvent(self, event):
223 if (self.last_mouse_note != -1):
224 self.noteOff(self.last_mouse_note)
225 self.last_mouse_note = -1
226 return QWidget.mouseReleaseEvent(self, event)
228 def handleMousePos(self, pos):
229 if (self.pixmap_mode == self.HORIZONTAL):
230 octave = pos.x()/self.p_width
231 n_pos = QPointF(pos.x()%self.p_width, pos.y())
232 elif (self.pixmap_mode == self.VERTICAL):
233 octave = (self.octaves-pos.y()/self.p_height)-1
234 n_pos = QPointF(pos.x(), pos.y()%self.p_height)
235 else:
236 octave = 0
237 n_pos = QPointF(0, 0)
238 return
240 note = -1
241 octave += 3
243 if (self.midi_map['1'].contains(n_pos)): # C#
244 note = 1+(octave*12)
245 elif (self.midi_map['3'].contains(n_pos)): # D#
246 note = 3+(octave*12)
247 elif (self.midi_map['6'].contains(n_pos)): # F#
248 note = 6+(octave*12)
249 elif (self.midi_map['8'].contains(n_pos)): # G#
250 note = 8+(octave*12)
251 elif (self.midi_map['10'].contains(n_pos)):# A#
252 note = 10+(octave*12)
253 elif (self.midi_map['0'].contains(n_pos)): # C
254 note = 0+(octave*12)
255 elif (self.midi_map['2'].contains(n_pos)): # D
256 note = 2+(octave*12)
257 elif (self.midi_map['4'].contains(n_pos)): # E
258 note = 4+(octave*12)
259 elif (self.midi_map['5'].contains(n_pos)): # F
260 note = 5+(octave*12)
261 elif (self.midi_map['7'].contains(n_pos)): # G
262 note = 7+(octave*12)
263 elif (self.midi_map['9'].contains(n_pos)): # A
264 note = 9+(octave*12)
265 elif (self.midi_map['11'].contains(n_pos)):# B
266 note = 11+(octave*12)
268 if (note == -1):
269 self.noteOff(self.last_mouse_note)
270 elif (self.last_mouse_note != note):
271 self.noteOff(self.last_mouse_note)
272 self.noteOn(note)
274 self.last_mouse_note = note
276 def paintEvent(self, event):
277 painter = QPainter(self)
279 # Paint clean keys
280 for i in range(self.octaves):
281 if (self.pixmap_mode == self.HORIZONTAL):
282 target = QRectF((self.p_width*i), 0, self.p_width, self.p_height)
283 elif (self.pixmap_mode == self.VERTICAL):
284 target = QRectF(0, self.p_height*i, self.p_width, self.p_height)
285 else:
286 return
288 source = QRectF(0, 0, self.p_width, self.p_height)
289 painter.drawPixmap(target, self.pixmap, source)
291 # Paint (white) pressed keys
292 for i in range(len(self.rect_to_enable)):
293 pos = self.rect_to_enable[i]
294 note = self.keys_to_enable[i]
296 if (note < 36 or note/12 > self.octaves+2 or note_is_black(note)):
297 continue
299 if (note < 35):
300 # cannot paint this note
301 continue
302 elif (note < 48):
303 octx = 0
304 elif (note < 60):
305 octx = 1
306 elif (note < 72):
307 octx = 2
308 elif (note < 84):
309 octx = 3
310 elif (note < 96):
311 octx = 4
312 elif (note < 108):
313 octx = 5
314 else:
315 # cannot paint this note too
316 continue
318 if (self.pixmap_mode == self.VERTICAL):
319 octx = self.octaves - octx - 1
321 if (self.pixmap_mode == self.HORIZONTAL):
322 target = QRectF(pos.x()+(self.p_width*octx), 0, pos.width(), pos.height())
323 source = QRectF(pos.x(), self.p_height, pos.width(), pos.height())
324 elif (self.pixmap_mode == self.VERTICAL):
325 target = QRectF(pos.x(), pos.y()+(self.p_height*octx), pos.width(), pos.height())
326 source = QRectF(self.p_width, pos.y(), pos.width(), pos.height())
328 painter.drawPixmap(target, self.pixmap, source)
330 # Clear white keys border
331 for octx in range(self.octaves):
332 for note in [2, 4, 7, 9, 11]:
333 pos = self.get_rect_from_key(note-1)
334 if (self.pixmap_mode == self.HORIZONTAL):
335 target = QRectF(pos.x()+(self.p_width*octx), 0, pos.width(), pos.height())
336 source = QRectF(pos.x(), 0, pos.width(), pos.height())
337 elif (self.pixmap_mode == self.VERTICAL):
338 target = QRectF(pos.x(), pos.y()+(self.p_height*octx), pos.width(), pos.height())
339 source = QRectF(0, pos.y(), pos.width(), pos.height())
341 painter.drawPixmap(target, self.pixmap, source)
343 # Paint (black) pressed keys
344 for i in range(len(self.rect_to_enable)):
345 pos = self.rect_to_enable[i]
346 note = self.keys_to_enable[i]
348 if (note < 36 or note/12 > self.octaves+2 or not note_is_black(note)):
349 continue
351 if (note < 35):
352 # cannot paint this note
353 continue
354 elif (note < 48):
355 octx = 0
356 elif (note < 60):
357 octx = 1
358 elif (note < 72):
359 octx = 2
360 elif (note < 84):
361 octx = 3
362 elif (note < 96):
363 octx = 4
364 elif (note < 108):
365 octx = 5
366 else:
367 # cannot paint this note too
368 continue
370 if (self.pixmap_mode == self.VERTICAL):
371 octx = self.octaves - octx - 1
373 if (self.pixmap_mode == self.HORIZONTAL):
374 target = QRectF(pos.x()+(self.p_width*octx), 0, pos.width(), pos.height())
375 source = QRectF(pos.x(), self.p_height, pos.width(), pos.height())
376 elif (self.pixmap_mode == self.VERTICAL):
377 target = QRectF(pos.x(), pos.y()+(self.p_height*octx), pos.width(), pos.height())
378 source = QRectF(self.p_width, pos.y(), pos.width(), pos.height())
380 painter.drawPixmap(target, self.pixmap, source)
382 # Paint C# note info
383 painter.setFont(self.font)
384 painter.setPen(Qt.black)
385 for i in range(self.octaves):
386 if (self.pixmap_mode == self.HORIZONTAL):
387 painter.drawText(i*144, 48, 18, 18, Qt.AlignCenter, "C%s" % (i+1))
388 elif (self.pixmap_mode == self.VERTICAL):
389 painter.drawText(45, i*144, 18, 18, Qt.AlignCenter, "C%s" % (i+1))