#include "KeyboardWidget.hpp" #include "resources/MidiKeyboardResources.hpp" START_NAMESPACE_DISTRHO KeyboardWidget::KeyboardWidget(Window& parent) : Widget(parent), fCallback(nullptr), fMouseDown(false), fHeldKey(nullptr) { fSVGs[kWhiteKeyResourceIndex].loadFromMemory(MidiKeyboardResources::white_keyData, MidiKeyboardResources::white_keyDataSize, 1.0f); fSVGs[kWhiteKeyPressedResourceIndex].loadFromMemory(MidiKeyboardResources::white_key_pressedData, MidiKeyboardResources::white_key_pressedDataSize, 1.0f); fSVGs[kBlackKeyResourceIndex].loadFromMemory(MidiKeyboardResources::black_keyData, MidiKeyboardResources::black_keyDataSize, 1.0f); fSVGs[kBlackKeyPressedResourceIndex].loadFromMemory(MidiKeyboardResources::black_key_pressedData, MidiKeyboardResources::black_key_pressedDataSize, 1.0f); for (int i = 0; i < kResourcesCount; ++i) { fImages[i].loadFromSVG(fSVGs[i]); } const int whiteKeysTotalSpacing = kWhiteKeySpacing * kWhiteKeysCount; const uint width = fImages[kWhiteKeyResourceIndex].getWidth() * kWhiteKeysCount + whiteKeysTotalSpacing; const uint height = fImages[kWhiteKeyResourceIndex].getHeight(); setSize(width, height); setupKeyLookupTable(); setKeyImages(); positionKeys(); } void KeyboardWidget::setKeyPressed(const int keyIndex, const bool pressed, const bool sendCallback) { DISTRHO_SAFE_ASSERT_RETURN(keyIndex >= 0 && keyIndex < kKeyCount, ) PianoKey& key = *fKeysLookup[keyIndex]; if (key.isPressed() == pressed) return; key.setPressed(pressed); if (fCallback != nullptr && sendCallback) { if (pressed) { fCallback->keyboardKeyPressed(keyIndex); } else { fCallback->keyboardKeyReleased(keyIndex); } } repaint(); } void KeyboardWidget::setCallback(Callback* callback) { fCallback = callback; } void KeyboardWidget::onDisplay() { // Draw the white keys. for (int i = 0; i < kWhiteKeysCount; ++i) { fWhiteKeys[i].draw(); } // Draw the black keys, on top of the white keys. for (int i = 0; i < kBlackKeysCount; ++i) { fBlackKeys[i].draw(); } } PianoKey* KeyboardWidget::tryGetHoveredKey(const Point& point) { // Since the black keys are on top of the white keys, we check for a mouse event on the black ones first for (int i = 0; i < kBlackKeysCount; ++i) { if (fBlackKeys[i].contains(point)) { return &fBlackKeys[i]; } } // Check for mouse event on white keys for (int i = 0; i < kWhiteKeysCount; ++i) { if (fWhiteKeys[i].contains(point)) { return &fWhiteKeys[i]; } } return nullptr; } bool KeyboardWidget::onMouse(const MouseEvent& ev) { // We only care about left mouse button events. if (ev.button != 1) { return false; } fMouseDown = ev.press; if (!fMouseDown) { if (fHeldKey != nullptr) { setKeyPressed(fHeldKey->getIndex(), false, true); fHeldKey = nullptr; return true; } } if (!contains(ev.pos)) { return false; } PianoKey* key = tryGetHoveredKey(ev.pos); if (key != nullptr) { setKeyPressed(key->getIndex(), ev.press, true); fHeldKey = key; return true; } return false; } bool KeyboardWidget::onMotion(const MotionEvent& ev) { if (!fMouseDown) { return false; } PianoKey* key = tryGetHoveredKey(ev.pos); if (key != fHeldKey) { if (fHeldKey != nullptr) { setKeyPressed(fHeldKey->getIndex(), false, true); } if (key != nullptr) { setKeyPressed(key->getIndex(), true, true); } fHeldKey = key; repaint(); } return true; } void KeyboardWidget::setupKeyLookupTable() { int whiteKeysCounter = 0; int blackKeysCounter = 0; for (int i = 0; i < kKeyCount; ++i) { if (isBlackKey(i)) { fKeysLookup[i] = &fBlackKeys[blackKeysCounter++]; } else { fKeysLookup[i] = &fWhiteKeys[whiteKeysCounter++]; } } } void KeyboardWidget::setKeyImages() { for (int i = 0; i < kWhiteKeysCount; ++i) { fWhiteKeys[i].setImages(fImages[kWhiteKeyResourceIndex], fImages[kWhiteKeyPressedResourceIndex]); } for (int i = 0; i < kBlackKeysCount; ++i) { fBlackKeys[i].setImages(fImages[kBlackKeyResourceIndex], fImages[kBlackKeyPressedResourceIndex]); } } void KeyboardWidget::positionKeys() { const int whiteKeyWidth = fImages[kWhiteKeyResourceIndex].getWidth(); const int blackKeyWidth = fImages[kBlackKeyResourceIndex].getWidth(); int whiteKeysCounter = 0; int blackKeysCounter = 0; for (int i = 0; i < kKeyCount; ++i) { const int totalSpacing = kWhiteKeySpacing * whiteKeysCounter; PianoKey* key; int xPos; if (isBlackKey(i)) { key = &fBlackKeys[blackKeysCounter]; xPos = whiteKeysCounter * whiteKeyWidth + totalSpacing - blackKeyWidth / 2; blackKeysCounter++; } else { key = &fWhiteKeys[whiteKeysCounter]; xPos = whiteKeysCounter * whiteKeyWidth + totalSpacing; whiteKeysCounter++; } key->setPosition(xPos, 0); key->setIndex(i); } } bool KeyboardWidget::isWhiteKey(const uint noteIndex) { return !isBlackKey(noteIndex); } bool KeyboardWidget::isBlackKey(const uint noteIndex) { // Bring the index down to the first octave const uint adjustedIndex = noteIndex % 12; return adjustedIndex == 1 || adjustedIndex == 3 || adjustedIndex == 6 || adjustedIndex == 8 || adjustedIndex == 10; } END_NAMESPACE_DISTRHO