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.

515 lines
15KB

  1. /*
  2. * Pixmap Keyboard, a custom Qt4 widget
  3. * Copyright (C) 2011-2012 Filipe Coelho <falktx@gmail.com>
  4. *
  5. * This program is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation; either version 2 of the License, or
  8. * 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.h"
  18. #include <QtCore/QMap>
  19. #include <QtCore/QTimer>
  20. #include <QtGui/QKeyEvent>
  21. #include <QtGui/QMouseEvent>
  22. #include <QtGui/QPainter>
  23. QMap<int, QRectF> midi_key2rect_map_horizontal;
  24. QMap<int, QRectF> midi_key2rect_map_vertical;
  25. QMap<int, int> midi_keyboard2key_map;
  26. QVector<int> blackNotes;
  27. static bool pixmapkeyboard_initiated = false;
  28. void pixmapkeyboard_init()
  29. {
  30. if (pixmapkeyboard_initiated)
  31. return;
  32. pixmapkeyboard_initiated = true;
  33. // midi_key2rect_map_horizontal ------
  34. midi_key2rect_map_horizontal[0] = QRectF(0, 0, 18, 64); // C
  35. midi_key2rect_map_horizontal[1] = QRectF(13, 0, 11, 42); // C#
  36. midi_key2rect_map_horizontal[2] = QRectF(18, 0, 25, 64); // D
  37. midi_key2rect_map_horizontal[3] = QRectF(37, 0, 11, 42); // D#
  38. midi_key2rect_map_horizontal[4] = QRectF(42, 0, 18, 64); // E
  39. midi_key2rect_map_horizontal[5] = QRectF(60, 0, 18, 64); // F
  40. midi_key2rect_map_horizontal[6] = QRectF(73, 0, 11, 42); // F#
  41. midi_key2rect_map_horizontal[7] = QRectF(78, 0, 25, 64); // G
  42. midi_key2rect_map_horizontal[8] = QRectF(97, 0, 11, 42); // G#
  43. midi_key2rect_map_horizontal[9] = QRectF(102, 0, 25, 64); // A
  44. midi_key2rect_map_horizontal[10] = QRectF(121, 0, 11, 42); // A#
  45. midi_key2rect_map_horizontal[11] = QRectF(126, 0, 18, 64); // B
  46. // midi_key2rect_map_vertical --------
  47. midi_key2rect_map_vertical[11] = QRectF(0, 0, 64, 18); // B
  48. midi_key2rect_map_vertical[10] = QRectF(0, 14, 42, 7); // A#
  49. midi_key2rect_map_vertical[9] = QRectF(0, 18, 64, 24); // A
  50. midi_key2rect_map_vertical[8] = QRectF(0, 38, 42, 7); // G#
  51. midi_key2rect_map_vertical[7] = QRectF(0, 42, 64, 24); // G
  52. midi_key2rect_map_vertical[6] = QRectF(0, 62, 42, 7); // F#
  53. midi_key2rect_map_vertical[5] = QRectF(0, 66, 64, 18); // F
  54. midi_key2rect_map_vertical[4] = QRectF(0, 84, 64, 18); // E
  55. midi_key2rect_map_vertical[3] = QRectF(0, 98, 42, 7); // D#
  56. midi_key2rect_map_vertical[2] = QRectF(0, 102, 64, 24); // D
  57. midi_key2rect_map_vertical[1] = QRectF(0, 122, 42, 7); // C#
  58. midi_key2rect_map_vertical[0] = QRectF(0, 126, 64, 18); // C
  59. // midi_keyboard2key_map -------------
  60. // 3th octave
  61. midi_keyboard2key_map[Qt::Key_Z] = 48;
  62. midi_keyboard2key_map[Qt::Key_S] = 49;
  63. midi_keyboard2key_map[Qt::Key_X] = 50;
  64. midi_keyboard2key_map[Qt::Key_D] = 51;
  65. midi_keyboard2key_map[Qt::Key_C] = 52;
  66. midi_keyboard2key_map[Qt::Key_V] = 53;
  67. midi_keyboard2key_map[Qt::Key_G] = 54;
  68. midi_keyboard2key_map[Qt::Key_B] = 55;
  69. midi_keyboard2key_map[Qt::Key_H] = 56;
  70. midi_keyboard2key_map[Qt::Key_N] = 57;
  71. midi_keyboard2key_map[Qt::Key_J] = 58;
  72. midi_keyboard2key_map[Qt::Key_M] = 59;
  73. // 4th octave
  74. midi_keyboard2key_map[Qt::Key_Q] = 60;
  75. midi_keyboard2key_map[Qt::Key_2] = 61;
  76. midi_keyboard2key_map[Qt::Key_W] = 62;
  77. midi_keyboard2key_map[Qt::Key_3] = 63;
  78. midi_keyboard2key_map[Qt::Key_E] = 64;
  79. midi_keyboard2key_map[Qt::Key_R] = 65;
  80. midi_keyboard2key_map[Qt::Key_5] = 66;
  81. midi_keyboard2key_map[Qt::Key_T] = 67;
  82. midi_keyboard2key_map[Qt::Key_6] = 68;
  83. midi_keyboard2key_map[Qt::Key_Y] = 69;
  84. midi_keyboard2key_map[Qt::Key_7] = 70;
  85. midi_keyboard2key_map[Qt::Key_U] = 71;
  86. blackNotes << 1;
  87. blackNotes << 3;
  88. blackNotes << 6;
  89. blackNotes << 8;
  90. blackNotes << 10;
  91. }
  92. PixmapKeyboard::PixmapKeyboard(QWidget* parent)
  93. : QWidget(parent),
  94. m_font("Monospace", 8, QFont::Normal)
  95. {
  96. pixmapkeyboard_init();
  97. m_octaves = 6;
  98. m_lastMouseNote = -1;
  99. m_needsUpdate = false;
  100. setCursor(Qt::PointingHandCursor);
  101. setMode(HORIZONTAL);
  102. }
  103. void PixmapKeyboard::sendNoteOn(int note, bool sendSignal)
  104. {
  105. if (0 <= note && note <= 127 && ! m_enabledKeys.contains(note))
  106. {
  107. m_enabledKeys.append(note);
  108. if (sendSignal)
  109. emit noteOn(note);
  110. m_needsUpdate = true;
  111. QTimer::singleShot(0, this, SLOT(updateOnce()));
  112. }
  113. if (m_enabledKeys.count() == 1)
  114. emit notesOn();
  115. }
  116. void PixmapKeyboard::sendNoteOff(int note, bool sendSignal)
  117. {
  118. if (note >= 0 && note <= 127 && m_enabledKeys.contains(note))
  119. {
  120. m_enabledKeys.removeOne(note);
  121. if (sendSignal)
  122. emit noteOff(note);
  123. m_needsUpdate = true;
  124. QTimer::singleShot(0, this, SLOT(updateOnce()));
  125. }
  126. if (m_enabledKeys.count() == 0)
  127. emit notesOff();
  128. }
  129. void PixmapKeyboard::setMode(Orientation mode, Color color)
  130. {
  131. if (color == COLOR_CLASSIC)
  132. {
  133. m_colorStr = "classic";
  134. }
  135. else if (color == COLOR_ORANGE)
  136. {
  137. m_colorStr = "orange";
  138. }
  139. else
  140. {
  141. qCritical("PixmapKeyboard::setMode(%i, %i) - invalid color", mode, color);
  142. return setMode(mode);
  143. }
  144. if (mode == HORIZONTAL)
  145. {
  146. m_midi_map = &midi_key2rect_map_horizontal;
  147. m_pixmap.load(QString(":/bitmaps/kbd_h_%1.png").arg(m_colorStr));
  148. m_pixmap_mode = HORIZONTAL;
  149. p_width = m_pixmap.width();
  150. p_height = m_pixmap.height() / 2;
  151. }
  152. else if (mode == VERTICAL)
  153. {
  154. m_midi_map = &midi_key2rect_map_vertical;
  155. m_pixmap.load(QString(":/bitmaps/kbd_v_%1.png").arg(m_colorStr));
  156. m_pixmap_mode = VERTICAL;
  157. p_width = m_pixmap.width() / 2;
  158. p_height = m_pixmap.height();
  159. }
  160. else
  161. {
  162. qCritical("PixmapKeyboard::setMode(%i, %i) - invalid mode", mode, color);
  163. return setMode(HORIZONTAL);
  164. }
  165. setOctaves(m_octaves);
  166. }
  167. void PixmapKeyboard::setOctaves(int octaves)
  168. {
  169. Q_ASSERT(octaves >= 1 && octaves <= 6);
  170. if (octaves < 1)
  171. octaves = 1;
  172. else if (octaves > 6)
  173. octaves = 6;
  174. m_octaves = octaves;
  175. if (m_pixmap_mode == HORIZONTAL)
  176. {
  177. setMinimumSize(p_width * m_octaves, p_height);
  178. setMaximumSize(p_width * m_octaves, p_height);
  179. }
  180. else if (m_pixmap_mode == VERTICAL)
  181. {
  182. setMinimumSize(p_width, p_height * m_octaves);
  183. setMaximumSize(p_width, p_height * m_octaves);
  184. }
  185. update();
  186. }
  187. void PixmapKeyboard::handleMousePos(const QPoint& pos)
  188. {
  189. int note, octave;
  190. QPointF n_pos;
  191. if (m_pixmap_mode == HORIZONTAL)
  192. {
  193. if (pos.x() < 0 or pos.x() > m_octaves * 144)
  194. return;
  195. int posX = pos.x() - 1;
  196. octave = posX / p_width;
  197. n_pos = QPointF(posX % p_width, pos.y());
  198. }
  199. else if (m_pixmap_mode == VERTICAL)
  200. {
  201. if (pos.y() < 0 or pos.y() > m_octaves * 144)
  202. return;
  203. int posY = pos.y() - 1;
  204. octave = m_octaves - posY / p_height;
  205. n_pos = QPointF(pos.x(), posY % p_height);
  206. }
  207. else
  208. return;
  209. octave += 3;
  210. if ((*m_midi_map)[1].contains(n_pos)) // C#
  211. note = 1;
  212. else if ((*m_midi_map)[3].contains(n_pos)) // D#
  213. note = 3;
  214. else if ((*m_midi_map)[6].contains(n_pos)) // F#
  215. note = 6;
  216. else if ((*m_midi_map)[8].contains(n_pos)) // G#
  217. note = 8;
  218. else if ((*m_midi_map)[10].contains(n_pos))// A#
  219. note = 10;
  220. else if ((*m_midi_map)[0].contains(n_pos)) // C
  221. note = 0;
  222. else if ((*m_midi_map)[2].contains(n_pos)) // D
  223. note = 2;
  224. else if ((*m_midi_map)[4].contains(n_pos)) // E
  225. note = 4;
  226. else if ((*m_midi_map)[5].contains(n_pos)) // F
  227. note = 5;
  228. else if ((*m_midi_map)[7].contains(n_pos)) // G
  229. note = 7;
  230. else if ((*m_midi_map)[9].contains(n_pos)) // A
  231. note = 9;
  232. else if ((*m_midi_map)[11].contains(n_pos))// B
  233. note = 11;
  234. else
  235. note = -1;
  236. if (note != -1)
  237. {
  238. note += octave * 12;
  239. if (m_lastMouseNote != note)
  240. {
  241. sendNoteOff(m_lastMouseNote);
  242. sendNoteOn(note);
  243. }
  244. }
  245. else
  246. sendNoteOff(m_lastMouseNote);
  247. m_lastMouseNote = note;
  248. }
  249. void PixmapKeyboard::keyPressEvent(QKeyEvent* event)
  250. {
  251. if (! event->isAutoRepeat())
  252. {
  253. int qKey = event->key();
  254. if (midi_keyboard2key_map.keys().contains(qKey))
  255. sendNoteOn(midi_keyboard2key_map[qKey]);
  256. }
  257. QWidget::keyPressEvent(event);
  258. }
  259. void PixmapKeyboard::keyReleaseEvent(QKeyEvent* event)
  260. {
  261. if (! event->isAutoRepeat())
  262. {
  263. int qKey = event->key();
  264. if (midi_keyboard2key_map.keys().contains(qKey))
  265. sendNoteOff(midi_keyboard2key_map[qKey]);
  266. }
  267. QWidget::keyReleaseEvent(event);
  268. }
  269. void PixmapKeyboard::mousePressEvent(QMouseEvent* event)
  270. {
  271. m_lastMouseNote = -1;
  272. handleMousePos(event->pos());
  273. setFocus();
  274. QWidget::mousePressEvent(event);
  275. }
  276. void PixmapKeyboard::mouseMoveEvent(QMouseEvent* event)
  277. {
  278. handleMousePos(event->pos());
  279. QWidget::mousePressEvent(event);
  280. }
  281. void PixmapKeyboard::mouseReleaseEvent(QMouseEvent* event)
  282. {
  283. if (m_lastMouseNote != -1)
  284. {
  285. sendNoteOff(m_lastMouseNote);
  286. m_lastMouseNote = -1;
  287. }
  288. QWidget::mouseReleaseEvent(event);
  289. }
  290. void PixmapKeyboard::paintEvent(QPaintEvent*)
  291. {
  292. QPainter painter(this);
  293. // -------------------------------------------------------------
  294. // Paint clean keys (as background)
  295. for (int octave=0; octave < m_octaves; octave++)
  296. {
  297. QRectF target;
  298. if (m_pixmap_mode == HORIZONTAL)
  299. target = QRectF(p_width * octave, 0, p_width, p_height);
  300. else if (m_pixmap_mode == VERTICAL)
  301. target = QRectF(0, p_height * octave, p_width, p_height);
  302. else
  303. return;
  304. QRectF source = QRectF(0, 0, p_width, p_height);
  305. painter.drawPixmap(target, m_pixmap, source);
  306. }
  307. // -------------------------------------------------------------
  308. // Paint (white) pressed keys
  309. bool paintedWhite = false;
  310. for (int i=0; i < m_enabledKeys.count(); i++)
  311. {
  312. int octave, note = m_enabledKeys[i];
  313. QRectF pos = _getRectFromMidiNote(note);
  314. if (_isNoteBlack(note))
  315. continue;
  316. if (note < 36)
  317. // cannot paint this note
  318. continue;
  319. else if (note < 48)
  320. octave = 0;
  321. else if (note < 60)
  322. octave = 1;
  323. else if (note < 72)
  324. octave = 2;
  325. else if (note < 84)
  326. octave = 3;
  327. else if (note < 96)
  328. octave = 4;
  329. else if (note < 108)
  330. octave = 5;
  331. else
  332. // cannot paint this note either
  333. continue;
  334. if (m_pixmap_mode == VERTICAL)
  335. octave = m_octaves - octave - 1;
  336. QRectF target, source;
  337. if (m_pixmap_mode == HORIZONTAL)
  338. {
  339. target = QRectF(pos.x() + (p_width * octave), 0, pos.width(), pos.height());
  340. source = QRectF(pos.x(), p_height, pos.width(), pos.height());
  341. }
  342. else if (m_pixmap_mode == VERTICAL)
  343. {
  344. target = QRectF(pos.x(), pos.y() + (p_height * octave), pos.width(), pos.height());
  345. source = QRectF(p_width, pos.y(), pos.width(), pos.height());
  346. }
  347. else
  348. return;
  349. paintedWhite = true;
  350. painter.drawPixmap(target, m_pixmap, source);
  351. }
  352. // -------------------------------------------------------------
  353. // Clear white keys border
  354. if (paintedWhite)
  355. {
  356. for (int octave=0; octave < m_octaves; octave++)
  357. {
  358. foreach (int note, blackNotes)
  359. {
  360. QRectF target, source;
  361. QRectF pos = _getRectFromMidiNote(note);
  362. if (m_pixmap_mode == HORIZONTAL)
  363. {
  364. target = QRectF(pos.x() + (p_width * octave), 0, pos.width(), pos.height());
  365. source = QRectF(pos.x(), 0, pos.width(), pos.height());
  366. }
  367. else if (m_pixmap_mode == VERTICAL)
  368. {
  369. target = QRectF(pos.x(), pos.y() + (p_height * octave), pos.width(), pos.height());
  370. source = QRectF(0, pos.y(), pos.width(), pos.height());
  371. }
  372. else
  373. return;
  374. painter.drawPixmap(target, m_pixmap, source);
  375. }
  376. }
  377. }
  378. // -------------------------------------------------------------
  379. // Paint (black) pressed keys
  380. for (int i=0; i < m_enabledKeys.count(); i++)
  381. {
  382. int octave, note = m_enabledKeys[i];
  383. QRectF pos = _getRectFromMidiNote(note);
  384. if (! _isNoteBlack(note))
  385. continue;
  386. if (note < 36)
  387. // cannot paint this note
  388. continue;
  389. else if (note < 48)
  390. octave = 0;
  391. else if (note < 60)
  392. octave = 1;
  393. else if (note < 72)
  394. octave = 2;
  395. else if (note < 84)
  396. octave = 3;
  397. else if (note < 96)
  398. octave = 4;
  399. else if (note < 108)
  400. octave = 5;
  401. else
  402. // cannot paint this note either
  403. continue;
  404. if (m_pixmap_mode == VERTICAL)
  405. octave = m_octaves - octave - 1;
  406. QRectF target, source;
  407. if (m_pixmap_mode == HORIZONTAL)
  408. {
  409. target = QRectF(pos.x() + (p_width * octave), 0, pos.width(), pos.height());
  410. source = QRectF(pos.x(), p_height, pos.width(), pos.height());
  411. }
  412. else if (m_pixmap_mode == VERTICAL)
  413. {
  414. target = QRectF(pos.x(), pos.y() + (p_height * octave), pos.width(), pos.height());
  415. source = QRectF(p_width, pos.y(), pos.width(), pos.height());
  416. }
  417. else
  418. return;
  419. painter.drawPixmap(target, m_pixmap, source);
  420. }
  421. // Paint C-number note info
  422. painter.setFont(m_font);
  423. painter.setPen(Qt::black);
  424. for (int i=0; i < m_octaves; i++)
  425. {
  426. if (m_pixmap_mode == HORIZONTAL)
  427. painter.drawText(i * 144, 48, 18, 18, Qt::AlignCenter, QString("C%1").arg(i + 2));
  428. else if (m_pixmap_mode == VERTICAL)
  429. painter.drawText(45, (m_octaves * 144) - (i * 144) - 16, 18, 18, Qt::AlignCenter, QString("C%1").arg(i + 2));
  430. }
  431. }
  432. void PixmapKeyboard::updateOnce()
  433. {
  434. if (m_needsUpdate)
  435. {
  436. update();
  437. m_needsUpdate = false;
  438. }
  439. }
  440. bool PixmapKeyboard::_isNoteBlack(int note)
  441. {
  442. int baseNote = note % 12;
  443. return blackNotes.contains(baseNote);
  444. }
  445. QRectF PixmapKeyboard::_getRectFromMidiNote(int note)
  446. {
  447. return (*m_midi_map)[note % 12];
  448. }