Collection of tools useful for audio production
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

615 lines
17KB

  1. /*
  2. * Pixmap Keyboard, a custom Qt4 widget
  3. * Copyright (C) 2011-2013 Filipe Coelho <falktx@falktx.com>
  4. *
  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.
  9. *
  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.
  14. *
  15. * For a full copy of the GNU General Public License see the COPYING file
  16. */
  17. #include "pixmapkeyboard.hpp"
  18. #include <QtCore/QTimer>
  19. #include <QtGui/QKeyEvent>
  20. #include <QtGui/QMouseEvent>
  21. #include <QtGui/QPainter>
  22. #ifndef Q_COMPILER_LAMBDA
  23. static std::map<int, QRectF> kMidiKey2RectMapHorizontal;
  24. #else
  25. static std::map<int, QRectF> kMidiKey2RectMapHorizontal = {
  26. {0, QRectF(0, 0, 18, 64)}, // C
  27. {1, QRectF(13, 0, 11, 42)}, // C#
  28. {2, QRectF(18, 0, 25, 64)}, // D
  29. {3, QRectF(37, 0, 11, 42)}, // D#
  30. {4, QRectF(42, 0, 18, 64)}, // E
  31. {5, QRectF(60, 0, 18, 64)}, // F
  32. {6, QRectF(73, 0, 11, 42)}, // F#
  33. {7, QRectF(78, 0, 25, 64)}, // G
  34. {8, QRectF(97, 0, 11, 42)}, // G#
  35. {9, QRectF(102, 0, 25, 64)}, // A
  36. {10, QRectF(121, 0, 11, 42)}, // A#
  37. {11, QRectF(126, 0, 18, 64)} // B
  38. };
  39. #endif
  40. #ifndef Q_COMPILER_LAMBDA
  41. static std::map<int, QRectF> kMidiKey2RectMapVertical;
  42. #else
  43. static std::map<int, QRectF> kMidiKey2RectMapVertical = {
  44. {11, QRectF(0, 0, 64, 18)}, // B
  45. {10, QRectF(0, 14, 42, 7)}, // A#
  46. {9, QRectF(0, 18, 64, 24)}, // A
  47. {8, QRectF(0, 38, 42, 7)}, // G#
  48. {7, QRectF(0, 42, 64, 24)}, // G
  49. {6, QRectF(0, 62, 42, 7)}, // F#
  50. {5, QRectF(0, 66, 64, 18)}, // F
  51. {4, QRectF(0, 84, 64, 18)}, // E
  52. {3, QRectF(0, 98, 42, 7)}, // D#
  53. {2, QRectF(0, 102, 64, 24)}, // D
  54. {1, QRectF(0, 122, 42, 7)}, // C#
  55. {0, QRectF(0, 126, 64, 18)} // C
  56. };
  57. #endif
  58. #ifndef Q_COMPILER_LAMBDA
  59. static std::map<int, int> kMidiKeyboard2KeyMap;
  60. #else
  61. static const std::map<int, int> kMidiKeyboard2KeyMap = {
  62. // bottom row, 3rd octave
  63. {Qt::Key_Z, 48},
  64. {Qt::Key_S, 49},
  65. {Qt::Key_X, 50},
  66. {Qt::Key_D, 51},
  67. {Qt::Key_C, 52},
  68. {Qt::Key_V, 53},
  69. {Qt::Key_G, 54},
  70. {Qt::Key_B, 55},
  71. {Qt::Key_H, 56},
  72. {Qt::Key_N, 57},
  73. {Qt::Key_J, 58},
  74. {Qt::Key_M, 59},
  75. // top row, 4th octave
  76. {Qt::Key_Q, 60},
  77. {Qt::Key_2, 61},
  78. {Qt::Key_W, 62},
  79. {Qt::Key_3, 63},
  80. {Qt::Key_E, 64},
  81. {Qt::Key_R, 65},
  82. {Qt::Key_5, 66},
  83. {Qt::Key_T, 67},
  84. {Qt::Key_6, 68},
  85. {Qt::Key_Y, 69},
  86. {Qt::Key_7, 70},
  87. {Qt::Key_U, 71},
  88. // continue the top row in to the 5th octave
  89. {Qt::Key_I, 72},
  90. {Qt::Key_9, 73},
  91. {Qt::Key_O, 74},
  92. {Qt::Key_0, 75},
  93. {Qt::Key_P, 76}
  94. };
  95. #endif
  96. #ifndef Q_COMPILER_LAMBDA
  97. static QVector<int> kBlackNotes;
  98. #else
  99. static const QVector<int> kBlackNotes = {1, 3, 6, 8, 10};
  100. #endif
  101. #ifndef Q_COMPILER_LAMBDA
  102. static const struct PixmapKeyboardInit {
  103. PixmapKeyboardInit() {
  104. // kMidiKey2RectMapHorizontal
  105. kMidiKey2RectMapHorizontal[0] = QRectF(0, 0, 18, 64); // C
  106. kMidiKey2RectMapHorizontal[1] = QRectF(13, 0, 11, 42); // C#
  107. kMidiKey2RectMapHorizontal[2] = QRectF(18, 0, 25, 64); // D
  108. kMidiKey2RectMapHorizontal[3] = QRectF(37, 0, 11, 42); // D#
  109. kMidiKey2RectMapHorizontal[4] = QRectF(42, 0, 18, 64); // E
  110. kMidiKey2RectMapHorizontal[5] = QRectF(60, 0, 18, 64); // F
  111. kMidiKey2RectMapHorizontal[6] = QRectF(73, 0, 11, 42); // F#
  112. kMidiKey2RectMapHorizontal[7] = QRectF(78, 0, 25, 64); // G
  113. kMidiKey2RectMapHorizontal[8] = QRectF(97, 0, 11, 42); // G#
  114. kMidiKey2RectMapHorizontal[9] = QRectF(102, 0, 25, 64); // A
  115. kMidiKey2RectMapHorizontal[10] = QRectF(121, 0, 11, 42); // A#
  116. kMidiKey2RectMapHorizontal[11] = QRectF(126, 0, 18, 64); // B
  117. // kMidiKey2RectMapVertical
  118. kMidiKey2RectMapVertical[11] = QRectF(0, 0, 64, 18); // B
  119. kMidiKey2RectMapVertical[10] = QRectF(0, 14, 42, 7); // A#
  120. kMidiKey2RectMapVertical[9] = QRectF(0, 18, 64, 24); // A
  121. kMidiKey2RectMapVertical[8] = QRectF(0, 38, 42, 7); // G#
  122. kMidiKey2RectMapVertical[7] = QRectF(0, 42, 64, 24); // G
  123. kMidiKey2RectMapVertical[6] = QRectF(0, 62, 42, 7); // F#
  124. kMidiKey2RectMapVertical[5] = QRectF(0, 66, 64, 18); // F
  125. kMidiKey2RectMapVertical[4] = QRectF(0, 84, 64, 18); // E
  126. kMidiKey2RectMapVertical[3] = QRectF(0, 98, 42, 7); // D#
  127. kMidiKey2RectMapVertical[2] = QRectF(0, 102, 64, 24); // D
  128. kMidiKey2RectMapVertical[1] = QRectF(0, 122, 42, 7); // C#
  129. kMidiKey2RectMapVertical[0] = QRectF(0, 126, 64, 18); // C
  130. // kMidiKeyboard2KeyMap, 3th octave
  131. kMidiKeyboard2KeyMap[Qt::Key_Z] = 48;
  132. kMidiKeyboard2KeyMap[Qt::Key_S] = 49;
  133. kMidiKeyboard2KeyMap[Qt::Key_X] = 50;
  134. kMidiKeyboard2KeyMap[Qt::Key_D] = 51;
  135. kMidiKeyboard2KeyMap[Qt::Key_C] = 52;
  136. kMidiKeyboard2KeyMap[Qt::Key_V] = 53;
  137. kMidiKeyboard2KeyMap[Qt::Key_G] = 54;
  138. kMidiKeyboard2KeyMap[Qt::Key_B] = 55;
  139. kMidiKeyboard2KeyMap[Qt::Key_H] = 56;
  140. kMidiKeyboard2KeyMap[Qt::Key_N] = 57;
  141. kMidiKeyboard2KeyMap[Qt::Key_J] = 58;
  142. kMidiKeyboard2KeyMap[Qt::Key_M] = 59;
  143. // kMidiKeyboard2KeyMap, 4th octave
  144. kMidiKeyboard2KeyMap[Qt::Key_Q] = 60;
  145. kMidiKeyboard2KeyMap[Qt::Key_2] = 61;
  146. kMidiKeyboard2KeyMap[Qt::Key_W] = 62;
  147. kMidiKeyboard2KeyMap[Qt::Key_3] = 63;
  148. kMidiKeyboard2KeyMap[Qt::Key_E] = 64;
  149. kMidiKeyboard2KeyMap[Qt::Key_R] = 65;
  150. kMidiKeyboard2KeyMap[Qt::Key_5] = 66;
  151. kMidiKeyboard2KeyMap[Qt::Key_T] = 67;
  152. kMidiKeyboard2KeyMap[Qt::Key_6] = 68;
  153. kMidiKeyboard2KeyMap[Qt::Key_Y] = 69;
  154. kMidiKeyboard2KeyMap[Qt::Key_7] = 70;
  155. kMidiKeyboard2KeyMap[Qt::Key_U] = 71;
  156. // kMidiKeyboard2KeyMap, 5th octave
  157. kMidiKeyboard2KeyMap[Qt::Key_I] = 72;
  158. kMidiKeyboard2KeyMap[Qt::Key_9] = 73;
  159. kMidiKeyboard2KeyMap[Qt::Key_O] = 74;
  160. kMidiKeyboard2KeyMap[Qt::Key_0] = 75;
  161. kMidiKeyboard2KeyMap[Qt::Key_P] = 76;
  162. // kBlackNotes
  163. kBlackNotes << 1;
  164. kBlackNotes << 3;
  165. kBlackNotes << 6;
  166. kBlackNotes << 8;
  167. kBlackNotes << 10;
  168. }
  169. } _pixmapKeyboardInitInit;
  170. #endif
  171. PixmapKeyboard::PixmapKeyboard(QWidget* parent)
  172. : QWidget(parent),
  173. fPixmap(""),
  174. fPixmapMode(HORIZONTAL),
  175. fColorStr("orange"),
  176. fFont("Monospace", 7, QFont::Normal),
  177. fOctaves(6),
  178. fLastMouseNote(-1),
  179. fWidth(0),
  180. fHeight(0),
  181. fMidiMap(kMidiKey2RectMapHorizontal)
  182. {
  183. setCursor(Qt::PointingHandCursor);
  184. setMode(HORIZONTAL);
  185. }
  186. void PixmapKeyboard::allNotesOff()
  187. {
  188. fEnabledKeys.clear();
  189. emit notesOff();
  190. update();
  191. }
  192. void PixmapKeyboard::sendNoteOn(int note, bool sendSignal)
  193. {
  194. if (0 <= note && note <= 127 && ! fEnabledKeys.contains(note))
  195. {
  196. fEnabledKeys.append(note);
  197. if (sendSignal)
  198. emit noteOn(note);
  199. update();
  200. }
  201. if (fEnabledKeys.count() == 1)
  202. emit notesOn();
  203. }
  204. void PixmapKeyboard::sendNoteOff(int note, bool sendSignal)
  205. {
  206. if (note >= 0 && note <= 127 && fEnabledKeys.contains(note))
  207. {
  208. fEnabledKeys.removeOne(note);
  209. if (sendSignal)
  210. emit noteOff(note);
  211. update();
  212. }
  213. if (fEnabledKeys.count() == 0)
  214. emit notesOff();
  215. }
  216. void PixmapKeyboard::setMode(Orientation mode, Color color)
  217. {
  218. if (color == COLOR_CLASSIC)
  219. {
  220. fColorStr = "classic";
  221. }
  222. else if (color == COLOR_ORANGE)
  223. {
  224. fColorStr = "orange";
  225. }
  226. else
  227. {
  228. qCritical("PixmapKeyboard::setMode(%i, %i) - invalid color", mode, color);
  229. return setMode(mode);
  230. }
  231. if (mode == HORIZONTAL)
  232. {
  233. fMidiMap = kMidiKey2RectMapHorizontal;
  234. fPixmap.load(QString(":/bitmaps/kbd_h_%1.png").arg(fColorStr));
  235. fPixmapMode = HORIZONTAL;
  236. fWidth = fPixmap.width();
  237. fHeight = fPixmap.height() / 2;
  238. }
  239. else if (mode == VERTICAL)
  240. {
  241. fMidiMap = kMidiKey2RectMapVertical;
  242. fPixmap.load(QString(":/bitmaps/kbd_v_%1.png").arg(fColorStr));
  243. fPixmapMode = VERTICAL;
  244. fWidth = fPixmap.width() / 2;
  245. fHeight = fPixmap.height();
  246. }
  247. else
  248. {
  249. qCritical("PixmapKeyboard::setMode(%i, %i) - invalid mode", mode, color);
  250. return setMode(HORIZONTAL);
  251. }
  252. setOctaves(fOctaves);
  253. }
  254. void PixmapKeyboard::setOctaves(int octaves)
  255. {
  256. Q_ASSERT(octaves >= 1 && octaves <= 10);
  257. if (octaves < 1)
  258. octaves = 1;
  259. else if (octaves > 10)
  260. octaves = 10;
  261. fOctaves = octaves;
  262. if (fPixmapMode == HORIZONTAL)
  263. {
  264. setMinimumSize(fWidth * fOctaves, fHeight);
  265. setMaximumSize(fWidth * fOctaves, fHeight);
  266. }
  267. else if (fPixmapMode == VERTICAL)
  268. {
  269. setMinimumSize(fWidth, fHeight * fOctaves);
  270. setMaximumSize(fWidth, fHeight * fOctaves);
  271. }
  272. update();
  273. }
  274. void PixmapKeyboard::handleMousePos(const QPoint& pos)
  275. {
  276. int note, octave;
  277. QPointF keyPos;
  278. if (fPixmapMode == HORIZONTAL)
  279. {
  280. if (pos.x() < 0 or pos.x() > fOctaves * 144)
  281. return;
  282. int posX = pos.x() - 1;
  283. octave = posX / fWidth;
  284. keyPos = QPointF(posX % fWidth, pos.y());
  285. }
  286. else if (fPixmapMode == VERTICAL)
  287. {
  288. if (pos.y() < 0 or pos.y() > fOctaves * 144)
  289. return;
  290. int posY = pos.y() - 1;
  291. octave = fOctaves - posY / fHeight;
  292. keyPos = QPointF(pos.x(), posY % fHeight);
  293. }
  294. else
  295. return;
  296. if (fMidiMap[1].contains(keyPos)) // C#
  297. note = 1;
  298. else if (fMidiMap[3].contains(keyPos)) // D#
  299. note = 3;
  300. else if (fMidiMap[6].contains(keyPos)) // F#
  301. note = 6;
  302. else if (fMidiMap[8].contains(keyPos)) // G#
  303. note = 8;
  304. else if (fMidiMap[10].contains(keyPos))// A#
  305. note = 10;
  306. else if (fMidiMap[0].contains(keyPos)) // C
  307. note = 0;
  308. else if (fMidiMap[2].contains(keyPos)) // D
  309. note = 2;
  310. else if (fMidiMap[4].contains(keyPos)) // E
  311. note = 4;
  312. else if (fMidiMap[5].contains(keyPos)) // F
  313. note = 5;
  314. else if (fMidiMap[7].contains(keyPos)) // G
  315. note = 7;
  316. else if (fMidiMap[9].contains(keyPos)) // A
  317. note = 9;
  318. else if (fMidiMap[11].contains(keyPos))// B
  319. note = 11;
  320. else
  321. note = -1;
  322. if (note != -1)
  323. {
  324. note += octave * 12;
  325. if (fLastMouseNote != note)
  326. {
  327. sendNoteOff(fLastMouseNote);
  328. sendNoteOn(note);
  329. }
  330. }
  331. else if (fLastMouseNote != -1)
  332. sendNoteOff(fLastMouseNote);
  333. fLastMouseNote = note;
  334. }
  335. void PixmapKeyboard::keyPressEvent(QKeyEvent* event)
  336. {
  337. if (! event->isAutoRepeat())
  338. {
  339. int qKey = event->key();
  340. std::map<int, int>::const_iterator it = kMidiKeyboard2KeyMap.find(qKey);
  341. if (it != kMidiKeyboard2KeyMap.end())
  342. sendNoteOn(it->second);
  343. }
  344. QWidget::keyPressEvent(event);
  345. }
  346. void PixmapKeyboard::keyReleaseEvent(QKeyEvent* event)
  347. {
  348. if (! event->isAutoRepeat())
  349. {
  350. int qKey = event->key();
  351. std::map<int, int>::const_iterator it = kMidiKeyboard2KeyMap.find(qKey);
  352. if (it != kMidiKeyboard2KeyMap.end())
  353. sendNoteOff(it->second);
  354. }
  355. QWidget::keyReleaseEvent(event);
  356. }
  357. void PixmapKeyboard::mousePressEvent(QMouseEvent* event)
  358. {
  359. fLastMouseNote = -1;
  360. handleMousePos(event->pos());
  361. setFocus();
  362. QWidget::mousePressEvent(event);
  363. }
  364. void PixmapKeyboard::mouseMoveEvent(QMouseEvent* event)
  365. {
  366. handleMousePos(event->pos());
  367. QWidget::mouseMoveEvent(event);
  368. }
  369. void PixmapKeyboard::mouseReleaseEvent(QMouseEvent* event)
  370. {
  371. if (fLastMouseNote != -1)
  372. {
  373. sendNoteOff(fLastMouseNote);
  374. fLastMouseNote = -1;
  375. }
  376. QWidget::mouseReleaseEvent(event);
  377. }
  378. void PixmapKeyboard::paintEvent(QPaintEvent* event)
  379. {
  380. QPainter painter(this);
  381. event->accept();
  382. // -------------------------------------------------------------
  383. // Paint clean keys (as background)
  384. for (int octave=0; octave < fOctaves; ++octave)
  385. {
  386. QRectF target;
  387. if (fPixmapMode == HORIZONTAL)
  388. target = QRectF(fWidth * octave, 0, fWidth, fHeight);
  389. else if (fPixmapMode == VERTICAL)
  390. target = QRectF(0, fHeight * octave, fWidth, fHeight);
  391. else
  392. return;
  393. QRectF source = QRectF(0, 0, fWidth, fHeight);
  394. painter.drawPixmap(target, fPixmap, source);
  395. }
  396. // -------------------------------------------------------------
  397. // Paint (white) pressed keys
  398. bool paintedWhite = false;
  399. for (int i=0, count=fEnabledKeys.count(); i < count; ++i)
  400. {
  401. int octave, note = fEnabledKeys[i];
  402. const QRectF& pos(_getRectFromMidiNote(note));
  403. if (_isNoteBlack(note))
  404. continue;
  405. if (note < 12)
  406. octave = 0;
  407. else if (note < 24)
  408. octave = 1;
  409. else if (note < 36)
  410. octave = 2;
  411. else if (note < 48)
  412. octave = 3;
  413. else if (note < 60)
  414. octave = 4;
  415. else if (note < 72)
  416. octave = 5;
  417. else if (note < 84)
  418. octave = 6;
  419. else if (note < 96)
  420. octave = 7;
  421. else if (note < 108)
  422. octave = 8;
  423. else if (note < 120)
  424. octave = 9;
  425. else if (note < 132)
  426. octave = 10;
  427. else
  428. // cannot paint this note
  429. continue;
  430. if (fPixmapMode == VERTICAL)
  431. octave = fOctaves - octave - 1;
  432. QRectF target, source;
  433. if (fPixmapMode == HORIZONTAL)
  434. {
  435. target = QRectF(pos.x() + (fWidth * octave), 0, pos.width(), pos.height());
  436. source = QRectF(pos.x(), fHeight, pos.width(), pos.height());
  437. }
  438. else if (fPixmapMode == VERTICAL)
  439. {
  440. target = QRectF(pos.x(), pos.y() + (fHeight * octave), pos.width(), pos.height());
  441. source = QRectF(fWidth, pos.y(), pos.width(), pos.height());
  442. }
  443. else
  444. return;
  445. paintedWhite = true;
  446. painter.drawPixmap(target, fPixmap, source);
  447. }
  448. // -------------------------------------------------------------
  449. // Clear white keys border
  450. if (paintedWhite)
  451. {
  452. for (int octave=0; octave < fOctaves; ++octave)
  453. {
  454. foreach (int note, kBlackNotes)
  455. {
  456. QRectF target, source;
  457. const QRectF& pos(_getRectFromMidiNote(note));
  458. if (fPixmapMode == HORIZONTAL)
  459. {
  460. target = QRectF(pos.x() + (fWidth * octave), 0, pos.width(), pos.height());
  461. source = QRectF(pos.x(), 0, pos.width(), pos.height());
  462. }
  463. else if (fPixmapMode == VERTICAL)
  464. {
  465. target = QRectF(pos.x(), pos.y() + (fHeight * octave), pos.width(), pos.height());
  466. source = QRectF(0, pos.y(), pos.width(), pos.height());
  467. }
  468. else
  469. return;
  470. painter.drawPixmap(target, fPixmap, source);
  471. }
  472. }
  473. }
  474. // -------------------------------------------------------------
  475. // Paint (black) pressed keys
  476. for (int i=0, count=fEnabledKeys.count(); i < count; ++i)
  477. {
  478. int octave, note = fEnabledKeys[i];
  479. const QRectF& pos(_getRectFromMidiNote(note));
  480. if (! _isNoteBlack(note))
  481. continue;
  482. if (note < 12)
  483. octave = 0;
  484. else if (note < 24)
  485. octave = 1;
  486. else if (note < 36)
  487. octave = 2;
  488. else if (note < 48)
  489. octave = 3;
  490. else if (note < 60)
  491. octave = 4;
  492. else if (note < 72)
  493. octave = 5;
  494. else if (note < 84)
  495. octave = 6;
  496. else if (note < 96)
  497. octave = 7;
  498. else if (note < 108)
  499. octave = 8;
  500. else if (note < 120)
  501. octave = 9;
  502. else if (note < 132)
  503. octave = 10;
  504. else
  505. // cannot paint this note
  506. continue;
  507. if (fPixmapMode == VERTICAL)
  508. octave = fOctaves - octave - 1;
  509. QRectF target, source;
  510. if (fPixmapMode == HORIZONTAL)
  511. {
  512. target = QRectF(pos.x() + (fWidth * octave), 0, pos.width(), pos.height());
  513. source = QRectF(pos.x(), fHeight, pos.width(), pos.height());
  514. }
  515. else if (fPixmapMode == VERTICAL)
  516. {
  517. target = QRectF(pos.x(), pos.y() + (fHeight * octave), pos.width(), pos.height());
  518. source = QRectF(fWidth, pos.y(), pos.width(), pos.height());
  519. }
  520. else
  521. return;
  522. painter.drawPixmap(target, fPixmap, source);
  523. }
  524. // Paint C-number note info
  525. painter.setFont(fFont);
  526. painter.setPen(Qt::black);
  527. for (int i=0; i < fOctaves; ++i)
  528. {
  529. if (fPixmapMode == HORIZONTAL)
  530. painter.drawText(i * 144, 48, 18, 18, Qt::AlignCenter, QString("C%1").arg(i-1));
  531. else if (fPixmapMode == VERTICAL)
  532. painter.drawText(45, (fOctaves * 144) - (i * 144) - 16, 18, 18, Qt::AlignCenter, QString("C%1").arg(i-1));
  533. }
  534. }
  535. bool PixmapKeyboard::_isNoteBlack(int note) const
  536. {
  537. const int baseNote = note % 12;
  538. return kBlackNotes.contains(baseNote);
  539. }
  540. const QRectF& PixmapKeyboard::_getRectFromMidiNote(int note) const
  541. {
  542. const int baseNote = note % 12;
  543. return fMidiMap[baseNote];
  544. }