DISTRHO Plugin Framework
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.

266 lines
6.3KB

  1. #include "KeyboardWidget.hpp"
  2. #include "resources/MidiKeyboardResources.hpp"
  3. START_NAMESPACE_DISTRHO
  4. KeyboardWidget::KeyboardWidget(Window& parent)
  5. : Widget(parent),
  6. fCallback(nullptr),
  7. fMouseDown(false),
  8. fHeldKey(nullptr)
  9. {
  10. fSVGs[kWhiteKeyResourceIndex].loadFromMemory(MidiKeyboardResources::white_keyData,
  11. MidiKeyboardResources::white_keyDataSize,
  12. 1.0f);
  13. fSVGs[kWhiteKeyPressedResourceIndex].loadFromMemory(MidiKeyboardResources::white_key_pressedData,
  14. MidiKeyboardResources::white_key_pressedDataSize,
  15. 1.0f);
  16. fSVGs[kBlackKeyResourceIndex].loadFromMemory(MidiKeyboardResources::black_keyData,
  17. MidiKeyboardResources::black_keyDataSize,
  18. 1.0f);
  19. fSVGs[kBlackKeyPressedResourceIndex].loadFromMemory(MidiKeyboardResources::black_key_pressedData,
  20. MidiKeyboardResources::black_key_pressedDataSize,
  21. 1.0f);
  22. for (int i = 0; i < kResourcesCount; ++i)
  23. {
  24. fImages[i].loadFromSVG(fSVGs[i]);
  25. }
  26. const int whiteKeysTotalSpacing = kWhiteKeySpacing * kWhiteKeysCount;
  27. const uint width = fImages[kWhiteKeyResourceIndex].getWidth() * kWhiteKeysCount + whiteKeysTotalSpacing;
  28. const uint height = fImages[kWhiteKeyResourceIndex].getHeight();
  29. setSize(width, height);
  30. setupKeyLookupTable();
  31. setKeyImages();
  32. positionKeys();
  33. }
  34. void KeyboardWidget::setKeyPressed(const int keyIndex, const bool pressed, const bool sendCallback)
  35. {
  36. DISTRHO_SAFE_ASSERT_RETURN(keyIndex >= 0 && keyIndex < kKeyCount, )
  37. PianoKey& key = *fKeysLookup[keyIndex];
  38. if (key.isPressed() == pressed)
  39. return;
  40. key.setPressed(pressed);
  41. if (fCallback != nullptr && sendCallback)
  42. {
  43. if (pressed)
  44. {
  45. fCallback->keyboardKeyPressed(keyIndex);
  46. }
  47. else
  48. {
  49. fCallback->keyboardKeyReleased(keyIndex);
  50. }
  51. }
  52. repaint();
  53. }
  54. void KeyboardWidget::setCallback(Callback* callback)
  55. {
  56. fCallback = callback;
  57. }
  58. void KeyboardWidget::onDisplay()
  59. {
  60. // Draw the white keys.
  61. for (int i = 0; i < kWhiteKeysCount; ++i)
  62. {
  63. fWhiteKeys[i].draw();
  64. }
  65. // Draw the black keys, on top of the white keys.
  66. for (int i = 0; i < kBlackKeysCount; ++i)
  67. {
  68. fBlackKeys[i].draw();
  69. }
  70. }
  71. PianoKey* KeyboardWidget::tryGetHoveredKey(const Point<int>& point)
  72. {
  73. // Since the black keys are on top of the white keys, we check for a mouse event on the black ones first
  74. for (int i = 0; i < kBlackKeysCount; ++i)
  75. {
  76. if (fBlackKeys[i].contains(point))
  77. {
  78. return &fBlackKeys[i];
  79. }
  80. }
  81. // Check for mouse event on white keys
  82. for (int i = 0; i < kWhiteKeysCount; ++i)
  83. {
  84. if (fWhiteKeys[i].contains(point))
  85. {
  86. return &fWhiteKeys[i];
  87. }
  88. }
  89. return nullptr;
  90. }
  91. bool KeyboardWidget::onMouse(const MouseEvent& ev)
  92. {
  93. // We only care about left mouse button events.
  94. if (ev.button != 1)
  95. {
  96. return false;
  97. }
  98. fMouseDown = ev.press;
  99. if (!fMouseDown)
  100. {
  101. if (fHeldKey != nullptr)
  102. {
  103. setKeyPressed(fHeldKey->getIndex(), false, true);
  104. fHeldKey = nullptr;
  105. return true;
  106. }
  107. }
  108. if (!contains(ev.pos))
  109. {
  110. return false;
  111. }
  112. PianoKey* key = tryGetHoveredKey(ev.pos);
  113. if (key != nullptr)
  114. {
  115. setKeyPressed(key->getIndex(), ev.press, true);
  116. fHeldKey = key;
  117. return true;
  118. }
  119. return false;
  120. }
  121. bool KeyboardWidget::onMotion(const MotionEvent& ev)
  122. {
  123. if (!fMouseDown)
  124. {
  125. return false;
  126. }
  127. PianoKey* key = tryGetHoveredKey(ev.pos);
  128. if (key != fHeldKey)
  129. {
  130. if (fHeldKey != nullptr)
  131. {
  132. setKeyPressed(fHeldKey->getIndex(), false, true);
  133. }
  134. if (key != nullptr)
  135. {
  136. setKeyPressed(key->getIndex(), true, true);
  137. }
  138. fHeldKey = key;
  139. repaint();
  140. }
  141. return true;
  142. }
  143. void KeyboardWidget::setupKeyLookupTable()
  144. {
  145. int whiteKeysCounter = 0;
  146. int blackKeysCounter = 0;
  147. for (int i = 0; i < kKeyCount; ++i)
  148. {
  149. if (isBlackKey(i))
  150. {
  151. fKeysLookup[i] = &fBlackKeys[blackKeysCounter++];
  152. }
  153. else
  154. {
  155. fKeysLookup[i] = &fWhiteKeys[whiteKeysCounter++];
  156. }
  157. }
  158. }
  159. void KeyboardWidget::setKeyImages()
  160. {
  161. for (int i = 0; i < kWhiteKeysCount; ++i)
  162. {
  163. fWhiteKeys[i].setImages(fImages[kWhiteKeyResourceIndex], fImages[kWhiteKeyPressedResourceIndex]);
  164. }
  165. for (int i = 0; i < kBlackKeysCount; ++i)
  166. {
  167. fBlackKeys[i].setImages(fImages[kBlackKeyResourceIndex], fImages[kBlackKeyPressedResourceIndex]);
  168. }
  169. }
  170. void KeyboardWidget::positionKeys()
  171. {
  172. const int whiteKeyWidth = fImages[kWhiteKeyResourceIndex].getWidth();
  173. const int blackKeyWidth = fImages[kBlackKeyResourceIndex].getWidth();
  174. int whiteKeysCounter = 0;
  175. int blackKeysCounter = 0;
  176. for (int i = 0; i < kKeyCount; ++i)
  177. {
  178. const int totalSpacing = kWhiteKeySpacing * whiteKeysCounter;
  179. PianoKey* key;
  180. int xPos;
  181. if (isBlackKey(i))
  182. {
  183. key = &fBlackKeys[blackKeysCounter];
  184. xPos = whiteKeysCounter * whiteKeyWidth + totalSpacing - blackKeyWidth / 2;
  185. blackKeysCounter++;
  186. }
  187. else
  188. {
  189. key = &fWhiteKeys[whiteKeysCounter];
  190. xPos = whiteKeysCounter * whiteKeyWidth + totalSpacing;
  191. whiteKeysCounter++;
  192. }
  193. key->setPosition(xPos, 0);
  194. key->setIndex(i);
  195. }
  196. }
  197. bool KeyboardWidget::isWhiteKey(const uint noteIndex)
  198. {
  199. return !isBlackKey(noteIndex);
  200. }
  201. bool KeyboardWidget::isBlackKey(const uint noteIndex)
  202. {
  203. // Bring the index down to the first octave
  204. const uint adjustedIndex = noteIndex % 12;
  205. return adjustedIndex == 1
  206. || adjustedIndex == 3
  207. || adjustedIndex == 6
  208. || adjustedIndex == 8
  209. || adjustedIndex == 10;
  210. }
  211. END_NAMESPACE_DISTRHO