|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524 |
- /*
- * Pixmap Keyboard, a custom Qt4 widget
- * Copyright (C) 2011-2012 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 COPYING file
- */
-
- #include "pixmapkeyboard.hpp"
-
- #include <QtCore/QMap>
- #include <QtCore/QTimer>
- #include <QtGui/QKeyEvent>
- #include <QtGui/QMouseEvent>
- #include <QtGui/QPainter>
-
- QMap<int, QRectF> midi_key2rect_map_horizontal;
- QMap<int, QRectF> midi_key2rect_map_vertical;
- QMap<int, int> midi_keyboard2key_map;
- QVector<int> blackNotes;
-
- static bool pixmapkeyboard_initiated = false;
- void pixmapkeyboard_init()
- {
- if (pixmapkeyboard_initiated)
- return;
-
- pixmapkeyboard_initiated = true;
-
- // midi_key2rect_map_horizontal ------
- midi_key2rect_map_horizontal[0] = QRectF(0, 0, 18, 64); // C
- midi_key2rect_map_horizontal[1] = QRectF(13, 0, 11, 42); // C#
- midi_key2rect_map_horizontal[2] = QRectF(18, 0, 25, 64); // D
- midi_key2rect_map_horizontal[3] = QRectF(37, 0, 11, 42); // D#
- midi_key2rect_map_horizontal[4] = QRectF(42, 0, 18, 64); // E
- midi_key2rect_map_horizontal[5] = QRectF(60, 0, 18, 64); // F
- midi_key2rect_map_horizontal[6] = QRectF(73, 0, 11, 42); // F#
- midi_key2rect_map_horizontal[7] = QRectF(78, 0, 25, 64); // G
- midi_key2rect_map_horizontal[8] = QRectF(97, 0, 11, 42); // G#
- midi_key2rect_map_horizontal[9] = QRectF(102, 0, 25, 64); // A
- midi_key2rect_map_horizontal[10] = QRectF(121, 0, 11, 42); // A#
- midi_key2rect_map_horizontal[11] = QRectF(126, 0, 18, 64); // B
-
- // midi_key2rect_map_vertical --------
- midi_key2rect_map_vertical[11] = QRectF(0, 0, 64, 18); // B
- midi_key2rect_map_vertical[10] = QRectF(0, 14, 42, 7); // A#
- midi_key2rect_map_vertical[9] = QRectF(0, 18, 64, 24); // A
- midi_key2rect_map_vertical[8] = QRectF(0, 38, 42, 7); // G#
- midi_key2rect_map_vertical[7] = QRectF(0, 42, 64, 24); // G
- midi_key2rect_map_vertical[6] = QRectF(0, 62, 42, 7); // F#
- midi_key2rect_map_vertical[5] = QRectF(0, 66, 64, 18); // F
- midi_key2rect_map_vertical[4] = QRectF(0, 84, 64, 18); // E
- midi_key2rect_map_vertical[3] = QRectF(0, 98, 42, 7); // D#
- midi_key2rect_map_vertical[2] = QRectF(0, 102, 64, 24); // D
- midi_key2rect_map_vertical[1] = QRectF(0, 122, 42, 7); // C#
- midi_key2rect_map_vertical[0] = QRectF(0, 126, 64, 18); // C
-
- // midi_keyboard2key_map -------------
- // 3th octave
- midi_keyboard2key_map[Qt::Key_Z] = 48;
- midi_keyboard2key_map[Qt::Key_S] = 49;
- midi_keyboard2key_map[Qt::Key_X] = 50;
- midi_keyboard2key_map[Qt::Key_D] = 51;
- midi_keyboard2key_map[Qt::Key_C] = 52;
- midi_keyboard2key_map[Qt::Key_V] = 53;
- midi_keyboard2key_map[Qt::Key_G] = 54;
- midi_keyboard2key_map[Qt::Key_B] = 55;
- midi_keyboard2key_map[Qt::Key_H] = 56;
- midi_keyboard2key_map[Qt::Key_N] = 57;
- midi_keyboard2key_map[Qt::Key_J] = 58;
- midi_keyboard2key_map[Qt::Key_M] = 59;
- // 4th octave
- midi_keyboard2key_map[Qt::Key_Q] = 60;
- midi_keyboard2key_map[Qt::Key_2] = 61;
- midi_keyboard2key_map[Qt::Key_W] = 62;
- midi_keyboard2key_map[Qt::Key_3] = 63;
- midi_keyboard2key_map[Qt::Key_E] = 64;
- midi_keyboard2key_map[Qt::Key_R] = 65;
- midi_keyboard2key_map[Qt::Key_5] = 66;
- midi_keyboard2key_map[Qt::Key_T] = 67;
- midi_keyboard2key_map[Qt::Key_6] = 68;
- midi_keyboard2key_map[Qt::Key_Y] = 69;
- midi_keyboard2key_map[Qt::Key_7] = 70;
- midi_keyboard2key_map[Qt::Key_U] = 71;
-
- blackNotes << 1;
- blackNotes << 3;
- blackNotes << 6;
- blackNotes << 8;
- blackNotes << 10;
- }
-
- PixmapKeyboard::PixmapKeyboard(QWidget* parent)
- : QWidget(parent),
- m_font("Monospace", 8, QFont::Normal)
- {
- pixmapkeyboard_init();
-
- m_octaves = 6;
- m_lastMouseNote = -1;
- m_needsUpdate = false;
-
- setCursor(Qt::PointingHandCursor);
- setMode(HORIZONTAL);
- }
-
- void PixmapKeyboard::allNotesOff()
- {
- m_enabledKeys.clear();
-
- m_needsUpdate = true;
- QTimer::singleShot(0, this, SLOT(updateOnce()));
-
- emit notesOff();
- }
-
- void PixmapKeyboard::sendNoteOn(int note, bool sendSignal)
- {
- if (0 <= note && note <= 127 && ! m_enabledKeys.contains(note))
- {
- m_enabledKeys.append(note);
- if (sendSignal)
- emit noteOn(note);
-
- m_needsUpdate = true;
- QTimer::singleShot(0, this, SLOT(updateOnce()));
- }
-
- if (m_enabledKeys.count() == 1)
- emit notesOn();
- }
-
- void PixmapKeyboard::sendNoteOff(int note, bool sendSignal)
- {
- if (note >= 0 && note <= 127 && m_enabledKeys.contains(note))
- {
- m_enabledKeys.removeOne(note);
- if (sendSignal)
- emit noteOff(note);
-
- m_needsUpdate = true;
- QTimer::singleShot(0, this, SLOT(updateOnce()));
- }
-
- if (m_enabledKeys.count() == 0)
- emit notesOff();
- }
-
- void PixmapKeyboard::setMode(Orientation mode, Color color)
- {
- if (color == COLOR_CLASSIC)
- {
- m_colorStr = "classic";
- }
- else if (color == COLOR_ORANGE)
- {
- m_colorStr = "orange";
- }
- else
- {
- qCritical("PixmapKeyboard::setMode(%i, %i) - invalid color", mode, color);
- return setMode(mode);
- }
-
- if (mode == HORIZONTAL)
- {
- m_midi_map = &midi_key2rect_map_horizontal;
- m_pixmap.load(QString(":/bitmaps/kbd_h_%1.png").arg(m_colorStr));
- m_pixmap_mode = HORIZONTAL;
- p_width = m_pixmap.width();
- p_height = m_pixmap.height() / 2;
- }
- else if (mode == VERTICAL)
- {
- m_midi_map = &midi_key2rect_map_vertical;
- m_pixmap.load(QString(":/bitmaps/kbd_v_%1.png").arg(m_colorStr));
- m_pixmap_mode = VERTICAL;
- p_width = m_pixmap.width() / 2;
- p_height = m_pixmap.height();
- }
- else
- {
- qCritical("PixmapKeyboard::setMode(%i, %i) - invalid mode", mode, color);
- return setMode(HORIZONTAL);
- }
-
- setOctaves(m_octaves);
- }
-
- void PixmapKeyboard::setOctaves(int octaves)
- {
- Q_ASSERT(octaves >= 1 && octaves <= 6);
-
- if (octaves < 1)
- octaves = 1;
- else if (octaves > 6)
- octaves = 6;
- m_octaves = octaves;
-
- if (m_pixmap_mode == HORIZONTAL)
- {
- setMinimumSize(p_width * m_octaves, p_height);
- setMaximumSize(p_width * m_octaves, p_height);
- }
- else if (m_pixmap_mode == VERTICAL)
- {
- setMinimumSize(p_width, p_height * m_octaves);
- setMaximumSize(p_width, p_height * m_octaves);
- }
-
- update();
- }
-
- void PixmapKeyboard::handleMousePos(const QPoint& pos)
- {
- int note, octave;
- QPointF n_pos;
-
- if (m_pixmap_mode == HORIZONTAL)
- {
- if (pos.x() < 0 or pos.x() > m_octaves * 144)
- return;
- int posX = pos.x() - 1;
- octave = posX / p_width;
- n_pos = QPointF(posX % p_width, pos.y());
- }
- else if (m_pixmap_mode == VERTICAL)
- {
- if (pos.y() < 0 or pos.y() > m_octaves * 144)
- return;
- int posY = pos.y() - 1;
- octave = m_octaves - posY / p_height;
- n_pos = QPointF(pos.x(), posY % p_height);
- }
- else
- return;
-
- octave += 3;
-
- if ((*m_midi_map)[1].contains(n_pos)) // C#
- note = 1;
- else if ((*m_midi_map)[3].contains(n_pos)) // D#
- note = 3;
- else if ((*m_midi_map)[6].contains(n_pos)) // F#
- note = 6;
- else if ((*m_midi_map)[8].contains(n_pos)) // G#
- note = 8;
- else if ((*m_midi_map)[10].contains(n_pos))// A#
- note = 10;
- else if ((*m_midi_map)[0].contains(n_pos)) // C
- note = 0;
- else if ((*m_midi_map)[2].contains(n_pos)) // D
- note = 2;
- else if ((*m_midi_map)[4].contains(n_pos)) // E
- note = 4;
- else if ((*m_midi_map)[5].contains(n_pos)) // F
- note = 5;
- else if ((*m_midi_map)[7].contains(n_pos)) // G
- note = 7;
- else if ((*m_midi_map)[9].contains(n_pos)) // A
- note = 9;
- else if ((*m_midi_map)[11].contains(n_pos))// B
- note = 11;
- else
- note = -1;
-
- if (note != -1)
- {
- note += octave * 12;
- if (m_lastMouseNote != note)
- {
- sendNoteOff(m_lastMouseNote);
- sendNoteOn(note);
- }
- }
- else
- sendNoteOff(m_lastMouseNote);
-
- m_lastMouseNote = note;
- }
-
- void PixmapKeyboard::keyPressEvent(QKeyEvent* event)
- {
- if (! event->isAutoRepeat())
- {
- int qKey = event->key();
- if (midi_keyboard2key_map.keys().contains(qKey))
- sendNoteOn(midi_keyboard2key_map[qKey]);
- }
- QWidget::keyPressEvent(event);
- }
-
- void PixmapKeyboard::keyReleaseEvent(QKeyEvent* event)
- {
- if (! event->isAutoRepeat())
- {
- int qKey = event->key();
- if (midi_keyboard2key_map.keys().contains(qKey))
- sendNoteOff(midi_keyboard2key_map[qKey]);
- }
- QWidget::keyReleaseEvent(event);
- }
-
- void PixmapKeyboard::mousePressEvent(QMouseEvent* event)
- {
- m_lastMouseNote = -1;
- handleMousePos(event->pos());
- setFocus();
- QWidget::mousePressEvent(event);
- }
-
- void PixmapKeyboard::mouseMoveEvent(QMouseEvent* event)
- {
- handleMousePos(event->pos());
- QWidget::mousePressEvent(event);
- }
-
- void PixmapKeyboard::mouseReleaseEvent(QMouseEvent* event)
- {
- if (m_lastMouseNote != -1)
- {
- sendNoteOff(m_lastMouseNote);
- m_lastMouseNote = -1;
- }
- QWidget::mouseReleaseEvent(event);
- }
-
- void PixmapKeyboard::paintEvent(QPaintEvent*)
- {
- QPainter painter(this);
-
- // -------------------------------------------------------------
- // Paint clean keys (as background)
-
- for (int octave=0; octave < m_octaves; octave++)
- {
- QRectF target;
-
- if (m_pixmap_mode == HORIZONTAL)
- target = QRectF(p_width * octave, 0, p_width, p_height);
- else if (m_pixmap_mode == VERTICAL)
- target = QRectF(0, p_height * octave, p_width, p_height);
- else
- return;
-
- QRectF source = QRectF(0, 0, p_width, p_height);
- painter.drawPixmap(target, m_pixmap, source);
- }
-
- // -------------------------------------------------------------
- // Paint (white) pressed keys
-
- bool paintedWhite = false;
-
- for (int i=0; i < m_enabledKeys.count(); i++)
- {
- int octave, note = m_enabledKeys[i];
- QRectF pos = _getRectFromMidiNote(note);
-
- if (_isNoteBlack(note))
- continue;
-
- if (note < 36)
- // cannot paint this note
- continue;
- else if (note < 48)
- octave = 0;
- else if (note < 60)
- octave = 1;
- else if (note < 72)
- octave = 2;
- else if (note < 84)
- octave = 3;
- else if (note < 96)
- octave = 4;
- else if (note < 108)
- octave = 5;
- else
- // cannot paint this note either
- continue;
-
- if (m_pixmap_mode == VERTICAL)
- octave = m_octaves - octave - 1;
-
- QRectF target, source;
-
- if (m_pixmap_mode == HORIZONTAL)
- {
- target = QRectF(pos.x() + (p_width * octave), 0, pos.width(), pos.height());
- source = QRectF(pos.x(), p_height, pos.width(), pos.height());
- }
- else if (m_pixmap_mode == VERTICAL)
- {
- target = QRectF(pos.x(), pos.y() + (p_height * octave), pos.width(), pos.height());
- source = QRectF(p_width, pos.y(), pos.width(), pos.height());
- }
- else
- return;
-
- paintedWhite = true;
- painter.drawPixmap(target, m_pixmap, source);
- }
-
- // -------------------------------------------------------------
- // Clear white keys border
-
- if (paintedWhite)
- {
- for (int octave=0; octave < m_octaves; octave++)
- {
- foreach (int note, blackNotes)
- {
- QRectF target, source;
- QRectF pos = _getRectFromMidiNote(note);
- if (m_pixmap_mode == HORIZONTAL)
- {
- target = QRectF(pos.x() + (p_width * octave), 0, pos.width(), pos.height());
- source = QRectF(pos.x(), 0, pos.width(), pos.height());
- }
- else if (m_pixmap_mode == VERTICAL)
- {
- target = QRectF(pos.x(), pos.y() + (p_height * octave), pos.width(), pos.height());
- source = QRectF(0, pos.y(), pos.width(), pos.height());
- }
- else
- return;
-
- painter.drawPixmap(target, m_pixmap, source);
- }
- }
- }
-
- // -------------------------------------------------------------
- // Paint (black) pressed keys
-
- for (int i=0; i < m_enabledKeys.count(); i++)
- {
- int octave, note = m_enabledKeys[i];
- QRectF pos = _getRectFromMidiNote(note);
-
- if (! _isNoteBlack(note))
- continue;
-
- if (note < 36)
- // cannot paint this note
- continue;
- else if (note < 48)
- octave = 0;
- else if (note < 60)
- octave = 1;
- else if (note < 72)
- octave = 2;
- else if (note < 84)
- octave = 3;
- else if (note < 96)
- octave = 4;
- else if (note < 108)
- octave = 5;
- else
- // cannot paint this note either
- continue;
-
- if (m_pixmap_mode == VERTICAL)
- octave = m_octaves - octave - 1;
-
- QRectF target, source;
-
- if (m_pixmap_mode == HORIZONTAL)
- {
- target = QRectF(pos.x() + (p_width * octave), 0, pos.width(), pos.height());
- source = QRectF(pos.x(), p_height, pos.width(), pos.height());
- }
- else if (m_pixmap_mode == VERTICAL)
- {
- target = QRectF(pos.x(), pos.y() + (p_height * octave), pos.width(), pos.height());
- source = QRectF(p_width, pos.y(), pos.width(), pos.height());
- }
- else
- return;
-
- painter.drawPixmap(target, m_pixmap, source);
- }
-
- // Paint C-number note info
- painter.setFont(m_font);
- painter.setPen(Qt::black);
-
- for (int i=0; i < m_octaves; i++)
- {
- if (m_pixmap_mode == HORIZONTAL)
- painter.drawText(i * 144, 48, 18, 18, Qt::AlignCenter, QString("C%1").arg(i + 2));
- else if (m_pixmap_mode == VERTICAL)
- painter.drawText(45, (m_octaves * 144) - (i * 144) - 16, 18, 18, Qt::AlignCenter, QString("C%1").arg(i + 2));
- }
- }
-
- void PixmapKeyboard::updateOnce()
- {
- if (m_needsUpdate)
- {
- update();
- m_needsUpdate = false;
- }
- }
-
- bool PixmapKeyboard::_isNoteBlack(int note)
- {
- int baseNote = note % 12;
- return blackNotes.contains(baseNote);
- }
-
- QRectF PixmapKeyboard::_getRectFromMidiNote(int note)
- {
- return (*m_midi_map)[note % 12];
- }
|