/* * Pixmap Keyboard, a custom Qt4 widget * Copyright (C) 2011-2013 Filipe Coelho * * 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 #include #include #include #ifndef HAVE_CPP11_SUPPORT static std::map kMidiKey2RectMapHorizontal; #else static std::map 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 kMidiKey2RectMapVertical; #else static std::map 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 kMidiKeyboard2KeyMap; #else static const std::map 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 kBlackNotes; #else static const QVector 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::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::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]; }