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.

329 lines
10KB

  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. MidiMessageSequence::MidiMessageSequence()
  19. {
  20. }
  21. MidiMessageSequence::MidiMessageSequence (const MidiMessageSequence& other)
  22. {
  23. list.ensureStorageAllocated (other.list.size());
  24. for (int i = 0; i < other.list.size(); ++i)
  25. list.add (new MidiEventHolder (other.list.getUnchecked(i)->message));
  26. }
  27. MidiMessageSequence& MidiMessageSequence::operator= (const MidiMessageSequence& other)
  28. {
  29. MidiMessageSequence otherCopy (other);
  30. swapWith (otherCopy);
  31. return *this;
  32. }
  33. void MidiMessageSequence::swapWith (MidiMessageSequence& other) noexcept
  34. {
  35. list.swapWithArray (other.list);
  36. }
  37. MidiMessageSequence::~MidiMessageSequence()
  38. {
  39. }
  40. void MidiMessageSequence::clear()
  41. {
  42. list.clear();
  43. }
  44. int MidiMessageSequence::getNumEvents() const
  45. {
  46. return list.size();
  47. }
  48. MidiMessageSequence::MidiEventHolder* MidiMessageSequence::getEventPointer (const int index) const
  49. {
  50. return list [index];
  51. }
  52. double MidiMessageSequence::getTimeOfMatchingKeyUp (const int index) const
  53. {
  54. const MidiEventHolder* const meh = list [index];
  55. if (meh != nullptr && meh->noteOffObject != nullptr)
  56. return meh->noteOffObject->message.getTimeStamp();
  57. else
  58. return 0.0;
  59. }
  60. int MidiMessageSequence::getIndexOfMatchingKeyUp (const int index) const
  61. {
  62. const MidiEventHolder* const meh = list [index];
  63. return meh != nullptr ? list.indexOf (meh->noteOffObject) : -1;
  64. }
  65. int MidiMessageSequence::getIndexOf (MidiEventHolder* const event) const
  66. {
  67. return list.indexOf (event);
  68. }
  69. int MidiMessageSequence::getNextIndexAtTime (const double timeStamp) const
  70. {
  71. const int numEvents = list.size();
  72. int i;
  73. for (i = 0; i < numEvents; ++i)
  74. if (list.getUnchecked(i)->message.getTimeStamp() >= timeStamp)
  75. break;
  76. return i;
  77. }
  78. //==============================================================================
  79. double MidiMessageSequence::getStartTime() const
  80. {
  81. return getEventTime (0);
  82. }
  83. double MidiMessageSequence::getEndTime() const
  84. {
  85. return getEventTime (list.size() - 1);
  86. }
  87. double MidiMessageSequence::getEventTime (const int index) const
  88. {
  89. const MidiEventHolder* const e = list [index];
  90. return e != nullptr ? e->message.getTimeStamp() : 0.0;
  91. }
  92. //==============================================================================
  93. void MidiMessageSequence::addEvent (const MidiMessage& newMessage,
  94. double timeAdjustment)
  95. {
  96. MidiEventHolder* const newOne = new MidiEventHolder (newMessage);
  97. timeAdjustment += newMessage.getTimeStamp();
  98. newOne->message.setTimeStamp (timeAdjustment);
  99. int i;
  100. for (i = list.size(); --i >= 0;)
  101. if (list.getUnchecked(i)->message.getTimeStamp() <= timeAdjustment)
  102. break;
  103. list.insert (i + 1, newOne);
  104. }
  105. void MidiMessageSequence::deleteEvent (const int index,
  106. const bool deleteMatchingNoteUp)
  107. {
  108. if (isPositiveAndBelow (index, list.size()))
  109. {
  110. if (deleteMatchingNoteUp)
  111. deleteEvent (getIndexOfMatchingKeyUp (index), false);
  112. list.remove (index);
  113. }
  114. }
  115. struct MidiMessageSequenceSorter
  116. {
  117. static int compareElements (const MidiMessageSequence::MidiEventHolder* const first,
  118. const MidiMessageSequence::MidiEventHolder* const second) noexcept
  119. {
  120. const double diff = first->message.getTimeStamp() - second->message.getTimeStamp();
  121. return (diff > 0) - (diff < 0);
  122. }
  123. };
  124. void MidiMessageSequence::addSequence (const MidiMessageSequence& other,
  125. double timeAdjustment,
  126. double firstAllowableTime,
  127. double endOfAllowableDestTimes)
  128. {
  129. firstAllowableTime -= timeAdjustment;
  130. endOfAllowableDestTimes -= timeAdjustment;
  131. for (int i = 0; i < other.list.size(); ++i)
  132. {
  133. const MidiMessage& m = other.list.getUnchecked(i)->message;
  134. const double t = m.getTimeStamp();
  135. if (t >= firstAllowableTime && t < endOfAllowableDestTimes)
  136. {
  137. MidiEventHolder* const newOne = new MidiEventHolder (m);
  138. newOne->message.setTimeStamp (timeAdjustment + t);
  139. list.add (newOne);
  140. }
  141. }
  142. MidiMessageSequenceSorter sorter;
  143. list.sort (sorter, true);
  144. }
  145. //==============================================================================
  146. void MidiMessageSequence::updateMatchedPairs()
  147. {
  148. for (int i = 0; i < list.size(); ++i)
  149. {
  150. const MidiMessage& m1 = list.getUnchecked(i)->message;
  151. if (m1.isNoteOn())
  152. {
  153. list.getUnchecked(i)->noteOffObject = nullptr;
  154. const int note = m1.getNoteNumber();
  155. const int chan = m1.getChannel();
  156. const int len = list.size();
  157. for (int j = i + 1; j < len; ++j)
  158. {
  159. const MidiMessage& m = list.getUnchecked(j)->message;
  160. if (m.getNoteNumber() == note && m.getChannel() == chan)
  161. {
  162. if (m.isNoteOff())
  163. {
  164. list.getUnchecked(i)->noteOffObject = list[j];
  165. break;
  166. }
  167. else if (m.isNoteOn())
  168. {
  169. list.insert (j, new MidiEventHolder (MidiMessage::noteOff (chan, note)));
  170. list.getUnchecked(j)->message.setTimeStamp (m.getTimeStamp());
  171. list.getUnchecked(i)->noteOffObject = list[j];
  172. break;
  173. }
  174. }
  175. }
  176. }
  177. }
  178. }
  179. void MidiMessageSequence::addTimeToMessages (const double delta)
  180. {
  181. for (int i = list.size(); --i >= 0;)
  182. list.getUnchecked (i)->message.setTimeStamp (list.getUnchecked (i)->message.getTimeStamp()
  183. + delta);
  184. }
  185. //==============================================================================
  186. void MidiMessageSequence::extractMidiChannelMessages (const int channelNumberToExtract,
  187. MidiMessageSequence& destSequence,
  188. const bool alsoIncludeMetaEvents) const
  189. {
  190. for (int i = 0; i < list.size(); ++i)
  191. {
  192. const MidiMessage& mm = list.getUnchecked(i)->message;
  193. if (mm.isForChannel (channelNumberToExtract)
  194. || (alsoIncludeMetaEvents && mm.isMetaEvent()))
  195. {
  196. destSequence.addEvent (mm);
  197. }
  198. }
  199. }
  200. void MidiMessageSequence::extractSysExMessages (MidiMessageSequence& destSequence) const
  201. {
  202. for (int i = 0; i < list.size(); ++i)
  203. {
  204. const MidiMessage& mm = list.getUnchecked(i)->message;
  205. if (mm.isSysEx())
  206. destSequence.addEvent (mm);
  207. }
  208. }
  209. void MidiMessageSequence::deleteMidiChannelMessages (const int channelNumberToRemove)
  210. {
  211. for (int i = list.size(); --i >= 0;)
  212. if (list.getUnchecked(i)->message.isForChannel (channelNumberToRemove))
  213. list.remove(i);
  214. }
  215. void MidiMessageSequence::deleteSysExMessages()
  216. {
  217. for (int i = list.size(); --i >= 0;)
  218. if (list.getUnchecked(i)->message.isSysEx())
  219. list.remove(i);
  220. }
  221. //==============================================================================
  222. void MidiMessageSequence::createControllerUpdatesForTime (const int channelNumber,
  223. const double time,
  224. OwnedArray<MidiMessage>& dest)
  225. {
  226. bool doneProg = false;
  227. bool donePitchWheel = false;
  228. Array <int> doneControllers;
  229. doneControllers.ensureStorageAllocated (32);
  230. for (int i = list.size(); --i >= 0;)
  231. {
  232. const MidiMessage& mm = list.getUnchecked(i)->message;
  233. if (mm.isForChannel (channelNumber)
  234. && mm.getTimeStamp() <= time)
  235. {
  236. if (mm.isProgramChange())
  237. {
  238. if (! doneProg)
  239. {
  240. dest.add (new MidiMessage (mm, 0.0));
  241. doneProg = true;
  242. }
  243. }
  244. else if (mm.isController())
  245. {
  246. if (! doneControllers.contains (mm.getControllerNumber()))
  247. {
  248. dest.add (new MidiMessage (mm, 0.0));
  249. doneControllers.add (mm.getControllerNumber());
  250. }
  251. }
  252. else if (mm.isPitchWheel())
  253. {
  254. if (! donePitchWheel)
  255. {
  256. dest.add (new MidiMessage (mm, 0.0));
  257. donePitchWheel = true;
  258. }
  259. }
  260. }
  261. }
  262. }
  263. //==============================================================================
  264. MidiMessageSequence::MidiEventHolder::MidiEventHolder (const MidiMessage& message_)
  265. : message (message_),
  266. noteOffObject (nullptr)
  267. {
  268. }
  269. MidiMessageSequence::MidiEventHolder::~MidiEventHolder()
  270. {
  271. }