Cleanup
[carla.git] / source / frontend / widgets / pixmapkeyboard.cpp
blob229b5c256415a599498eb333fb41a0c6c02e5f16
1 /*
2 * Pixmap Keyboard, a custom Qt4 widget
3 * Copyright (C) 2011-2013 Filipe Coelho <falktx@falktx.com>
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as
7 * published by the Free Software Foundation; either version 2 of
8 * the License, or any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * For a full copy of the GNU General Public License see the doc/GPL.txt file.
18 #include "pixmapkeyboard.hpp"
20 #include <QtCore/QTimer>
21 #include <QtGui/QKeyEvent>
22 #include <QtGui/QMouseEvent>
23 #include <QtGui/QPainter>
25 #ifndef HAVE_CPP11_SUPPORT
26 static std::map<int, QRectF> kMidiKey2RectMapHorizontal;
27 #else
28 static std::map<int, QRectF> kMidiKey2RectMapHorizontal = {
29 {0, QRectF(0, 0, 18, 64)}, // C
30 {1, QRectF(13, 0, 11, 42)}, // C#
31 {2, QRectF(18, 0, 25, 64)}, // D
32 {3, QRectF(37, 0, 11, 42)}, // D#
33 {4, QRectF(42, 0, 18, 64)}, // E
34 {5, QRectF(60, 0, 18, 64)}, // F
35 {6, QRectF(73, 0, 11, 42)}, // F#
36 {7, QRectF(78, 0, 25, 64)}, // G
37 {8, QRectF(97, 0, 11, 42)}, // G#
38 {9, QRectF(102, 0, 25, 64)}, // A
39 {10, QRectF(121, 0, 11, 42)}, // A#
40 {11, QRectF(126, 0, 18, 64)} // B
42 #endif
44 #ifndef HAVE_CPP11_SUPPORT
45 static std::map<int, QRectF> kMidiKey2RectMapVertical;
46 #else
47 static std::map<int, QRectF> kMidiKey2RectMapVertical = {
48 {11, QRectF(0, 0, 64, 18)}, // B
49 {10, QRectF(0, 14, 42, 7)}, // A#
50 {9, QRectF(0, 18, 64, 24)}, // A
51 {8, QRectF(0, 38, 42, 7)}, // G#
52 {7, QRectF(0, 42, 64, 24)}, // G
53 {6, QRectF(0, 62, 42, 7)}, // F#
54 {5, QRectF(0, 66, 64, 18)}, // F
55 {4, QRectF(0, 84, 64, 18)}, // E
56 {3, QRectF(0, 98, 42, 7)}, // D#
57 {2, QRectF(0, 102, 64, 24)}, // D
58 {1, QRectF(0, 122, 42, 7)}, // C#
59 {0, QRectF(0, 126, 64, 18)} // C
61 #endif
63 #ifndef HAVE_CPP11_SUPPORT
64 static std::map<int, int> kMidiKeyboard2KeyMap;
65 #else
66 static const std::map<int, int> kMidiKeyboard2KeyMap = {
67 // 3th octave
68 {Qt::Key_Z, 48},
69 {Qt::Key_S, 49},
70 {Qt::Key_X, 50},
71 {Qt::Key_D, 51},
72 {Qt::Key_C, 52},
73 {Qt::Key_V, 53},
74 {Qt::Key_G, 54},
75 {Qt::Key_B, 55},
76 {Qt::Key_H, 56},
77 {Qt::Key_N, 57},
78 {Qt::Key_J, 58},
79 {Qt::Key_M, 59},
80 // 4th octave
81 {Qt::Key_Q, 60},
82 {Qt::Key_2, 61},
83 {Qt::Key_W, 62},
84 {Qt::Key_3, 63},
85 {Qt::Key_E, 64},
86 {Qt::Key_R, 65},
87 {Qt::Key_5, 66},
88 {Qt::Key_T, 67},
89 {Qt::Key_6, 68},
90 {Qt::Key_Y, 69},
91 {Qt::Key_7, 70},
92 {Qt::Key_U, 71}
94 #endif
96 #ifndef HAVE_CPP11_SUPPORT
97 static QVector<int> kBlackNotes;
98 #else
99 static const QVector<int> kBlackNotes = {1, 3, 6, 8, 10};
100 #endif
102 #ifndef HAVE_CPP11_SUPPORT
103 static const struct PixmapKeyboardInit {
104 PixmapKeyboardInit() {
105 // kMidiKey2RectMapHorizontal
106 kMidiKey2RectMapHorizontal[0] = QRectF(0, 0, 18, 64); // C
107 kMidiKey2RectMapHorizontal[1] = QRectF(13, 0, 11, 42); // C#
108 kMidiKey2RectMapHorizontal[2] = QRectF(18, 0, 25, 64); // D
109 kMidiKey2RectMapHorizontal[3] = QRectF(37, 0, 11, 42); // D#
110 kMidiKey2RectMapHorizontal[4] = QRectF(42, 0, 18, 64); // E
111 kMidiKey2RectMapHorizontal[5] = QRectF(60, 0, 18, 64); // F
112 kMidiKey2RectMapHorizontal[6] = QRectF(73, 0, 11, 42); // F#
113 kMidiKey2RectMapHorizontal[7] = QRectF(78, 0, 25, 64); // G
114 kMidiKey2RectMapHorizontal[8] = QRectF(97, 0, 11, 42); // G#
115 kMidiKey2RectMapHorizontal[9] = QRectF(102, 0, 25, 64); // A
116 kMidiKey2RectMapHorizontal[10] = QRectF(121, 0, 11, 42); // A#
117 kMidiKey2RectMapHorizontal[11] = QRectF(126, 0, 18, 64); // B
119 // kMidiKey2RectMapVertical
120 kMidiKey2RectMapVertical[11] = QRectF(0, 0, 64, 18); // B
121 kMidiKey2RectMapVertical[10] = QRectF(0, 14, 42, 7); // A#
122 kMidiKey2RectMapVertical[9] = QRectF(0, 18, 64, 24); // A
123 kMidiKey2RectMapVertical[8] = QRectF(0, 38, 42, 7); // G#
124 kMidiKey2RectMapVertical[7] = QRectF(0, 42, 64, 24); // G
125 kMidiKey2RectMapVertical[6] = QRectF(0, 62, 42, 7); // F#
126 kMidiKey2RectMapVertical[5] = QRectF(0, 66, 64, 18); // F
127 kMidiKey2RectMapVertical[4] = QRectF(0, 84, 64, 18); // E
128 kMidiKey2RectMapVertical[3] = QRectF(0, 98, 42, 7); // D#
129 kMidiKey2RectMapVertical[2] = QRectF(0, 102, 64, 24); // D
130 kMidiKey2RectMapVertical[1] = QRectF(0, 122, 42, 7); // C#
131 kMidiKey2RectMapVertical[0] = QRectF(0, 126, 64, 18); // C
133 // kMidiKeyboard2KeyMap, 3th octave
134 kMidiKeyboard2KeyMap[Qt::Key_Z] = 48;
135 kMidiKeyboard2KeyMap[Qt::Key_S] = 49;
136 kMidiKeyboard2KeyMap[Qt::Key_X] = 50;
137 kMidiKeyboard2KeyMap[Qt::Key_D] = 51;
138 kMidiKeyboard2KeyMap[Qt::Key_C] = 52;
139 kMidiKeyboard2KeyMap[Qt::Key_V] = 53;
140 kMidiKeyboard2KeyMap[Qt::Key_G] = 54;
141 kMidiKeyboard2KeyMap[Qt::Key_B] = 55;
142 kMidiKeyboard2KeyMap[Qt::Key_H] = 56;
143 kMidiKeyboard2KeyMap[Qt::Key_N] = 57;
144 kMidiKeyboard2KeyMap[Qt::Key_J] = 58;
145 kMidiKeyboard2KeyMap[Qt::Key_M] = 59;
146 // kMidiKeyboard2KeyMap, 4th octave
147 kMidiKeyboard2KeyMap[Qt::Key_Q] = 60;
148 kMidiKeyboard2KeyMap[Qt::Key_2] = 61;
149 kMidiKeyboard2KeyMap[Qt::Key_W] = 62;
150 kMidiKeyboard2KeyMap[Qt::Key_3] = 63;
151 kMidiKeyboard2KeyMap[Qt::Key_E] = 64;
152 kMidiKeyboard2KeyMap[Qt::Key_R] = 65;
153 kMidiKeyboard2KeyMap[Qt::Key_5] = 66;
154 kMidiKeyboard2KeyMap[Qt::Key_T] = 67;
155 kMidiKeyboard2KeyMap[Qt::Key_6] = 68;
156 kMidiKeyboard2KeyMap[Qt::Key_Y] = 69;
157 kMidiKeyboard2KeyMap[Qt::Key_7] = 70;
158 kMidiKeyboard2KeyMap[Qt::Key_U] = 71;
160 // kBlackNotes
161 kBlackNotes << 1;
162 kBlackNotes << 3;
163 kBlackNotes << 6;
164 kBlackNotes << 8;
165 kBlackNotes << 10;
167 } _pixmapKeyboardInitInit;
168 #endif
170 PixmapKeyboard::PixmapKeyboard(QWidget* parent)
171 : QWidget(parent),
172 fPixmap(""),
173 fPixmapMode(HORIZONTAL),
174 fColorStr("orange"),
175 fFont("Monospace", 7, QFont::Normal),
176 fOctaves(6),
177 fLastMouseNote(-1),
178 fWidth(0),
179 fHeight(0),
180 fMidiMap(kMidiKey2RectMapHorizontal)
182 setCursor(Qt::PointingHandCursor);
183 setMode(HORIZONTAL);
186 void PixmapKeyboard::allNotesOff()
188 fEnabledKeys.clear();
190 emit notesOff();
191 update();
194 void PixmapKeyboard::sendNoteOn(int note, bool sendSignal)
196 if (0 <= note && note <= 127 && ! fEnabledKeys.contains(note))
198 fEnabledKeys.append(note);
200 if (sendSignal)
201 emit noteOn(note);
203 update();
206 if (fEnabledKeys.count() == 1)
207 emit notesOn();
210 void PixmapKeyboard::sendNoteOff(int note, bool sendSignal)
212 if (note >= 0 && note <= 127 && fEnabledKeys.contains(note))
214 fEnabledKeys.removeOne(note);
216 if (sendSignal)
217 emit noteOff(note);
219 update();
222 if (fEnabledKeys.count() == 0)
223 emit notesOff();
226 void PixmapKeyboard::setMode(Orientation mode, Color color)
228 if (color == COLOR_CLASSIC)
230 fColorStr = "classic";
232 else if (color == COLOR_ORANGE)
234 fColorStr = "orange";
236 else
238 qCritical("PixmapKeyboard::setMode(%i, %i) - invalid color", mode, color);
239 return setMode(mode);
242 if (mode == HORIZONTAL)
244 fMidiMap = kMidiKey2RectMapHorizontal;
245 fPixmap.load(QString(":/bitmaps/kbd_h_%1.png").arg(fColorStr));
246 fPixmapMode = HORIZONTAL;
247 fWidth = fPixmap.width();
248 fHeight = fPixmap.height() / 2;
250 else if (mode == VERTICAL)
252 fMidiMap = kMidiKey2RectMapVertical;
253 fPixmap.load(QString(":/bitmaps/kbd_v_%1.png").arg(fColorStr));
254 fPixmapMode = VERTICAL;
255 fWidth = fPixmap.width() / 2;
256 fHeight = fPixmap.height();
258 else
260 qCritical("PixmapKeyboard::setMode(%i, %i) - invalid mode", mode, color);
261 return setMode(HORIZONTAL);
264 setOctaves(fOctaves);
267 void PixmapKeyboard::setOctaves(int octaves)
269 Q_ASSERT(octaves >= 1 && octaves <= 10);
271 if (octaves < 1)
272 octaves = 1;
273 else if (octaves > 10)
274 octaves = 10;
276 fOctaves = octaves;
278 if (fPixmapMode == HORIZONTAL)
280 setMinimumSize(fWidth * fOctaves, fHeight);
281 setMaximumSize(fWidth * fOctaves, fHeight);
283 else if (fPixmapMode == VERTICAL)
285 setMinimumSize(fWidth, fHeight * fOctaves);
286 setMaximumSize(fWidth, fHeight * fOctaves);
289 update();
292 void PixmapKeyboard::handleMousePos(const QPoint& pos)
294 int note, octave;
295 QPointF keyPos;
297 if (fPixmapMode == HORIZONTAL)
299 if (pos.x() < 0 or pos.x() > fOctaves * 144)
300 return;
301 int posX = pos.x() - 1;
302 octave = posX / fWidth;
303 keyPos = QPointF(posX % fWidth, pos.y());
305 else if (fPixmapMode == VERTICAL)
307 if (pos.y() < 0 or pos.y() > fOctaves * 144)
308 return;
309 int posY = pos.y() - 1;
310 octave = fOctaves - posY / fHeight;
311 keyPos = QPointF(pos.x(), posY % fHeight);
313 else
314 return;
316 if (fMidiMap[1].contains(keyPos)) // C#
317 note = 1;
318 else if (fMidiMap[3].contains(keyPos)) // D#
319 note = 3;
320 else if (fMidiMap[6].contains(keyPos)) // F#
321 note = 6;
322 else if (fMidiMap[8].contains(keyPos)) // G#
323 note = 8;
324 else if (fMidiMap[10].contains(keyPos))// A#
325 note = 10;
326 else if (fMidiMap[0].contains(keyPos)) // C
327 note = 0;
328 else if (fMidiMap[2].contains(keyPos)) // D
329 note = 2;
330 else if (fMidiMap[4].contains(keyPos)) // E
331 note = 4;
332 else if (fMidiMap[5].contains(keyPos)) // F
333 note = 5;
334 else if (fMidiMap[7].contains(keyPos)) // G
335 note = 7;
336 else if (fMidiMap[9].contains(keyPos)) // A
337 note = 9;
338 else if (fMidiMap[11].contains(keyPos))// B
339 note = 11;
340 else
341 note = -1;
343 if (note != -1)
345 note += octave * 12;
347 if (fLastMouseNote != note)
349 sendNoteOff(fLastMouseNote);
350 sendNoteOn(note);
353 else if (fLastMouseNote != -1)
354 sendNoteOff(fLastMouseNote);
356 fLastMouseNote = note;
359 void PixmapKeyboard::keyPressEvent(QKeyEvent* event)
361 if (! event->isAutoRepeat())
363 int qKey = event->key();
364 std::map<int, int>::const_iterator it = kMidiKeyboard2KeyMap.find(qKey);
365 if (it != kMidiKeyboard2KeyMap.end())
366 sendNoteOn(it->second);
368 QWidget::keyPressEvent(event);
371 void PixmapKeyboard::keyReleaseEvent(QKeyEvent* event)
373 if (! event->isAutoRepeat())
375 int qKey = event->key();
376 std::map<int, int>::const_iterator it = kMidiKeyboard2KeyMap.find(qKey);
377 if (it != kMidiKeyboard2KeyMap.end())
378 sendNoteOff(it->second);
380 QWidget::keyReleaseEvent(event);
383 void PixmapKeyboard::mousePressEvent(QMouseEvent* event)
385 fLastMouseNote = -1;
386 handleMousePos(event->pos());
387 setFocus();
388 QWidget::mousePressEvent(event);
391 void PixmapKeyboard::mouseMoveEvent(QMouseEvent* event)
393 handleMousePos(event->pos());
394 QWidget::mouseMoveEvent(event);
397 void PixmapKeyboard::mouseReleaseEvent(QMouseEvent* event)
399 if (fLastMouseNote != -1)
401 sendNoteOff(fLastMouseNote);
402 fLastMouseNote = -1;
404 QWidget::mouseReleaseEvent(event);
407 void PixmapKeyboard::paintEvent(QPaintEvent* event)
409 QPainter painter(this);
410 event->accept();
412 // -------------------------------------------------------------
413 // Paint clean keys (as background)
415 for (int octave=0; octave < fOctaves; ++octave)
417 QRectF target;
419 if (fPixmapMode == HORIZONTAL)
420 target = QRectF(fWidth * octave, 0, fWidth, fHeight);
421 else if (fPixmapMode == VERTICAL)
422 target = QRectF(0, fHeight * octave, fWidth, fHeight);
423 else
424 return;
426 QRectF source = QRectF(0, 0, fWidth, fHeight);
427 painter.drawPixmap(target, fPixmap, source);
430 // -------------------------------------------------------------
431 // Paint (white) pressed keys
433 bool paintedWhite = false;
435 for (int i=0, count=fEnabledKeys.count(); i < count; ++i)
437 int octave, note = fEnabledKeys[i];
438 const QRectF& pos(_getRectFromMidiNote(note));
440 if (_isNoteBlack(note))
441 continue;
443 if (note < 12)
444 octave = 0;
445 else if (note < 24)
446 octave = 1;
447 else if (note < 36)
448 octave = 2;
449 else if (note < 48)
450 octave = 3;
451 else if (note < 60)
452 octave = 4;
453 else if (note < 72)
454 octave = 5;
455 else if (note < 84)
456 octave = 6;
457 else if (note < 96)
458 octave = 7;
459 else if (note < 108)
460 octave = 8;
461 else if (note < 120)
462 octave = 9;
463 else if (note < 132)
464 octave = 10;
465 else
466 // cannot paint this note
467 continue;
469 if (fPixmapMode == VERTICAL)
470 octave = fOctaves - octave - 1;
472 QRectF target, source;
474 if (fPixmapMode == HORIZONTAL)
476 target = QRectF(pos.x() + (fWidth * octave), 0, pos.width(), pos.height());
477 source = QRectF(pos.x(), fHeight, pos.width(), pos.height());
479 else if (fPixmapMode == VERTICAL)
481 target = QRectF(pos.x(), pos.y() + (fHeight * octave), pos.width(), pos.height());
482 source = QRectF(fWidth, pos.y(), pos.width(), pos.height());
484 else
485 return;
487 paintedWhite = true;
488 painter.drawPixmap(target, fPixmap, source);
491 // -------------------------------------------------------------
492 // Clear white keys border
494 if (paintedWhite)
496 for (int octave=0; octave < fOctaves; ++octave)
498 foreach (int note, kBlackNotes)
500 QRectF target, source;
501 const QRectF& pos(_getRectFromMidiNote(note));
503 if (fPixmapMode == HORIZONTAL)
505 target = QRectF(pos.x() + (fWidth * octave), 0, pos.width(), pos.height());
506 source = QRectF(pos.x(), 0, pos.width(), pos.height());
508 else if (fPixmapMode == VERTICAL)
510 target = QRectF(pos.x(), pos.y() + (fHeight * octave), pos.width(), pos.height());
511 source = QRectF(0, pos.y(), pos.width(), pos.height());
513 else
514 return;
516 painter.drawPixmap(target, fPixmap, source);
521 // -------------------------------------------------------------
522 // Paint (black) pressed keys
524 for (int i=0, count=fEnabledKeys.count(); i < count; ++i)
526 int octave, note = fEnabledKeys[i];
527 const QRectF& pos(_getRectFromMidiNote(note));
529 if (! _isNoteBlack(note))
530 continue;
532 if (note < 12)
533 octave = 0;
534 else if (note < 24)
535 octave = 1;
536 else if (note < 36)
537 octave = 2;
538 else if (note < 48)
539 octave = 3;
540 else if (note < 60)
541 octave = 4;
542 else if (note < 72)
543 octave = 5;
544 else if (note < 84)
545 octave = 6;
546 else if (note < 96)
547 octave = 7;
548 else if (note < 108)
549 octave = 8;
550 else if (note < 120)
551 octave = 9;
552 else if (note < 132)
553 octave = 10;
554 else
555 // cannot paint this note
556 continue;
558 if (fPixmapMode == VERTICAL)
559 octave = fOctaves - octave - 1;
561 QRectF target, source;
563 if (fPixmapMode == HORIZONTAL)
565 target = QRectF(pos.x() + (fWidth * octave), 0, pos.width(), pos.height());
566 source = QRectF(pos.x(), fHeight, pos.width(), pos.height());
568 else if (fPixmapMode == VERTICAL)
570 target = QRectF(pos.x(), pos.y() + (fHeight * octave), pos.width(), pos.height());
571 source = QRectF(fWidth, pos.y(), pos.width(), pos.height());
573 else
574 return;
576 painter.drawPixmap(target, fPixmap, source);
579 // Paint C-number note info
580 painter.setFont(fFont);
581 painter.setPen(Qt::black);
583 for (int i=0; i < fOctaves; ++i)
585 if (fPixmapMode == HORIZONTAL)
586 painter.drawText(i * 144, 48, 18, 18, Qt::AlignCenter, QString("C%1").arg(i-1));
587 else if (fPixmapMode == VERTICAL)
588 painter.drawText(45, (fOctaves * 144) - (i * 144) - 16, 18, 18, Qt::AlignCenter, QString("C%1").arg(i-1));
592 bool PixmapKeyboard::_isNoteBlack(int note) const
594 const int baseNote = note % 12;
595 return kBlackNotes.contains(baseNote);
598 const QRectF& PixmapKeyboard::_getRectFromMidiNote(int note) const
600 const int baseNote = note % 12;
601 return fMidiMap[baseNote];