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.

507 lines
14KB

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