|
- /*
- * Pixmap Keyboard, a custom Qt4 widget
- * Copyright (C) 2011-2013 Filipe Coelho <falktx@falktx.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * For a full copy of the GNU General Public License see the GPL.txt file
- */
-
- #include "pixmapkeyboard.hpp"
-
- #include <QtCore/QTimer>
- #include <QtGui/QKeyEvent>
- #include <QtGui/QMouseEvent>
- #include <QtGui/QPainter>
-
- #ifndef HAVE_CPP11_SUPPORT
- static std::map<int, QRectF> kMidiKey2RectMapHorizontal;
- #else
- static std::map<int, QRectF> kMidiKey2RectMapHorizontal = {
- {0, QRectF(0, 0, 18, 64)}, // C
- {1, QRectF(13, 0, 11, 42)}, // C#
- {2, QRectF(18, 0, 25, 64)}, // D
- {3, QRectF(37, 0, 11, 42)}, // D#
- {4, QRectF(42, 0, 18, 64)}, // E
- {5, QRectF(60, 0, 18, 64)}, // F
- {6, QRectF(73, 0, 11, 42)}, // F#
- {7, QRectF(78, 0, 25, 64)}, // G
- {8, QRectF(97, 0, 11, 42)}, // G#
- {9, QRectF(102, 0, 25, 64)}, // A
- {10, QRectF(121, 0, 11, 42)}, // A#
- {11, QRectF(126, 0, 18, 64)} // B
- };
- #endif
-
- #ifndef HAVE_CPP11_SUPPORT
- static std::map<int, QRectF> kMidiKey2RectMapVertical;
- #else
- static std::map<int, QRectF> kMidiKey2RectMapVertical = {
- {11, QRectF(0, 0, 64, 18)}, // B
- {10, QRectF(0, 14, 42, 7)}, // A#
- {9, QRectF(0, 18, 64, 24)}, // A
- {8, QRectF(0, 38, 42, 7)}, // G#
- {7, QRectF(0, 42, 64, 24)}, // G
- {6, QRectF(0, 62, 42, 7)}, // F#
- {5, QRectF(0, 66, 64, 18)}, // F
- {4, QRectF(0, 84, 64, 18)}, // E
- {3, QRectF(0, 98, 42, 7)}, // D#
- {2, QRectF(0, 102, 64, 24)}, // D
- {1, QRectF(0, 122, 42, 7)}, // C#
- {0, QRectF(0, 126, 64, 18)} // C
- };
- #endif
-
- #ifndef HAVE_CPP11_SUPPORT
- static std::map<int, int> kMidiKeyboard2KeyMap;
- #else
- static const std::map<int, int> kMidiKeyboard2KeyMap = {
- // 3th octave
- {Qt::Key_Z, 48},
- {Qt::Key_S, 49},
- {Qt::Key_X, 50},
- {Qt::Key_D, 51},
- {Qt::Key_C, 52},
- {Qt::Key_V, 53},
- {Qt::Key_G, 54},
- {Qt::Key_B, 55},
- {Qt::Key_H, 56},
- {Qt::Key_N, 57},
- {Qt::Key_J, 58},
- {Qt::Key_M, 59},
- // 4th octave
- {Qt::Key_Q, 60},
- {Qt::Key_2, 61},
- {Qt::Key_W, 62},
- {Qt::Key_3, 63},
- {Qt::Key_E, 64},
- {Qt::Key_R, 65},
- {Qt::Key_5, 66},
- {Qt::Key_T, 67},
- {Qt::Key_6, 68},
- {Qt::Key_Y, 69},
- {Qt::Key_7, 70},
- {Qt::Key_U, 71}
- };
- #endif
-
- #ifndef HAVE_CPP11_SUPPORT
- static QVector<int> kBlackNotes;
- #else
- static const QVector<int> kBlackNotes = {1, 3, 6, 8, 10};
- #endif
-
- #ifndef HAVE_CPP11_SUPPORT
- static const struct PixmapKeyboardInit {
- PixmapKeyboardInit() {
- // kMidiKey2RectMapHorizontal
- kMidiKey2RectMapHorizontal[0] = QRectF(0, 0, 18, 64); // C
- kMidiKey2RectMapHorizontal[1] = QRectF(13, 0, 11, 42); // C#
- kMidiKey2RectMapHorizontal[2] = QRectF(18, 0, 25, 64); // D
- kMidiKey2RectMapHorizontal[3] = QRectF(37, 0, 11, 42); // D#
- kMidiKey2RectMapHorizontal[4] = QRectF(42, 0, 18, 64); // E
- kMidiKey2RectMapHorizontal[5] = QRectF(60, 0, 18, 64); // F
- kMidiKey2RectMapHorizontal[6] = QRectF(73, 0, 11, 42); // F#
- kMidiKey2RectMapHorizontal[7] = QRectF(78, 0, 25, 64); // G
- kMidiKey2RectMapHorizontal[8] = QRectF(97, 0, 11, 42); // G#
- kMidiKey2RectMapHorizontal[9] = QRectF(102, 0, 25, 64); // A
- kMidiKey2RectMapHorizontal[10] = QRectF(121, 0, 11, 42); // A#
- kMidiKey2RectMapHorizontal[11] = QRectF(126, 0, 18, 64); // B
-
- // kMidiKey2RectMapVertical
- kMidiKey2RectMapVertical[11] = QRectF(0, 0, 64, 18); // B
- kMidiKey2RectMapVertical[10] = QRectF(0, 14, 42, 7); // A#
- kMidiKey2RectMapVertical[9] = QRectF(0, 18, 64, 24); // A
- kMidiKey2RectMapVertical[8] = QRectF(0, 38, 42, 7); // G#
- kMidiKey2RectMapVertical[7] = QRectF(0, 42, 64, 24); // G
- kMidiKey2RectMapVertical[6] = QRectF(0, 62, 42, 7); // F#
- kMidiKey2RectMapVertical[5] = QRectF(0, 66, 64, 18); // F
- kMidiKey2RectMapVertical[4] = QRectF(0, 84, 64, 18); // E
- kMidiKey2RectMapVertical[3] = QRectF(0, 98, 42, 7); // D#
- kMidiKey2RectMapVertical[2] = QRectF(0, 102, 64, 24); // D
- kMidiKey2RectMapVertical[1] = QRectF(0, 122, 42, 7); // C#
- kMidiKey2RectMapVertical[0] = QRectF(0, 126, 64, 18); // C
-
- // kMidiKeyboard2KeyMap, 3th octave
- kMidiKeyboard2KeyMap[Qt::Key_Z] = 48;
- kMidiKeyboard2KeyMap[Qt::Key_S] = 49;
- kMidiKeyboard2KeyMap[Qt::Key_X] = 50;
- kMidiKeyboard2KeyMap[Qt::Key_D] = 51;
- kMidiKeyboard2KeyMap[Qt::Key_C] = 52;
- kMidiKeyboard2KeyMap[Qt::Key_V] = 53;
- kMidiKeyboard2KeyMap[Qt::Key_G] = 54;
- kMidiKeyboard2KeyMap[Qt::Key_B] = 55;
- kMidiKeyboard2KeyMap[Qt::Key_H] = 56;
- kMidiKeyboard2KeyMap[Qt::Key_N] = 57;
- kMidiKeyboard2KeyMap[Qt::Key_J] = 58;
- kMidiKeyboard2KeyMap[Qt::Key_M] = 59;
- // kMidiKeyboard2KeyMap, 4th octave
- kMidiKeyboard2KeyMap[Qt::Key_Q] = 60;
- kMidiKeyboard2KeyMap[Qt::Key_2] = 61;
- kMidiKeyboard2KeyMap[Qt::Key_W] = 62;
- kMidiKeyboard2KeyMap[Qt::Key_3] = 63;
- kMidiKeyboard2KeyMap[Qt::Key_E] = 64;
- kMidiKeyboard2KeyMap[Qt::Key_R] = 65;
- kMidiKeyboard2KeyMap[Qt::Key_5] = 66;
- kMidiKeyboard2KeyMap[Qt::Key_T] = 67;
- kMidiKeyboard2KeyMap[Qt::Key_6] = 68;
- kMidiKeyboard2KeyMap[Qt::Key_Y] = 69;
- kMidiKeyboard2KeyMap[Qt::Key_7] = 70;
- kMidiKeyboard2KeyMap[Qt::Key_U] = 71;
-
- // kBlackNotes
- kBlackNotes << 1;
- kBlackNotes << 3;
- kBlackNotes << 6;
- kBlackNotes << 8;
- kBlackNotes << 10;
- }
- } _pixmapKeyboardInitInit;
- #endif
-
- PixmapKeyboard::PixmapKeyboard(QWidget* parent)
- : QWidget(parent),
- fPixmap(""),
- fPixmapMode(HORIZONTAL),
- fColorStr("orange"),
- fFont("Monospace", 7, QFont::Normal),
- fOctaves(6),
- fLastMouseNote(-1),
- fWidth(0),
- fHeight(0),
- fMidiMap(kMidiKey2RectMapHorizontal)
- {
- setCursor(Qt::PointingHandCursor);
- setMode(HORIZONTAL);
- }
-
- void PixmapKeyboard::allNotesOff()
- {
- fEnabledKeys.clear();
-
- emit notesOff();
- update();
- }
-
- void PixmapKeyboard::sendNoteOn(int note, bool sendSignal)
- {
- if (0 <= note && note <= 127 && ! fEnabledKeys.contains(note))
- {
- fEnabledKeys.append(note);
-
- if (sendSignal)
- emit noteOn(note);
-
- update();
- }
-
- if (fEnabledKeys.count() == 1)
- emit notesOn();
- }
-
- void PixmapKeyboard::sendNoteOff(int note, bool sendSignal)
- {
- if (note >= 0 && note <= 127 && fEnabledKeys.contains(note))
- {
- fEnabledKeys.removeOne(note);
-
- if (sendSignal)
- emit noteOff(note);
-
- update();
- }
-
- if (fEnabledKeys.count() == 0)
- emit notesOff();
- }
-
- void PixmapKeyboard::setMode(Orientation mode, Color color)
- {
- if (color == COLOR_CLASSIC)
- {
- fColorStr = "classic";
- }
- else if (color == COLOR_ORANGE)
- {
- fColorStr = "orange";
- }
- else
- {
- qCritical("PixmapKeyboard::setMode(%i, %i) - invalid color", mode, color);
- return setMode(mode);
- }
-
- if (mode == HORIZONTAL)
- {
- fMidiMap = kMidiKey2RectMapHorizontal;
- fPixmap.load(QString(":/bitmaps/kbd_h_%1.png").arg(fColorStr));
- fPixmapMode = HORIZONTAL;
- fWidth = fPixmap.width();
- fHeight = fPixmap.height() / 2;
- }
- else if (mode == VERTICAL)
- {
- fMidiMap = kMidiKey2RectMapVertical;
- fPixmap.load(QString(":/bitmaps/kbd_v_%1.png").arg(fColorStr));
- fPixmapMode = VERTICAL;
- fWidth = fPixmap.width() / 2;
- fHeight = fPixmap.height();
- }
- else
- {
- qCritical("PixmapKeyboard::setMode(%i, %i) - invalid mode", mode, color);
- return setMode(HORIZONTAL);
- }
-
- setOctaves(fOctaves);
- }
-
- void PixmapKeyboard::setOctaves(int octaves)
- {
- Q_ASSERT(octaves >= 1 && octaves <= 10);
-
- if (octaves < 1)
- octaves = 1;
- else if (octaves > 10)
- octaves = 10;
-
- fOctaves = octaves;
-
- if (fPixmapMode == HORIZONTAL)
- {
- setMinimumSize(fWidth * fOctaves, fHeight);
- setMaximumSize(fWidth * fOctaves, fHeight);
- }
- else if (fPixmapMode == VERTICAL)
- {
- setMinimumSize(fWidth, fHeight * fOctaves);
- setMaximumSize(fWidth, fHeight * fOctaves);
- }
-
- update();
- }
-
- void PixmapKeyboard::handleMousePos(const QPoint& pos)
- {
- int note, octave;
- QPointF keyPos;
-
- if (fPixmapMode == HORIZONTAL)
- {
- if (pos.x() < 0 or pos.x() > fOctaves * 144)
- return;
- int posX = pos.x() - 1;
- octave = posX / fWidth;
- keyPos = QPointF(posX % fWidth, pos.y());
- }
- else if (fPixmapMode == VERTICAL)
- {
- if (pos.y() < 0 or pos.y() > fOctaves * 144)
- return;
- int posY = pos.y() - 1;
- octave = fOctaves - posY / fHeight;
- keyPos = QPointF(pos.x(), posY % fHeight);
- }
- else
- return;
-
- if (fMidiMap[1].contains(keyPos)) // C#
- note = 1;
- else if (fMidiMap[3].contains(keyPos)) // D#
- note = 3;
- else if (fMidiMap[6].contains(keyPos)) // F#
- note = 6;
- else if (fMidiMap[8].contains(keyPos)) // G#
- note = 8;
- else if (fMidiMap[10].contains(keyPos))// A#
- note = 10;
- else if (fMidiMap[0].contains(keyPos)) // C
- note = 0;
- else if (fMidiMap[2].contains(keyPos)) // D
- note = 2;
- else if (fMidiMap[4].contains(keyPos)) // E
- note = 4;
- else if (fMidiMap[5].contains(keyPos)) // F
- note = 5;
- else if (fMidiMap[7].contains(keyPos)) // G
- note = 7;
- else if (fMidiMap[9].contains(keyPos)) // A
- note = 9;
- else if (fMidiMap[11].contains(keyPos))// B
- note = 11;
- else
- note = -1;
-
- if (note != -1)
- {
- note += octave * 12;
-
- if (fLastMouseNote != note)
- {
- sendNoteOff(fLastMouseNote);
- sendNoteOn(note);
- }
- }
- else if (fLastMouseNote != -1)
- sendNoteOff(fLastMouseNote);
-
- fLastMouseNote = note;
- }
-
- void PixmapKeyboard::keyPressEvent(QKeyEvent* event)
- {
- if (! event->isAutoRepeat())
- {
- int qKey = event->key();
- std::map<int, int>::const_iterator it = kMidiKeyboard2KeyMap.find(qKey);
- if (it != kMidiKeyboard2KeyMap.end())
- sendNoteOn(it->second);
- }
- QWidget::keyPressEvent(event);
- }
-
- void PixmapKeyboard::keyReleaseEvent(QKeyEvent* event)
- {
- if (! event->isAutoRepeat())
- {
- int qKey = event->key();
- std::map<int, int>::const_iterator it = kMidiKeyboard2KeyMap.find(qKey);
- if (it != kMidiKeyboard2KeyMap.end())
- sendNoteOff(it->second);
- }
- QWidget::keyReleaseEvent(event);
- }
-
- void PixmapKeyboard::mousePressEvent(QMouseEvent* event)
- {
- fLastMouseNote = -1;
- handleMousePos(event->pos());
- setFocus();
- QWidget::mousePressEvent(event);
- }
-
- void PixmapKeyboard::mouseMoveEvent(QMouseEvent* event)
- {
- handleMousePos(event->pos());
- QWidget::mouseMoveEvent(event);
- }
-
- void PixmapKeyboard::mouseReleaseEvent(QMouseEvent* event)
- {
- if (fLastMouseNote != -1)
- {
- sendNoteOff(fLastMouseNote);
- fLastMouseNote = -1;
- }
- QWidget::mouseReleaseEvent(event);
- }
-
- void PixmapKeyboard::paintEvent(QPaintEvent* event)
- {
- QPainter painter(this);
- event->accept();
-
- // -------------------------------------------------------------
- // Paint clean keys (as background)
-
- for (int octave=0; octave < fOctaves; ++octave)
- {
- QRectF target;
-
- if (fPixmapMode == HORIZONTAL)
- target = QRectF(fWidth * octave, 0, fWidth, fHeight);
- else if (fPixmapMode == VERTICAL)
- target = QRectF(0, fHeight * octave, fWidth, fHeight);
- else
- return;
-
- QRectF source = QRectF(0, 0, fWidth, fHeight);
- painter.drawPixmap(target, fPixmap, source);
- }
-
- // -------------------------------------------------------------
- // Paint (white) pressed keys
-
- bool paintedWhite = false;
-
- for (int i=0, count=fEnabledKeys.count(); i < count; ++i)
- {
- int octave, note = fEnabledKeys[i];
- const QRectF& pos(_getRectFromMidiNote(note));
-
- if (_isNoteBlack(note))
- continue;
-
- if (note < 12)
- octave = 0;
- else if (note < 24)
- octave = 1;
- else if (note < 36)
- octave = 2;
- else if (note < 48)
- octave = 3;
- else if (note < 60)
- octave = 4;
- else if (note < 72)
- octave = 5;
- else if (note < 84)
- octave = 6;
- else if (note < 96)
- octave = 7;
- else if (note < 108)
- octave = 8;
- else if (note < 120)
- octave = 9;
- else if (note < 132)
- octave = 10;
- else
- // cannot paint this note
- continue;
-
- if (fPixmapMode == VERTICAL)
- octave = fOctaves - octave - 1;
-
- QRectF target, source;
-
- if (fPixmapMode == HORIZONTAL)
- {
- target = QRectF(pos.x() + (fWidth * octave), 0, pos.width(), pos.height());
- source = QRectF(pos.x(), fHeight, pos.width(), pos.height());
- }
- else if (fPixmapMode == VERTICAL)
- {
- target = QRectF(pos.x(), pos.y() + (fHeight * octave), pos.width(), pos.height());
- source = QRectF(fWidth, pos.y(), pos.width(), pos.height());
- }
- else
- return;
-
- paintedWhite = true;
- painter.drawPixmap(target, fPixmap, source);
- }
-
- // -------------------------------------------------------------
- // Clear white keys border
-
- if (paintedWhite)
- {
- for (int octave=0; octave < fOctaves; ++octave)
- {
- foreach (int note, kBlackNotes)
- {
- QRectF target, source;
- const QRectF& pos(_getRectFromMidiNote(note));
-
- if (fPixmapMode == HORIZONTAL)
- {
- target = QRectF(pos.x() + (fWidth * octave), 0, pos.width(), pos.height());
- source = QRectF(pos.x(), 0, pos.width(), pos.height());
- }
- else if (fPixmapMode == VERTICAL)
- {
- target = QRectF(pos.x(), pos.y() + (fHeight * octave), pos.width(), pos.height());
- source = QRectF(0, pos.y(), pos.width(), pos.height());
- }
- else
- return;
-
- painter.drawPixmap(target, fPixmap, source);
- }
- }
- }
-
- // -------------------------------------------------------------
- // Paint (black) pressed keys
-
- for (int i=0, count=fEnabledKeys.count(); i < count; ++i)
- {
- int octave, note = fEnabledKeys[i];
- const QRectF& pos(_getRectFromMidiNote(note));
-
- if (! _isNoteBlack(note))
- continue;
-
- if (note < 12)
- octave = 0;
- else if (note < 24)
- octave = 1;
- else if (note < 36)
- octave = 2;
- else if (note < 48)
- octave = 3;
- else if (note < 60)
- octave = 4;
- else if (note < 72)
- octave = 5;
- else if (note < 84)
- octave = 6;
- else if (note < 96)
- octave = 7;
- else if (note < 108)
- octave = 8;
- else if (note < 120)
- octave = 9;
- else if (note < 132)
- octave = 10;
- else
- // cannot paint this note
- continue;
-
- if (fPixmapMode == VERTICAL)
- octave = fOctaves - octave - 1;
-
- QRectF target, source;
-
- if (fPixmapMode == HORIZONTAL)
- {
- target = QRectF(pos.x() + (fWidth * octave), 0, pos.width(), pos.height());
- source = QRectF(pos.x(), fHeight, pos.width(), pos.height());
- }
- else if (fPixmapMode == VERTICAL)
- {
- target = QRectF(pos.x(), pos.y() + (fHeight * octave), pos.width(), pos.height());
- source = QRectF(fWidth, pos.y(), pos.width(), pos.height());
- }
- else
- return;
-
- painter.drawPixmap(target, fPixmap, source);
- }
-
- // Paint C-number note info
- painter.setFont(fFont);
- painter.setPen(Qt::black);
-
- for (int i=0; i < fOctaves; ++i)
- {
- if (fPixmapMode == HORIZONTAL)
- painter.drawText(i * 144, 48, 18, 18, Qt::AlignCenter, QString("C%1").arg(i-1));
- else if (fPixmapMode == VERTICAL)
- painter.drawText(45, (fOctaves * 144) - (i * 144) - 16, 18, 18, Qt::AlignCenter, QString("C%1").arg(i-1));
- }
- }
-
- bool PixmapKeyboard::_isNoteBlack(int note) const
- {
- const int baseNote = note % 12;
- return kBlackNotes.contains(baseNote);
- }
-
- const QRectF& PixmapKeyboard::_getRectFromMidiNote(int note) const
- {
- const int baseNote = note % 12;
- return fMidiMap[baseNote];
- }
|