The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
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.

883 lines
27KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-9 by Raw Material Software Ltd.
  5. ------------------------------------------------------------------------------
  6. JUCE can be redistributed and/or modified under the terms of the GNU General
  7. Public License (Version 2), as published by the Free Software Foundation.
  8. A copy of the license is included in the JUCE distribution, or can be found
  9. online at www.gnu.org/licenses.
  10. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  11. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  12. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  13. ------------------------------------------------------------------------------
  14. To release a closed-source product which uses JUCE, commercial licenses are
  15. available: visit www.rawmaterialsoftware.com/juce for more information.
  16. ==============================================================================
  17. */
  18. #include "../../../core/juce_StandardHeader.h"
  19. BEGIN_JUCE_NAMESPACE
  20. #include "juce_MidiKeyboardComponent.h"
  21. //==============================================================================
  22. class MidiKeyboardUpDownButton : public Button
  23. {
  24. public:
  25. MidiKeyboardUpDownButton (MidiKeyboardComponent* const owner_,
  26. const int delta_)
  27. : Button (String::empty),
  28. owner (owner_),
  29. delta (delta_)
  30. {
  31. setOpaque (true);
  32. }
  33. ~MidiKeyboardUpDownButton()
  34. {
  35. }
  36. void clicked()
  37. {
  38. int note = owner->getLowestVisibleKey();
  39. if (delta < 0)
  40. note = (note - 1) / 12;
  41. else
  42. note = note / 12 + 1;
  43. owner->setLowestVisibleKey (note * 12);
  44. }
  45. void paintButton (Graphics& g,
  46. bool isMouseOverButton,
  47. bool isButtonDown)
  48. {
  49. owner->drawUpDownButton (g, getWidth(), getHeight(),
  50. isMouseOverButton, isButtonDown,
  51. delta > 0);
  52. }
  53. private:
  54. MidiKeyboardComponent* const owner;
  55. const int delta;
  56. MidiKeyboardUpDownButton (const MidiKeyboardUpDownButton&);
  57. const MidiKeyboardUpDownButton& operator= (const MidiKeyboardUpDownButton&);
  58. };
  59. //==============================================================================
  60. MidiKeyboardComponent::MidiKeyboardComponent (MidiKeyboardState& state_,
  61. const Orientation orientation_)
  62. : state (state_),
  63. xOffset (0),
  64. blackNoteLength (1),
  65. keyWidth (16.0f),
  66. orientation (orientation_),
  67. midiChannel (1),
  68. midiInChannelMask (0xffff),
  69. velocity (1.0f),
  70. noteUnderMouse (-1),
  71. mouseDownNote (-1),
  72. rangeStart (0),
  73. rangeEnd (127),
  74. firstKey (12 * 4),
  75. canScroll (true),
  76. mouseDragging (false),
  77. useMousePositionForVelocity (true),
  78. keyMappingOctave (6),
  79. octaveNumForMiddleC (3)
  80. {
  81. addChildComponent (scrollDown = new MidiKeyboardUpDownButton (this, -1));
  82. addChildComponent (scrollUp = new MidiKeyboardUpDownButton (this, 1));
  83. // initialise with a default set of querty key-mappings..
  84. const char* const keymap = "awsedftgyhujkolp;";
  85. for (int i = String (keymap).length(); --i >= 0;)
  86. setKeyPressForNote (KeyPress (keymap[i], 0, 0), i);
  87. setOpaque (true);
  88. setWantsKeyboardFocus (true);
  89. state.addListener (this);
  90. }
  91. MidiKeyboardComponent::~MidiKeyboardComponent()
  92. {
  93. state.removeListener (this);
  94. jassert (mouseDownNote < 0 && keysPressed.countNumberOfSetBits() == 0); // leaving stuck notes!
  95. deleteAllChildren();
  96. }
  97. //==============================================================================
  98. void MidiKeyboardComponent::setKeyWidth (const float widthInPixels)
  99. {
  100. keyWidth = widthInPixels;
  101. resized();
  102. }
  103. void MidiKeyboardComponent::setOrientation (const Orientation newOrientation)
  104. {
  105. if (orientation != newOrientation)
  106. {
  107. orientation = newOrientation;
  108. resized();
  109. }
  110. }
  111. void MidiKeyboardComponent::setAvailableRange (const int lowestNote,
  112. const int highestNote)
  113. {
  114. jassert (lowestNote >= 0 && lowestNote <= 127);
  115. jassert (highestNote >= 0 && highestNote <= 127);
  116. jassert (lowestNote <= highestNote);
  117. if (rangeStart != lowestNote || rangeEnd != highestNote)
  118. {
  119. rangeStart = jlimit (0, 127, lowestNote);
  120. rangeEnd = jlimit (0, 127, highestNote);
  121. firstKey = jlimit (rangeStart, rangeEnd, firstKey);
  122. resized();
  123. }
  124. }
  125. void MidiKeyboardComponent::setLowestVisibleKey (int noteNumber)
  126. {
  127. noteNumber = jlimit (rangeStart, rangeEnd, noteNumber);
  128. if (noteNumber != firstKey)
  129. {
  130. firstKey = noteNumber;
  131. sendChangeMessage (this);
  132. resized();
  133. }
  134. }
  135. void MidiKeyboardComponent::setScrollButtonsVisible (const bool canScroll_)
  136. {
  137. if (canScroll != canScroll_)
  138. {
  139. canScroll = canScroll_;
  140. resized();
  141. }
  142. }
  143. void MidiKeyboardComponent::colourChanged()
  144. {
  145. repaint();
  146. }
  147. //==============================================================================
  148. void MidiKeyboardComponent::setMidiChannel (const int midiChannelNumber)
  149. {
  150. jassert (midiChannelNumber > 0 && midiChannelNumber <= 16);
  151. if (midiChannel != midiChannelNumber)
  152. {
  153. resetAnyKeysInUse();
  154. midiChannel = jlimit (1, 16, midiChannelNumber);
  155. }
  156. }
  157. void MidiKeyboardComponent::setMidiChannelsToDisplay (const int midiChannelMask)
  158. {
  159. midiInChannelMask = midiChannelMask;
  160. triggerAsyncUpdate();
  161. }
  162. void MidiKeyboardComponent::setVelocity (const float velocity_, const bool useMousePositionForVelocity_)
  163. {
  164. velocity = jlimit (0.0f, 1.0f, velocity_);
  165. useMousePositionForVelocity = useMousePositionForVelocity_;
  166. }
  167. //==============================================================================
  168. void MidiKeyboardComponent::getKeyPosition (int midiNoteNumber, const float keyWidth, int& x, int& w) const
  169. {
  170. jassert (midiNoteNumber >= 0 && midiNoteNumber < 128);
  171. static const float blackNoteWidth = 0.7f;
  172. static const float notePos[] = { 0.0f, 1 - blackNoteWidth * 0.6f,
  173. 1.0f, 2 - blackNoteWidth * 0.4f,
  174. 2.0f, 3.0f, 4 - blackNoteWidth * 0.7f,
  175. 4.0f, 5 - blackNoteWidth * 0.5f,
  176. 5.0f, 6 - blackNoteWidth * 0.3f,
  177. 6.0f };
  178. static const float widths[] = { 1.0f, blackNoteWidth,
  179. 1.0f, blackNoteWidth,
  180. 1.0f, 1.0f, blackNoteWidth,
  181. 1.0f, blackNoteWidth,
  182. 1.0f, blackNoteWidth,
  183. 1.0f };
  184. const int octave = midiNoteNumber / 12;
  185. const int note = midiNoteNumber % 12;
  186. x = roundFloatToInt (octave * 7.0f * keyWidth + notePos [note] * keyWidth);
  187. w = roundFloatToInt (widths [note] * keyWidth);
  188. }
  189. void MidiKeyboardComponent::getKeyPos (int midiNoteNumber, int& x, int& w) const
  190. {
  191. getKeyPosition (midiNoteNumber, keyWidth, x, w);
  192. int rx, rw;
  193. getKeyPosition (rangeStart, keyWidth, rx, rw);
  194. x -= xOffset + rx;
  195. }
  196. int MidiKeyboardComponent::getKeyStartPosition (const int midiNoteNumber) const
  197. {
  198. int x, y;
  199. getKeyPos (midiNoteNumber, x, y);
  200. return x;
  201. }
  202. static const uint8 whiteNotes[] = { 0, 2, 4, 5, 7, 9, 11 };
  203. static const uint8 blackNotes[] = { 1, 3, 6, 8, 10 };
  204. int MidiKeyboardComponent::xyToNote (int x, int y, float& mousePositionVelocity)
  205. {
  206. if (! reallyContains (x, y, false))
  207. return -1;
  208. if (orientation != horizontalKeyboard)
  209. {
  210. swapVariables (x, y);
  211. if (orientation == verticalKeyboardFacingLeft)
  212. y = getWidth() - y;
  213. else
  214. x = getHeight() - x;
  215. }
  216. return remappedXYToNote (x + xOffset, y, mousePositionVelocity);
  217. }
  218. int MidiKeyboardComponent::remappedXYToNote (int x, int y, float& mousePositionVelocity) const
  219. {
  220. if (y < blackNoteLength)
  221. {
  222. for (int octaveStart = 12 * (rangeStart / 12); octaveStart <= rangeEnd; octaveStart += 12)
  223. {
  224. for (int i = 0; i < 5; ++i)
  225. {
  226. const int note = octaveStart + blackNotes [i];
  227. if (note >= rangeStart && note <= rangeEnd)
  228. {
  229. int kx, kw;
  230. getKeyPos (note, kx, kw);
  231. kx += xOffset;
  232. if (x >= kx && x < kx + kw)
  233. {
  234. mousePositionVelocity = y / (float) blackNoteLength;
  235. return note;
  236. }
  237. }
  238. }
  239. }
  240. }
  241. for (int octaveStart = 12 * (rangeStart / 12); octaveStart <= rangeEnd; octaveStart += 12)
  242. {
  243. for (int i = 0; i < 7; ++i)
  244. {
  245. const int note = octaveStart + whiteNotes [i];
  246. if (note >= rangeStart && note <= rangeEnd)
  247. {
  248. int kx, kw;
  249. getKeyPos (note, kx, kw);
  250. kx += xOffset;
  251. if (x >= kx && x < kx + kw)
  252. {
  253. mousePositionVelocity = y / (float) getHeight();
  254. return note;
  255. }
  256. }
  257. }
  258. }
  259. mousePositionVelocity = 0;
  260. return -1;
  261. }
  262. //==============================================================================
  263. void MidiKeyboardComponent::repaintNote (const int noteNum)
  264. {
  265. if (noteNum >= rangeStart && noteNum <= rangeEnd)
  266. {
  267. int x, w;
  268. getKeyPos (noteNum, x, w);
  269. if (orientation == horizontalKeyboard)
  270. repaint (x, 0, w, getHeight());
  271. else if (orientation == verticalKeyboardFacingLeft)
  272. repaint (0, x, getWidth(), w);
  273. else if (orientation == verticalKeyboardFacingRight)
  274. repaint (0, getHeight() - x - w, getWidth(), w);
  275. }
  276. }
  277. void MidiKeyboardComponent::paint (Graphics& g)
  278. {
  279. g.fillAll (Colours::white.overlaidWith (findColour (whiteNoteColourId)));
  280. const Colour lineColour (findColour (keySeparatorLineColourId));
  281. const Colour textColour (findColour (textLabelColourId));
  282. int x, w, octave;
  283. for (octave = 0; octave < 128; octave += 12)
  284. {
  285. for (int white = 0; white < 7; ++white)
  286. {
  287. const int noteNum = octave + whiteNotes [white];
  288. if (noteNum >= rangeStart && noteNum <= rangeEnd)
  289. {
  290. getKeyPos (noteNum, x, w);
  291. if (orientation == horizontalKeyboard)
  292. drawWhiteNote (noteNum, g, x, 0, w, getHeight(),
  293. state.isNoteOnForChannels (midiInChannelMask, noteNum),
  294. noteUnderMouse == noteNum,
  295. lineColour, textColour);
  296. else if (orientation == verticalKeyboardFacingLeft)
  297. drawWhiteNote (noteNum, g, 0, x, getWidth(), w,
  298. state.isNoteOnForChannels (midiInChannelMask, noteNum),
  299. noteUnderMouse == noteNum,
  300. lineColour, textColour);
  301. else if (orientation == verticalKeyboardFacingRight)
  302. drawWhiteNote (noteNum, g, 0, getHeight() - x - w, getWidth(), w,
  303. state.isNoteOnForChannels (midiInChannelMask, noteNum),
  304. noteUnderMouse == noteNum,
  305. lineColour, textColour);
  306. }
  307. }
  308. }
  309. float x1 = 0.0f, y1 = 0.0f, x2 = 0.0f, y2 = 0.0f;
  310. if (orientation == verticalKeyboardFacingLeft)
  311. {
  312. x1 = getWidth() - 1.0f;
  313. x2 = getWidth() - 5.0f;
  314. }
  315. else if (orientation == verticalKeyboardFacingRight)
  316. x2 = 5.0f;
  317. else
  318. y2 = 5.0f;
  319. g.setGradientFill (ColourGradient (Colours::black.withAlpha (0.3f), x1, y1,
  320. Colours::transparentBlack, x2, y2, false));
  321. getKeyPos (rangeEnd, x, w);
  322. x += w;
  323. if (orientation == verticalKeyboardFacingLeft)
  324. g.fillRect (getWidth() - 5, 0, 5, x);
  325. else if (orientation == verticalKeyboardFacingRight)
  326. g.fillRect (0, 0, 5, x);
  327. else
  328. g.fillRect (0, 0, x, 5);
  329. g.setColour (lineColour);
  330. if (orientation == verticalKeyboardFacingLeft)
  331. g.fillRect (0, 0, 1, x);
  332. else if (orientation == verticalKeyboardFacingRight)
  333. g.fillRect (getWidth() - 1, 0, 1, x);
  334. else
  335. g.fillRect (0, getHeight() - 1, x, 1);
  336. const Colour blackNoteColour (findColour (blackNoteColourId));
  337. for (octave = 0; octave < 128; octave += 12)
  338. {
  339. for (int black = 0; black < 5; ++black)
  340. {
  341. const int noteNum = octave + blackNotes [black];
  342. if (noteNum >= rangeStart && noteNum <= rangeEnd)
  343. {
  344. getKeyPos (noteNum, x, w);
  345. if (orientation == horizontalKeyboard)
  346. drawBlackNote (noteNum, g, x, 0, w, blackNoteLength,
  347. state.isNoteOnForChannels (midiInChannelMask, noteNum),
  348. noteUnderMouse == noteNum,
  349. blackNoteColour);
  350. else if (orientation == verticalKeyboardFacingLeft)
  351. drawBlackNote (noteNum, g, getWidth() - blackNoteLength, x, blackNoteLength, w,
  352. state.isNoteOnForChannels (midiInChannelMask, noteNum),
  353. noteUnderMouse == noteNum,
  354. blackNoteColour);
  355. else if (orientation == verticalKeyboardFacingRight)
  356. drawBlackNote (noteNum, g, 0, getHeight() - x - w, blackNoteLength, w,
  357. state.isNoteOnForChannels (midiInChannelMask, noteNum),
  358. noteUnderMouse == noteNum,
  359. blackNoteColour);
  360. }
  361. }
  362. }
  363. }
  364. void MidiKeyboardComponent::drawWhiteNote (int midiNoteNumber,
  365. Graphics& g, int x, int y, int w, int h,
  366. bool isDown, bool isOver,
  367. const Colour& lineColour,
  368. const Colour& textColour)
  369. {
  370. Colour c (Colours::transparentWhite);
  371. if (isDown)
  372. c = findColour (keyDownOverlayColourId);
  373. if (isOver)
  374. c = c.overlaidWith (findColour (mouseOverKeyOverlayColourId));
  375. g.setColour (c);
  376. g.fillRect (x, y, w, h);
  377. const String text (getWhiteNoteText (midiNoteNumber));
  378. if (! text.isEmpty())
  379. {
  380. g.setColour (textColour);
  381. Font f (jmin (12.0f, keyWidth * 0.9f));
  382. f.setHorizontalScale (0.8f);
  383. g.setFont (f);
  384. Justification justification (Justification::centredBottom);
  385. if (orientation == verticalKeyboardFacingLeft)
  386. justification = Justification::centredLeft;
  387. else if (orientation == verticalKeyboardFacingRight)
  388. justification = Justification::centredRight;
  389. g.drawFittedText (text, x + 2, y + 2, w - 4, h - 4, justification, 1);
  390. }
  391. g.setColour (lineColour);
  392. if (orientation == horizontalKeyboard)
  393. g.fillRect (x, y, 1, h);
  394. else if (orientation == verticalKeyboardFacingLeft)
  395. g.fillRect (x, y, w, 1);
  396. else if (orientation == verticalKeyboardFacingRight)
  397. g.fillRect (x, y + h - 1, w, 1);
  398. if (midiNoteNumber == rangeEnd)
  399. {
  400. if (orientation == horizontalKeyboard)
  401. g.fillRect (x + w, y, 1, h);
  402. else if (orientation == verticalKeyboardFacingLeft)
  403. g.fillRect (x, y + h, w, 1);
  404. else if (orientation == verticalKeyboardFacingRight)
  405. g.fillRect (x, y - 1, w, 1);
  406. }
  407. }
  408. void MidiKeyboardComponent::drawBlackNote (int /*midiNoteNumber*/,
  409. Graphics& g, int x, int y, int w, int h,
  410. bool isDown, bool isOver,
  411. const Colour& noteFillColour)
  412. {
  413. Colour c (noteFillColour);
  414. if (isDown)
  415. c = c.overlaidWith (findColour (keyDownOverlayColourId));
  416. if (isOver)
  417. c = c.overlaidWith (findColour (mouseOverKeyOverlayColourId));
  418. g.setColour (c);
  419. g.fillRect (x, y, w, h);
  420. if (isDown)
  421. {
  422. g.setColour (noteFillColour);
  423. g.drawRect (x, y, w, h);
  424. }
  425. else
  426. {
  427. const int xIndent = jmax (1, jmin (w, h) / 8);
  428. g.setColour (c.brighter());
  429. if (orientation == horizontalKeyboard)
  430. g.fillRect (x + xIndent, y, w - xIndent * 2, 7 * h / 8);
  431. else if (orientation == verticalKeyboardFacingLeft)
  432. g.fillRect (x + w / 8, y + xIndent, w - w / 8, h - xIndent * 2);
  433. else if (orientation == verticalKeyboardFacingRight)
  434. g.fillRect (x, y + xIndent, 7 * w / 8, h - xIndent * 2);
  435. }
  436. }
  437. void MidiKeyboardComponent::setOctaveForMiddleC (const int octaveNumForMiddleC_) throw()
  438. {
  439. octaveNumForMiddleC = octaveNumForMiddleC_;
  440. repaint();
  441. }
  442. const String MidiKeyboardComponent::getWhiteNoteText (const int midiNoteNumber)
  443. {
  444. if (keyWidth > 14.0f && midiNoteNumber % 12 == 0)
  445. return MidiMessage::getMidiNoteName (midiNoteNumber, true, true, octaveNumForMiddleC);
  446. return String::empty;
  447. }
  448. void MidiKeyboardComponent::drawUpDownButton (Graphics& g, int w, int h,
  449. const bool isMouseOver,
  450. const bool isButtonDown,
  451. const bool movesOctavesUp)
  452. {
  453. g.fillAll (findColour (upDownButtonBackgroundColourId));
  454. float angle;
  455. if (orientation == MidiKeyboardComponent::horizontalKeyboard)
  456. angle = movesOctavesUp ? 0.0f : 0.5f;
  457. else if (orientation == MidiKeyboardComponent::verticalKeyboardFacingLeft)
  458. angle = movesOctavesUp ? 0.25f : 0.75f;
  459. else
  460. angle = movesOctavesUp ? 0.75f : 0.25f;
  461. Path path;
  462. path.lineTo (0.0f, 1.0f);
  463. path.lineTo (1.0f, 0.5f);
  464. path.closeSubPath();
  465. path.applyTransform (AffineTransform::rotation (float_Pi * 2.0f * angle, 0.5f, 0.5f));
  466. g.setColour (findColour (upDownButtonArrowColourId)
  467. .withAlpha (isButtonDown ? 1.0f : (isMouseOver ? 0.6f : 0.4f)));
  468. g.fillPath (path, path.getTransformToScaleToFit (1.0f, 1.0f,
  469. w - 2.0f,
  470. h - 2.0f,
  471. true));
  472. }
  473. void MidiKeyboardComponent::resized()
  474. {
  475. int w = getWidth();
  476. int h = getHeight();
  477. if (w > 0 && h > 0)
  478. {
  479. if (orientation != horizontalKeyboard)
  480. swapVariables (w, h);
  481. blackNoteLength = roundFloatToInt (h * 0.7f);
  482. int kx2, kw2;
  483. getKeyPos (rangeEnd, kx2, kw2);
  484. kx2 += kw2;
  485. if (firstKey != rangeStart)
  486. {
  487. int kx1, kw1;
  488. getKeyPos (rangeStart, kx1, kw1);
  489. if (kx2 - kx1 <= w)
  490. {
  491. firstKey = rangeStart;
  492. sendChangeMessage (this);
  493. repaint();
  494. }
  495. }
  496. const bool showScrollButtons = canScroll && (firstKey > rangeStart || kx2 > w + xOffset * 2);
  497. scrollDown->setVisible (showScrollButtons);
  498. scrollUp->setVisible (showScrollButtons);
  499. xOffset = 0;
  500. if (showScrollButtons)
  501. {
  502. const int scrollButtonW = jmin (12, w / 2);
  503. if (orientation == horizontalKeyboard)
  504. {
  505. scrollDown->setBounds (0, 0, scrollButtonW, getHeight());
  506. scrollUp->setBounds (getWidth() - scrollButtonW, 0, scrollButtonW, getHeight());
  507. }
  508. else if (orientation == verticalKeyboardFacingLeft)
  509. {
  510. scrollDown->setBounds (0, 0, getWidth(), scrollButtonW);
  511. scrollUp->setBounds (0, getHeight() - scrollButtonW, getWidth(), scrollButtonW);
  512. }
  513. else if (orientation == verticalKeyboardFacingRight)
  514. {
  515. scrollDown->setBounds (0, getHeight() - scrollButtonW, getWidth(), scrollButtonW);
  516. scrollUp->setBounds (0, 0, getWidth(), scrollButtonW);
  517. }
  518. int endOfLastKey, kw;
  519. getKeyPos (rangeEnd, endOfLastKey, kw);
  520. endOfLastKey += kw;
  521. float mousePositionVelocity;
  522. const int spaceAvailable = w - scrollButtonW * 2;
  523. const int lastStartKey = remappedXYToNote (endOfLastKey - spaceAvailable, 0, mousePositionVelocity) + 1;
  524. if (lastStartKey >= 0 && firstKey > lastStartKey)
  525. {
  526. firstKey = jlimit (rangeStart, rangeEnd, lastStartKey);
  527. sendChangeMessage (this);
  528. }
  529. int newOffset = 0;
  530. getKeyPos (firstKey, newOffset, kw);
  531. xOffset = newOffset - scrollButtonW;
  532. }
  533. else
  534. {
  535. firstKey = rangeStart;
  536. }
  537. timerCallback();
  538. repaint();
  539. }
  540. }
  541. //==============================================================================
  542. void MidiKeyboardComponent::handleNoteOn (MidiKeyboardState*, int /*midiChannel*/, int /*midiNoteNumber*/, float /*velocity*/)
  543. {
  544. triggerAsyncUpdate();
  545. }
  546. void MidiKeyboardComponent::handleNoteOff (MidiKeyboardState*, int /*midiChannel*/, int /*midiNoteNumber*/)
  547. {
  548. triggerAsyncUpdate();
  549. }
  550. void MidiKeyboardComponent::handleAsyncUpdate()
  551. {
  552. for (int i = rangeStart; i <= rangeEnd; ++i)
  553. {
  554. if (keysCurrentlyDrawnDown[i] != state.isNoteOnForChannels (midiInChannelMask, i))
  555. {
  556. keysCurrentlyDrawnDown.setBit (i, state.isNoteOnForChannels (midiInChannelMask, i));
  557. repaintNote (i);
  558. }
  559. }
  560. }
  561. //==============================================================================
  562. void MidiKeyboardComponent::resetAnyKeysInUse()
  563. {
  564. if (keysPressed.countNumberOfSetBits() > 0 || mouseDownNote > 0)
  565. {
  566. state.allNotesOff (midiChannel);
  567. keysPressed.clear();
  568. mouseDownNote = -1;
  569. }
  570. }
  571. void MidiKeyboardComponent::updateNoteUnderMouse (int x, int y)
  572. {
  573. float mousePositionVelocity = 0.0f;
  574. const int newNote = (mouseDragging || isMouseOver())
  575. ? xyToNote (x, y, mousePositionVelocity) : -1;
  576. if (noteUnderMouse != newNote)
  577. {
  578. if (mouseDownNote >= 0)
  579. {
  580. state.noteOff (midiChannel, mouseDownNote);
  581. mouseDownNote = -1;
  582. }
  583. if (mouseDragging && newNote >= 0)
  584. {
  585. if (! useMousePositionForVelocity)
  586. mousePositionVelocity = 1.0f;
  587. state.noteOn (midiChannel, newNote, mousePositionVelocity * velocity);
  588. mouseDownNote = newNote;
  589. }
  590. repaintNote (noteUnderMouse);
  591. noteUnderMouse = newNote;
  592. repaintNote (noteUnderMouse);
  593. }
  594. else if (mouseDownNote >= 0 && ! mouseDragging)
  595. {
  596. state.noteOff (midiChannel, mouseDownNote);
  597. mouseDownNote = -1;
  598. }
  599. }
  600. void MidiKeyboardComponent::mouseMove (const MouseEvent& e)
  601. {
  602. updateNoteUnderMouse (e.x, e.y);
  603. stopTimer();
  604. }
  605. void MidiKeyboardComponent::mouseDrag (const MouseEvent& e)
  606. {
  607. float mousePositionVelocity;
  608. const int newNote = xyToNote (e.x, e.y, mousePositionVelocity);
  609. if (newNote >= 0)
  610. mouseDraggedToKey (newNote, e);
  611. updateNoteUnderMouse (e.x, e.y);
  612. }
  613. bool MidiKeyboardComponent::mouseDownOnKey (int /*midiNoteNumber*/, const MouseEvent&)
  614. {
  615. return true;
  616. }
  617. void MidiKeyboardComponent::mouseDraggedToKey (int /*midiNoteNumber*/, const MouseEvent&)
  618. {
  619. }
  620. void MidiKeyboardComponent::mouseDown (const MouseEvent& e)
  621. {
  622. float mousePositionVelocity;
  623. const int newNote = xyToNote (e.x, e.y, mousePositionVelocity);
  624. mouseDragging = false;
  625. if (newNote >= 0 && mouseDownOnKey (newNote, e))
  626. {
  627. repaintNote (noteUnderMouse);
  628. noteUnderMouse = -1;
  629. mouseDragging = true;
  630. updateNoteUnderMouse (e.x, e.y);
  631. startTimer (500);
  632. }
  633. }
  634. void MidiKeyboardComponent::mouseUp (const MouseEvent& e)
  635. {
  636. mouseDragging = false;
  637. updateNoteUnderMouse (e.x, e.y);
  638. stopTimer();
  639. }
  640. void MidiKeyboardComponent::mouseEnter (const MouseEvent& e)
  641. {
  642. updateNoteUnderMouse (e.x, e.y);
  643. }
  644. void MidiKeyboardComponent::mouseExit (const MouseEvent& e)
  645. {
  646. updateNoteUnderMouse (e.x, e.y);
  647. }
  648. void MidiKeyboardComponent::mouseWheelMove (const MouseEvent&, float ix, float iy)
  649. {
  650. setLowestVisibleKey (getLowestVisibleKey() + roundFloatToInt ((ix != 0 ? ix : iy) * 5.0f));
  651. }
  652. void MidiKeyboardComponent::timerCallback()
  653. {
  654. int mx, my;
  655. getMouseXYRelative (mx, my);
  656. updateNoteUnderMouse (mx, my);
  657. }
  658. //==============================================================================
  659. void MidiKeyboardComponent::clearKeyMappings()
  660. {
  661. resetAnyKeysInUse();
  662. keyPressNotes.clear();
  663. keyPresses.clear();
  664. }
  665. void MidiKeyboardComponent::setKeyPressForNote (const KeyPress& key,
  666. const int midiNoteOffsetFromC)
  667. {
  668. removeKeyPressForNote (midiNoteOffsetFromC);
  669. keyPressNotes.add (midiNoteOffsetFromC);
  670. keyPresses.add (key);
  671. }
  672. void MidiKeyboardComponent::removeKeyPressForNote (const int midiNoteOffsetFromC)
  673. {
  674. for (int i = keyPressNotes.size(); --i >= 0;)
  675. {
  676. if (keyPressNotes.getUnchecked (i) == midiNoteOffsetFromC)
  677. {
  678. keyPressNotes.remove (i);
  679. keyPresses.remove (i);
  680. }
  681. }
  682. }
  683. void MidiKeyboardComponent::setKeyPressBaseOctave (const int newOctaveNumber)
  684. {
  685. jassert (newOctaveNumber >= 0 && newOctaveNumber <= 10);
  686. keyMappingOctave = newOctaveNumber;
  687. }
  688. bool MidiKeyboardComponent::keyStateChanged (const bool /*isKeyDown*/)
  689. {
  690. bool keyPressUsed = false;
  691. for (int i = keyPresses.size(); --i >= 0;)
  692. {
  693. const int note = 12 * keyMappingOctave + keyPressNotes.getUnchecked (i);
  694. if (keyPresses.getReference(i).isCurrentlyDown())
  695. {
  696. if (! keysPressed [note])
  697. {
  698. keysPressed.setBit (note);
  699. state.noteOn (midiChannel, note, velocity);
  700. keyPressUsed = true;
  701. }
  702. }
  703. else
  704. {
  705. if (keysPressed [note])
  706. {
  707. keysPressed.clearBit (note);
  708. state.noteOff (midiChannel, note);
  709. keyPressUsed = true;
  710. }
  711. }
  712. }
  713. return keyPressUsed;
  714. }
  715. void MidiKeyboardComponent::focusLost (FocusChangeType)
  716. {
  717. resetAnyKeysInUse();
  718. }
  719. END_JUCE_NAMESPACE