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.

154 lines
5.3KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2013 - Raw Material Software Ltd.
  5. Permission is granted to use this software under the terms of either:
  6. a) the GPL v2 (or any later version)
  7. b) the Affero GPL v3
  8. Details of these licenses can be found at: www.gnu.org/licenses
  9. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  10. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  11. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  12. ------------------------------------------------------------------------------
  13. To release a closed-source product which uses JUCE, commercial licenses are
  14. available: visit www.juce.com for more information.
  15. ==============================================================================
  16. */
  17. MidiMessageCollector::MidiMessageCollector()
  18. : lastCallbackTime (0),
  19. sampleRate (44100.0001)
  20. {
  21. }
  22. MidiMessageCollector::~MidiMessageCollector()
  23. {
  24. }
  25. //==============================================================================
  26. void MidiMessageCollector::reset (const double sampleRate_)
  27. {
  28. jassert (sampleRate_ > 0);
  29. const ScopedLock sl (midiCallbackLock);
  30. sampleRate = sampleRate_;
  31. incomingMessages.clear();
  32. lastCallbackTime = Time::getMillisecondCounterHiRes();
  33. }
  34. void MidiMessageCollector::addMessageToQueue (const MidiMessage& message)
  35. {
  36. // you need to call reset() to set the correct sample rate before using this object
  37. jassert (sampleRate != 44100.0001);
  38. // the messages that come in here need to be time-stamped correctly - see MidiInput
  39. // for details of what the number should be.
  40. jassert (message.getTimeStamp() != 0);
  41. const ScopedLock sl (midiCallbackLock);
  42. const int sampleNumber
  43. = (int) ((message.getTimeStamp() - 0.001 * lastCallbackTime) * sampleRate);
  44. incomingMessages.addEvent (message, sampleNumber);
  45. // if the messages don't get used for over a second, we'd better
  46. // get rid of any old ones to avoid the queue getting too big
  47. if (sampleNumber > sampleRate)
  48. incomingMessages.clear (0, sampleNumber - (int) sampleRate);
  49. }
  50. void MidiMessageCollector::removeNextBlockOfMessages (MidiBuffer& destBuffer,
  51. const int numSamples)
  52. {
  53. // you need to call reset() to set the correct sample rate before using this object
  54. jassert (sampleRate != 44100.0001);
  55. jassert (numSamples > 0);
  56. const double timeNow = Time::getMillisecondCounterHiRes();
  57. const double msElapsed = timeNow - lastCallbackTime;
  58. const ScopedLock sl (midiCallbackLock);
  59. lastCallbackTime = timeNow;
  60. if (! incomingMessages.isEmpty())
  61. {
  62. int numSourceSamples = jmax (1, roundToInt (msElapsed * 0.001 * sampleRate));
  63. int startSample = 0;
  64. int scale = 1 << 16;
  65. const uint8* midiData;
  66. int numBytes, samplePosition;
  67. MidiBuffer::Iterator iter (incomingMessages);
  68. if (numSourceSamples > numSamples)
  69. {
  70. // if our list of events is longer than the buffer we're being
  71. // asked for, scale them down to squeeze them all in..
  72. const int maxBlockLengthToUse = numSamples << 5;
  73. if (numSourceSamples > maxBlockLengthToUse)
  74. {
  75. startSample = numSourceSamples - maxBlockLengthToUse;
  76. numSourceSamples = maxBlockLengthToUse;
  77. iter.setNextSamplePosition (startSample);
  78. }
  79. scale = (numSamples << 10) / numSourceSamples;
  80. while (iter.getNextEvent (midiData, numBytes, samplePosition))
  81. {
  82. samplePosition = ((samplePosition - startSample) * scale) >> 10;
  83. destBuffer.addEvent (midiData, numBytes,
  84. jlimit (0, numSamples - 1, samplePosition));
  85. }
  86. }
  87. else
  88. {
  89. // if our event list is shorter than the number we need, put them
  90. // towards the end of the buffer
  91. startSample = numSamples - numSourceSamples;
  92. while (iter.getNextEvent (midiData, numBytes, samplePosition))
  93. {
  94. destBuffer.addEvent (midiData, numBytes,
  95. jlimit (0, numSamples - 1, samplePosition + startSample));
  96. }
  97. }
  98. incomingMessages.clear();
  99. }
  100. }
  101. //==============================================================================
  102. void MidiMessageCollector::handleNoteOn (MidiKeyboardState*, int midiChannel, int midiNoteNumber, float velocity)
  103. {
  104. MidiMessage m (MidiMessage::noteOn (midiChannel, midiNoteNumber, velocity));
  105. m.setTimeStamp (Time::getMillisecondCounterHiRes() * 0.001);
  106. addMessageToQueue (m);
  107. }
  108. void MidiMessageCollector::handleNoteOff (MidiKeyboardState*, int midiChannel, int midiNoteNumber)
  109. {
  110. MidiMessage m (MidiMessage::noteOff (midiChannel, midiNoteNumber));
  111. m.setTimeStamp (Time::getMillisecondCounterHiRes() * 0.001);
  112. addMessageToQueue (m);
  113. }
  114. void MidiMessageCollector::handleIncomingMidiMessage (MidiInput*, const MidiMessage& message)
  115. {
  116. addMessageToQueue (message);
  117. }