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.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. 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. }