2 # -*- coding: utf-8 -*-
5 from PyQt4
.QtCore
import qCritical
, Qt
, QPointF
, QRectF
, QTimer
, SIGNAL
6 from PyQt4
.QtGui
import QFont
, QPainter
, QPixmap
, QWidget
8 # Imports (Custom Stuff)
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
= {
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,
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
):
72 if (base_key
in (1, 3, 6, 8, 10)):
77 # MIDI Keyboard, using a pixmap for painting
78 class PixmapKeyboard(QWidget
):
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
= []
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
):
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"
119 qCritical("Wrong Keyboard Color!")
120 self
.setMidiMap(mode
, self
.COLOR_ORANGE
)
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
134 qCritical("Wrong Keyboard Mode!")
135 self
.setMode(self
.HORIZONTAL
)
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
):
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
)
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
))
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
))
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())
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
)
237 n_pos
= QPointF(0, 0)
243 if (self
.midi_map
['1'].contains(n_pos
)): # C#
245 elif (self
.midi_map
['3'].contains(n_pos
)): # D#
247 elif (self
.midi_map
['6'].contains(n_pos
)): # F#
249 elif (self
.midi_map
['8'].contains(n_pos
)): # G#
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
255 elif (self
.midi_map
['2'].contains(n_pos
)): # D
257 elif (self
.midi_map
['4'].contains(n_pos
)): # E
259 elif (self
.midi_map
['5'].contains(n_pos
)): # F
261 elif (self
.midi_map
['7'].contains(n_pos
)): # G
263 elif (self
.midi_map
['9'].contains(n_pos
)): # A
265 elif (self
.midi_map
['11'].contains(n_pos
)):# B
266 note
= 11+(octave
*12)
269 self
.noteOff(self
.last_mouse_note
)
270 elif (self
.last_mouse_note
!= note
):
271 self
.noteOff(self
.last_mouse_note
)
274 self
.last_mouse_note
= note
276 def paintEvent(self
, event
):
277 painter
= QPainter(self
)
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
)
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
)):
300 # cannot paint this note
315 # cannot paint this note too
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
)):
352 # cannot paint this note
367 # cannot paint this note too
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
)
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))