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.

155 lines
5.5KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-11 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. MidiMessageCollector::MidiMessageCollector()
  19. : lastCallbackTime (0),
  20. sampleRate (44100.0001)
  21. {
  22. }
  23. MidiMessageCollector::~MidiMessageCollector()
  24. {
  25. }
  26. //==============================================================================
  27. void MidiMessageCollector::reset (const double sampleRate_)
  28. {
  29. jassert (sampleRate_ > 0);
  30. const ScopedLock sl (midiCallbackLock);
  31. sampleRate = sampleRate_;
  32. incomingMessages.clear();
  33. lastCallbackTime = Time::getMillisecondCounterHiRes();
  34. }
  35. void MidiMessageCollector::addMessageToQueue (const MidiMessage& message)
  36. {
  37. // you need to call reset() to set the correct sample rate before using this object
  38. jassert (sampleRate != 44100.0001);
  39. // the messages that come in here need to be time-stamped correctly - see MidiInput
  40. // for details of what the number should be.
  41. jassert (message.getTimeStamp() != 0);
  42. const ScopedLock sl (midiCallbackLock);
  43. const int sampleNumber
  44. = (int) ((message.getTimeStamp() - 0.001 * lastCallbackTime) * sampleRate);
  45. incomingMessages.addEvent (message, sampleNumber);
  46. // if the messages don't get used for over a second, we'd better
  47. // get rid of any old ones to avoid the queue getting too big
  48. if (sampleNumber > sampleRate)
  49. incomingMessages.clear (0, sampleNumber - (int) sampleRate);
  50. }
  51. void MidiMessageCollector::removeNextBlockOfMessages (MidiBuffer& destBuffer,
  52. const int numSamples)
  53. {
  54. // you need to call reset() to set the correct sample rate before using this object
  55. jassert (sampleRate != 44100.0001);
  56. jassert (numSamples > 0);
  57. const double timeNow = Time::getMillisecondCounterHiRes();
  58. const double msElapsed = timeNow - lastCallbackTime;
  59. const ScopedLock sl (midiCallbackLock);
  60. lastCallbackTime = timeNow;
  61. if (! incomingMessages.isEmpty())
  62. {
  63. int numSourceSamples = jmax (1, roundToInt (msElapsed * 0.001 * sampleRate));
  64. int startSample = 0;
  65. int scale = 1 << 16;
  66. const uint8* midiData;
  67. int numBytes, samplePosition;
  68. MidiBuffer::Iterator iter (incomingMessages);
  69. if (numSourceSamples > numSamples)
  70. {
  71. // if our list of events is longer than the buffer we're being
  72. // asked for, scale them down to squeeze them all in..
  73. const int maxBlockLengthToUse = numSamples << 5;
  74. if (numSourceSamples > maxBlockLengthToUse)
  75. {
  76. startSample = numSourceSamples - maxBlockLengthToUse;
  77. numSourceSamples = maxBlockLengthToUse;
  78. iter.setNextSamplePosition (startSample);
  79. }
  80. scale = (numSamples << 10) / numSourceSamples;
  81. while (iter.getNextEvent (midiData, numBytes, samplePosition))
  82. {
  83. samplePosition = ((samplePosition - startSample) * scale) >> 10;
  84. destBuffer.addEvent (midiData, numBytes,
  85. jlimit (0, numSamples - 1, samplePosition));
  86. }
  87. }
  88. else
  89. {
  90. // if our event list is shorter than the number we need, put them
  91. // towards the end of the buffer
  92. startSample = numSamples - numSourceSamples;
  93. while (iter.getNextEvent (midiData, numBytes, samplePosition))
  94. {
  95. destBuffer.addEvent (midiData, numBytes,
  96. jlimit (0, numSamples - 1, samplePosition + startSample));
  97. }
  98. }
  99. incomingMessages.clear();
  100. }
  101. }
  102. //==============================================================================
  103. void MidiMessageCollector::handleNoteOn (MidiKeyboardState*, int midiChannel, int midiNoteNumber, float velocity)
  104. {
  105. MidiMessage m (MidiMessage::noteOn (midiChannel, midiNoteNumber, velocity));
  106. m.setTimeStamp (Time::getMillisecondCounterHiRes() * 0.001);
  107. addMessageToQueue (m);
  108. }
  109. void MidiMessageCollector::handleNoteOff (MidiKeyboardState*, int midiChannel, int midiNoteNumber)
  110. {
  111. MidiMessage m (MidiMessage::noteOff (midiChannel, midiNoteNumber));
  112. m.setTimeStamp (Time::getMillisecondCounterHiRes() * 0.001);
  113. addMessageToQueue (m);
  114. }
  115. void MidiMessageCollector::handleIncomingMidiMessage (MidiInput*, const MidiMessage& message)
  116. {
  117. addMessageToQueue (message);
  118. }