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.

170 lines
4.6KB

  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. BEGIN_JUCE_NAMESPACE
  19. //==============================================================================
  20. struct MidiOutput::PendingMessage
  21. {
  22. PendingMessage (const void* const data, const int len, const double timeStamp)
  23. : message (data, len, timeStamp)
  24. {}
  25. MidiMessage message;
  26. PendingMessage* next;
  27. };
  28. MidiOutput::MidiOutput()
  29. : Thread ("midi out"),
  30. internal (nullptr),
  31. firstMessage (nullptr)
  32. {
  33. }
  34. void MidiOutput::sendBlockOfMessages (const MidiBuffer& buffer,
  35. const double millisecondCounterToStartAt,
  36. double samplesPerSecondForBuffer)
  37. {
  38. // You've got to call startBackgroundThread() for this to actually work..
  39. jassert (isThreadRunning());
  40. // this needs to be a value in the future - RTFM for this method!
  41. jassert (millisecondCounterToStartAt > 0);
  42. const double timeScaleFactor = 1000.0 / samplesPerSecondForBuffer;
  43. MidiBuffer::Iterator i (buffer);
  44. const uint8* data;
  45. int len, time;
  46. while (i.getNextEvent (data, len, time))
  47. {
  48. const double eventTime = millisecondCounterToStartAt + timeScaleFactor * time;
  49. PendingMessage* const m = new PendingMessage (data, len, eventTime);
  50. const ScopedLock sl (lock);
  51. if (firstMessage == nullptr || firstMessage->message.getTimeStamp() > eventTime)
  52. {
  53. m->next = firstMessage;
  54. firstMessage = m;
  55. }
  56. else
  57. {
  58. PendingMessage* mm = firstMessage;
  59. while (mm->next != nullptr && mm->next->message.getTimeStamp() <= eventTime)
  60. mm = mm->next;
  61. m->next = mm->next;
  62. mm->next = m;
  63. }
  64. }
  65. notify();
  66. }
  67. void MidiOutput::clearAllPendingMessages()
  68. {
  69. const ScopedLock sl (lock);
  70. while (firstMessage != nullptr)
  71. {
  72. PendingMessage* const m = firstMessage;
  73. firstMessage = firstMessage->next;
  74. delete m;
  75. }
  76. }
  77. void MidiOutput::startBackgroundThread()
  78. {
  79. startThread (9);
  80. }
  81. void MidiOutput::stopBackgroundThread()
  82. {
  83. stopThread (5000);
  84. }
  85. void MidiOutput::run()
  86. {
  87. while (! threadShouldExit())
  88. {
  89. uint32 now = Time::getMillisecondCounter();
  90. uint32 eventTime = 0;
  91. uint32 timeToWait = 500;
  92. PendingMessage* message;
  93. {
  94. const ScopedLock sl (lock);
  95. message = firstMessage;
  96. if (message != nullptr)
  97. {
  98. eventTime = (uint32) roundToInt (message->message.getTimeStamp());
  99. if (eventTime > now + 20)
  100. {
  101. timeToWait = eventTime - (now + 20);
  102. message = nullptr;
  103. }
  104. else
  105. {
  106. firstMessage = message->next;
  107. }
  108. }
  109. }
  110. if (message != nullptr)
  111. {
  112. const ScopedPointer<PendingMessage> messageDeleter (message);
  113. if (eventTime > now)
  114. {
  115. Time::waitForMillisecondCounter (eventTime);
  116. if (threadShouldExit())
  117. break;
  118. }
  119. if (eventTime > now - 200)
  120. sendMessageNow (message->message);
  121. }
  122. else
  123. {
  124. jassert (timeToWait < 1000 * 30);
  125. wait ((int) timeToWait);
  126. }
  127. }
  128. clearAllPendingMessages();
  129. }
  130. END_JUCE_NAMESPACE