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.

877 lines
27KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-7 by Raw Material Software ltd.
  5. ------------------------------------------------------------------------------
  6. JUCE can be redistributed and/or modified under the terms of the
  7. GNU General Public License, as published by the Free Software Foundation;
  8. either version 2 of the License, or (at your option) any later version.
  9. JUCE is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. GNU General Public License for more details.
  13. You should have received a copy of the GNU General Public License
  14. along with JUCE; if not, visit www.gnu.org/licenses or write to the
  15. Free Software Foundation, Inc., 59 Temple Place, Suite 330,
  16. Boston, MA 02111-1307 USA
  17. ------------------------------------------------------------------------------
  18. If you'd like to release a closed-source product which uses JUCE, commercial
  19. licenses are also available: visit www.rawmaterialsoftware.com/juce for
  20. more information.
  21. ==============================================================================
  22. */
  23. #include "../../../../juce_core/basics/juce_StandardHeader.h"
  24. BEGIN_JUCE_NAMESPACE
  25. #include "juce_MidiKeyboardComponent.h"
  26. #include "../../graphics/brushes/juce_GradientBrush.h"
  27. //==============================================================================
  28. class MidiKeyboardUpDownButton : public Button
  29. {
  30. public:
  31. MidiKeyboardUpDownButton (MidiKeyboardComponent* const owner_,
  32. const int delta_)
  33. : Button (String::empty),
  34. owner (owner_),
  35. delta (delta_)
  36. {
  37. setOpaque (true);
  38. }
  39. ~MidiKeyboardUpDownButton()
  40. {
  41. }
  42. void clicked()
  43. {
  44. int note = owner->getLowestVisibleKey();
  45. if (delta < 0)
  46. note = (note - 1) / 12;
  47. else
  48. note = note / 12 + 1;
  49. owner->setLowestVisibleKey (note * 12);
  50. }
  51. void paintButton (Graphics& g,
  52. bool isMouseOverButton,
  53. bool isButtonDown)
  54. {
  55. owner->drawUpDownButton (g, getWidth(), getHeight(),
  56. isMouseOverButton, isButtonDown,
  57. delta > 0);
  58. }
  59. private:
  60. MidiKeyboardComponent* const owner;
  61. const int delta;
  62. MidiKeyboardUpDownButton (const MidiKeyboardUpDownButton&);
  63. const MidiKeyboardUpDownButton& operator= (const MidiKeyboardUpDownButton&);
  64. };
  65. //==============================================================================
  66. MidiKeyboardComponent::MidiKeyboardComponent (MidiKeyboardState& state_,
  67. const Orientation orientation_)
  68. : state (state_),
  69. xOffset (0),
  70. blackNoteLength (1),
  71. keyWidth (16.0f),
  72. orientation (orientation_),
  73. midiChannel (1),
  74. midiInChannelMask (0xffff),
  75. velocity (1.0f),
  76. noteUnderMouse (-1),
  77. mouseDownNote (-1),
  78. rangeStart (0),
  79. rangeEnd (127),
  80. firstKey (12 * 4),
  81. canScroll (true),
  82. mouseDragging (false),
  83. keyPresses (4),
  84. keyPressNotes (16),
  85. keyMappingOctave (6),
  86. octaveNumForMiddleC (3)
  87. {
  88. addChildComponent (scrollDown = new MidiKeyboardUpDownButton (this, -1));
  89. addChildComponent (scrollUp = new MidiKeyboardUpDownButton (this, 1));
  90. // initialise with a default set of querty key-mappings..
  91. const char* const keymap = "awsedftgyhujkolp;";
  92. for (int i = String (keymap).length(); --i >= 0;)
  93. setKeyPressForNote (KeyPress (keymap[i], 0, 0), i);
  94. setOpaque (true);
  95. setWantsKeyboardFocus (true);
  96. state.addListener (this);
  97. }
  98. MidiKeyboardComponent::~MidiKeyboardComponent()
  99. {
  100. state.removeListener (this);
  101. jassert (mouseDownNote < 0 && keysPressed.countNumberOfSetBits() == 0); // leaving stuck notes!
  102. deleteAllChildren();
  103. }
  104. //==============================================================================
  105. void MidiKeyboardComponent::setKeyWidth (const float widthInPixels)
  106. {
  107. keyWidth = widthInPixels;
  108. resized();
  109. }
  110. void MidiKeyboardComponent::setOrientation (const Orientation newOrientation)
  111. {
  112. if (orientation != newOrientation)
  113. {
  114. orientation = newOrientation;
  115. resized();
  116. }
  117. }
  118. void MidiKeyboardComponent::setAvailableRange (const int lowestNote,
  119. const int highestNote)
  120. {
  121. jassert (lowestNote >= 0 && lowestNote <= 127);
  122. jassert (highestNote >= 0 && highestNote <= 127);
  123. jassert (lowestNote <= highestNote);
  124. if (rangeStart != lowestNote || rangeEnd != highestNote)
  125. {
  126. rangeStart = jlimit (0, 127, lowestNote);
  127. rangeEnd = jlimit (0, 127, highestNote);
  128. firstKey = jlimit (rangeStart, rangeEnd, firstKey);
  129. resized();
  130. }
  131. }
  132. void MidiKeyboardComponent::setLowestVisibleKey (int noteNumber)
  133. {
  134. noteNumber = jlimit (rangeStart, rangeEnd, noteNumber);
  135. if (noteNumber != firstKey)
  136. {
  137. firstKey = noteNumber;
  138. sendChangeMessage (this);
  139. resized();
  140. }
  141. }
  142. void MidiKeyboardComponent::setScrollButtonsVisible (const bool canScroll_)
  143. {
  144. if (canScroll != canScroll_)
  145. {
  146. canScroll = canScroll_;
  147. resized();
  148. }
  149. }
  150. void MidiKeyboardComponent::colourChanged()
  151. {
  152. repaint();
  153. }
  154. //==============================================================================
  155. void MidiKeyboardComponent::setMidiChannel (const int midiChannelNumber)
  156. {
  157. jassert (midiChannelNumber > 0 && midiChannelNumber <= 16);
  158. if (midiChannel != midiChannelNumber)
  159. {
  160. resetAnyKeysInUse();
  161. midiChannel = jlimit (1, 16, midiChannelNumber);
  162. }
  163. }
  164. void MidiKeyboardComponent::setMidiChannelsToDisplay (const int midiChannelMask)
  165. {
  166. midiInChannelMask = midiChannelMask;
  167. triggerAsyncUpdate();
  168. }
  169. void MidiKeyboardComponent::setVelocity (const float velocity_)
  170. {
  171. velocity = jlimit (0.0f, 1.0f, velocity_);
  172. }
  173. //==============================================================================
  174. void MidiKeyboardComponent::getKeyPosition (int midiNoteNumber, const float keyWidth, int& x, int& w) const
  175. {
  176. jassert (midiNoteNumber >= 0 && midiNoteNumber < 128);
  177. static const float blackNoteWidth = 0.7f;
  178. static const float notePos[] = { 0.0f, 1 - blackNoteWidth * 0.6f,
  179. 1.0f, 2 - blackNoteWidth * 0.4f,
  180. 2.0f, 3.0f, 4 - blackNoteWidth * 0.7f,
  181. 4.0f, 5 - blackNoteWidth * 0.5f,
  182. 5.0f, 6 - blackNoteWidth * 0.3f,
  183. 6.0f };
  184. static const float widths[] = { 1.0f, blackNoteWidth,
  185. 1.0f, blackNoteWidth,
  186. 1.0f, 1.0f, blackNoteWidth,
  187. 1.0f, blackNoteWidth,
  188. 1.0f, blackNoteWidth,
  189. 1.0f };
  190. const int octave = midiNoteNumber / 12;
  191. const int note = midiNoteNumber % 12;
  192. x = roundFloatToInt (octave * 7.0f * keyWidth + notePos [note] * keyWidth);
  193. w = roundFloatToInt (widths [note] * keyWidth);
  194. }
  195. void MidiKeyboardComponent::getKeyPos (int midiNoteNumber, int& x, int& w) const
  196. {
  197. getKeyPosition (midiNoteNumber, keyWidth, x, w);
  198. int rx, rw;
  199. getKeyPosition (rangeStart, keyWidth, rx, rw);
  200. x -= xOffset + rx;
  201. }
  202. int MidiKeyboardComponent::getKeyStartPosition (const int midiNoteNumber) const
  203. {
  204. int x, y;
  205. getKeyPos (midiNoteNumber, x, y);
  206. return x;
  207. }
  208. static const uint8 whiteNotes[] = { 0, 2, 4, 5, 7, 9, 11 };
  209. static const uint8 blackNotes[] = { 1, 3, 6, 8, 10 };
  210. int MidiKeyboardComponent::xyToNote (int x, int y)
  211. {
  212. if (! reallyContains (x, y, false))
  213. return -1;
  214. if (orientation != horizontalKeyboard)
  215. {
  216. swapVariables (x, y);
  217. if (orientation == verticalKeyboardFacingLeft)
  218. y = getWidth() - y;
  219. else
  220. x = getHeight() - x;
  221. }
  222. return remappedXYToNote (x + xOffset, y);
  223. }
  224. int MidiKeyboardComponent::remappedXYToNote (int x, int y) const
  225. {
  226. if (y < blackNoteLength)
  227. {
  228. for (int octaveStart = 12 * (rangeStart / 12); octaveStart <= rangeEnd; octaveStart += 12)
  229. {
  230. for (int i = 0; i < 5; ++i)
  231. {
  232. const int note = octaveStart + blackNotes [i];
  233. if (note >= rangeStart && note <= rangeEnd)
  234. {
  235. int kx, kw;
  236. getKeyPos (note, kx, kw);
  237. kx += xOffset;
  238. if (x >= kx && x < kx + kw)
  239. return note;
  240. }
  241. }
  242. }
  243. }
  244. for (int octaveStart = 12 * (rangeStart / 12); octaveStart <= rangeEnd; octaveStart += 12)
  245. {
  246. for (int i = 0; i < 7; ++i)
  247. {
  248. const int note = octaveStart + whiteNotes [i];
  249. if (note >= rangeStart && note <= rangeEnd)
  250. {
  251. int kx, kw;
  252. getKeyPos (note, kx, kw);
  253. kx += xOffset;
  254. if (x >= kx && x < kx + kw)
  255. return note;
  256. }
  257. }
  258. }
  259. return -1;
  260. }
  261. //==============================================================================
  262. void MidiKeyboardComponent::repaintNote (const int noteNum)
  263. {
  264. if (noteNum >= rangeStart && noteNum <= rangeEnd)
  265. {
  266. int x, w;
  267. getKeyPos (noteNum, x, w);
  268. if (orientation == horizontalKeyboard)
  269. repaint (x, 0, w, getHeight());
  270. else if (orientation == verticalKeyboardFacingLeft)
  271. repaint (0, x, getWidth(), w);
  272. else if (orientation == verticalKeyboardFacingRight)
  273. repaint (0, getHeight() - x - w, getWidth(), w);
  274. }
  275. }
  276. void MidiKeyboardComponent::paint (Graphics& g)
  277. {
  278. g.fillAll (Colours::white.overlaidWith (findColour (whiteNoteColourId)));
  279. const Colour lineColour (findColour (keySeparatorLineColourId));
  280. const Colour textColour (findColour (textLabelColourId));
  281. int x, w, octave;
  282. for (octave = 0; octave < 128; octave += 12)
  283. {
  284. for (int white = 0; white < 7; ++white)
  285. {
  286. const int noteNum = octave + whiteNotes [white];
  287. if (noteNum >= rangeStart && noteNum <= rangeEnd)
  288. {
  289. getKeyPos (noteNum, x, w);
  290. if (orientation == horizontalKeyboard)
  291. drawWhiteNote (noteNum, g, x, 0, w, getHeight(),
  292. state.isNoteOnForChannels (midiInChannelMask, noteNum),
  293. noteUnderMouse == noteNum,
  294. lineColour, textColour);
  295. else if (orientation == verticalKeyboardFacingLeft)
  296. drawWhiteNote (noteNum, g, 0, x, getWidth(), w,
  297. state.isNoteOnForChannels (midiInChannelMask, noteNum),
  298. noteUnderMouse == noteNum,
  299. lineColour, textColour);
  300. else if (orientation == verticalKeyboardFacingRight)
  301. drawWhiteNote (noteNum, g, 0, getHeight() - x - w, getWidth(), w,
  302. state.isNoteOnForChannels (midiInChannelMask, noteNum),
  303. noteUnderMouse == noteNum,
  304. lineColour, textColour);
  305. }
  306. }
  307. }
  308. float x1 = 0.0f, y1 = 0.0f, x2 = 0.0f, y2 = 0.0f;
  309. if (orientation == verticalKeyboardFacingLeft)
  310. {
  311. x1 = getWidth() - 1.0f;
  312. x2 = getWidth() - 5.0f;
  313. }
  314. else if (orientation == verticalKeyboardFacingRight)
  315. x2 = 5.0f;
  316. else
  317. y2 = 5.0f;
  318. GradientBrush gb (Colours::black.withAlpha (0.3f), x1, y1,
  319. Colours::transparentBlack, x2, y2, false);
  320. g.setBrush (&gb);
  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. const int spaceAvailable = w - scrollButtonW * 2;
  522. const int lastStartKey = remappedXYToNote (endOfLastKey - spaceAvailable, 0) + 1;
  523. if (lastStartKey >= 0 && firstKey > lastStartKey)
  524. {
  525. firstKey = jlimit (rangeStart, rangeEnd, lastStartKey);
  526. sendChangeMessage (this);
  527. }
  528. int newOffset = 0;
  529. getKeyPos (firstKey, newOffset, kw);
  530. xOffset = newOffset - scrollButtonW;
  531. }
  532. else
  533. {
  534. firstKey = rangeStart;
  535. }
  536. timerCallback();
  537. repaint();
  538. }
  539. }
  540. //==============================================================================
  541. void MidiKeyboardComponent::handleNoteOn (MidiKeyboardState*, int /*midiChannel*/, int /*midiNoteNumber*/, float /*velocity*/)
  542. {
  543. triggerAsyncUpdate();
  544. }
  545. void MidiKeyboardComponent::handleNoteOff (MidiKeyboardState*, int /*midiChannel*/, int /*midiNoteNumber*/)
  546. {
  547. triggerAsyncUpdate();
  548. }
  549. void MidiKeyboardComponent::handleAsyncUpdate()
  550. {
  551. for (int i = rangeStart; i <= rangeEnd; ++i)
  552. {
  553. if (keysCurrentlyDrawnDown[i] != state.isNoteOnForChannels (midiInChannelMask, i))
  554. {
  555. keysCurrentlyDrawnDown.setBit (i, state.isNoteOnForChannels (midiInChannelMask, i));
  556. repaintNote (i);
  557. }
  558. }
  559. }
  560. //==============================================================================
  561. void MidiKeyboardComponent::resetAnyKeysInUse()
  562. {
  563. if (keysPressed.countNumberOfSetBits() > 0 || mouseDownNote > 0)
  564. {
  565. state.allNotesOff (midiChannel);
  566. keysPressed.clear();
  567. mouseDownNote = -1;
  568. }
  569. }
  570. void MidiKeyboardComponent::updateNoteUnderMouse (int x, int y)
  571. {
  572. const int newNote = (mouseDragging || isMouseOver())
  573. ? xyToNote (x, y) : -1;
  574. if (noteUnderMouse != newNote)
  575. {
  576. if (mouseDownNote >= 0)
  577. {
  578. state.noteOff (midiChannel, mouseDownNote);
  579. mouseDownNote = -1;
  580. }
  581. if (mouseDragging && newNote >= 0)
  582. {
  583. state.noteOn (midiChannel, newNote, velocity);
  584. mouseDownNote = newNote;
  585. }
  586. repaintNote (noteUnderMouse);
  587. noteUnderMouse = newNote;
  588. repaintNote (noteUnderMouse);
  589. }
  590. else if (mouseDownNote >= 0 && ! mouseDragging)
  591. {
  592. state.noteOff (midiChannel, mouseDownNote);
  593. mouseDownNote = -1;
  594. }
  595. }
  596. void MidiKeyboardComponent::mouseMove (const MouseEvent& e)
  597. {
  598. updateNoteUnderMouse (e.x, e.y);
  599. stopTimer();
  600. }
  601. void MidiKeyboardComponent::mouseDrag (const MouseEvent& e)
  602. {
  603. const int newNote = xyToNote (e.x, e.y);
  604. if (newNote >= 0)
  605. mouseDraggedToKey (newNote, e);
  606. updateNoteUnderMouse (e.x, e.y);
  607. }
  608. bool MidiKeyboardComponent::mouseDownOnKey (int /*midiNoteNumber*/, const MouseEvent&)
  609. {
  610. return true;
  611. }
  612. void MidiKeyboardComponent::mouseDraggedToKey (int /*midiNoteNumber*/, const MouseEvent&)
  613. {
  614. }
  615. void MidiKeyboardComponent::mouseDown (const MouseEvent& e)
  616. {
  617. const int newNote = xyToNote (e.x, e.y);
  618. mouseDragging = false;
  619. if (newNote >= 0 && mouseDownOnKey (newNote, e))
  620. {
  621. repaintNote (noteUnderMouse);
  622. noteUnderMouse = -1;
  623. mouseDragging = true;
  624. updateNoteUnderMouse (e.x, e.y);
  625. startTimer (500);
  626. }
  627. }
  628. void MidiKeyboardComponent::mouseUp (const MouseEvent& e)
  629. {
  630. mouseDragging = false;
  631. updateNoteUnderMouse (e.x, e.y);
  632. stopTimer();
  633. }
  634. void MidiKeyboardComponent::mouseEnter (const MouseEvent& e)
  635. {
  636. updateNoteUnderMouse (e.x, e.y);
  637. }
  638. void MidiKeyboardComponent::mouseExit (const MouseEvent& e)
  639. {
  640. updateNoteUnderMouse (e.x, e.y);
  641. }
  642. void MidiKeyboardComponent::mouseWheelMove (const MouseEvent&, float ix, float iy)
  643. {
  644. setLowestVisibleKey (getLowestVisibleKey() + roundFloatToInt ((ix != 0 ? ix : iy) * 5.0f));
  645. }
  646. void MidiKeyboardComponent::timerCallback()
  647. {
  648. int mx, my;
  649. getMouseXYRelative (mx, my);
  650. updateNoteUnderMouse (mx, my);
  651. }
  652. //==============================================================================
  653. void MidiKeyboardComponent::clearKeyMappings()
  654. {
  655. resetAnyKeysInUse();
  656. keyPressNotes.clear();
  657. keyPresses.clear();
  658. }
  659. void MidiKeyboardComponent::setKeyPressForNote (const KeyPress& key,
  660. const int midiNoteOffsetFromC)
  661. {
  662. removeKeyPressForNote (midiNoteOffsetFromC);
  663. keyPressNotes.add (midiNoteOffsetFromC);
  664. keyPresses.add (key);
  665. }
  666. void MidiKeyboardComponent::removeKeyPressForNote (const int midiNoteOffsetFromC)
  667. {
  668. for (int i = keyPressNotes.size(); --i >= 0;)
  669. {
  670. if (keyPressNotes.getUnchecked (i) == midiNoteOffsetFromC)
  671. {
  672. keyPressNotes.remove (i);
  673. keyPresses.remove (i);
  674. }
  675. }
  676. }
  677. void MidiKeyboardComponent::setKeyPressBaseOctave (const int newOctaveNumber)
  678. {
  679. jassert (newOctaveNumber >= 0 && newOctaveNumber <= 10);
  680. keyMappingOctave = newOctaveNumber;
  681. }
  682. bool MidiKeyboardComponent::keyStateChanged (const bool /*isKeyDown*/)
  683. {
  684. bool keyPressUsed = false;
  685. for (int i = keyPresses.size(); --i >= 0;)
  686. {
  687. const int note = 12 * keyMappingOctave + keyPressNotes.getUnchecked (i);
  688. if (keyPresses.getReference(i).isCurrentlyDown())
  689. {
  690. if (! keysPressed [note])
  691. {
  692. keysPressed.setBit (note);
  693. state.noteOn (midiChannel, note, velocity);
  694. keyPressUsed = true;
  695. }
  696. }
  697. else
  698. {
  699. if (keysPressed [note])
  700. {
  701. keysPressed.clearBit (note);
  702. state.noteOff (midiChannel, note);
  703. keyPressUsed = true;
  704. }
  705. }
  706. }
  707. return keyPressUsed;
  708. }
  709. void MidiKeyboardComponent::focusLost (FocusChangeType)
  710. {
  711. resetAnyKeysInUse();
  712. }
  713. END_JUCE_NAMESPACE