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.

335 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. if (const MidiEventHolder* const meh = list [index])
  55. if (meh->noteOffObject != nullptr)
  56. return meh->noteOffObject->message.getTimeStamp();
  57. return 0.0;
  58. }
  59. int MidiMessageSequence::getIndexOfMatchingKeyUp (const int index) const
  60. {
  61. if (const MidiEventHolder* const meh = list [index])
  62. return list.indexOf (meh->noteOffObject);
  63. return -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. if (const MidiEventHolder* const meh = list [index])
  90. return meh->message.getTimeStamp();
  91. return 0.0;
  92. }
  93. //==============================================================================
  94. MidiMessageSequence::MidiEventHolder* MidiMessageSequence::addEvent (const MidiMessage& newMessage,
  95. double timeAdjustment)
  96. {
  97. MidiEventHolder* const newOne = new MidiEventHolder (newMessage);
  98. timeAdjustment += newMessage.getTimeStamp();
  99. newOne->message.setTimeStamp (timeAdjustment);
  100. int i;
  101. for (i = list.size(); --i >= 0;)
  102. if (list.getUnchecked(i)->message.getTimeStamp() <= timeAdjustment)
  103. break;
  104. list.insert (i + 1, newOne);
  105. return newOne;
  106. }
  107. void MidiMessageSequence::deleteEvent (const int index,
  108. const bool deleteMatchingNoteUp)
  109. {
  110. if (isPositiveAndBelow (index, list.size()))
  111. {
  112. if (deleteMatchingNoteUp)
  113. deleteEvent (getIndexOfMatchingKeyUp (index), false);
  114. list.remove (index);
  115. }
  116. }
  117. struct MidiMessageSequenceSorter
  118. {
  119. static int compareElements (const MidiMessageSequence::MidiEventHolder* const first,
  120. const MidiMessageSequence::MidiEventHolder* const second) noexcept
  121. {
  122. const double diff = first->message.getTimeStamp() - second->message.getTimeStamp();
  123. return (diff > 0) - (diff < 0);
  124. }
  125. };
  126. void MidiMessageSequence::addSequence (const MidiMessageSequence& other,
  127. double timeAdjustment,
  128. double firstAllowableTime,
  129. double endOfAllowableDestTimes)
  130. {
  131. firstAllowableTime -= timeAdjustment;
  132. endOfAllowableDestTimes -= timeAdjustment;
  133. for (int i = 0; i < other.list.size(); ++i)
  134. {
  135. const MidiMessage& m = other.list.getUnchecked(i)->message;
  136. const double t = m.getTimeStamp();
  137. if (t >= firstAllowableTime && t < endOfAllowableDestTimes)
  138. {
  139. MidiEventHolder* const newOne = new MidiEventHolder (m);
  140. newOne->message.setTimeStamp (timeAdjustment + t);
  141. list.add (newOne);
  142. }
  143. }
  144. sort();
  145. }
  146. //==============================================================================
  147. void MidiMessageSequence::sort()
  148. {
  149. MidiMessageSequenceSorter sorter;
  150. list.sort (sorter, true);
  151. }
  152. void MidiMessageSequence::updateMatchedPairs()
  153. {
  154. for (int i = 0; i < list.size(); ++i)
  155. {
  156. MidiEventHolder* const meh = list.getUnchecked(i);
  157. const MidiMessage& m1 = meh->message;
  158. if (m1.isNoteOn())
  159. {
  160. meh->noteOffObject = nullptr;
  161. const int note = m1.getNoteNumber();
  162. const int chan = m1.getChannel();
  163. const int len = list.size();
  164. for (int j = i + 1; j < len; ++j)
  165. {
  166. const MidiMessage& m = list.getUnchecked(j)->message;
  167. if (m.getNoteNumber() == note && m.getChannel() == chan)
  168. {
  169. if (m.isNoteOff())
  170. {
  171. meh->noteOffObject = list[j];
  172. break;
  173. }
  174. else if (m.isNoteOn())
  175. {
  176. list.insert (j, new MidiEventHolder (MidiMessage::noteOff (chan, note)));
  177. list.getUnchecked(j)->message.setTimeStamp (m.getTimeStamp());
  178. meh->noteOffObject = list[j];
  179. break;
  180. }
  181. }
  182. }
  183. }
  184. }
  185. }
  186. void MidiMessageSequence::addTimeToMessages (const double delta)
  187. {
  188. for (int i = list.size(); --i >= 0;)
  189. {
  190. MidiMessage& mm = list.getUnchecked(i)->message;
  191. mm.setTimeStamp (m.getTimeStamp() + delta);
  192. }
  193. }
  194. //==============================================================================
  195. void MidiMessageSequence::extractMidiChannelMessages (const int channelNumberToExtract,
  196. MidiMessageSequence& destSequence,
  197. const bool alsoIncludeMetaEvents) const
  198. {
  199. for (int i = 0; i < list.size(); ++i)
  200. {
  201. const MidiMessage& mm = list.getUnchecked(i)->message;
  202. if (mm.isForChannel (channelNumberToExtract) || (alsoIncludeMetaEvents && mm.isMetaEvent()))
  203. destSequence.addEvent (mm);
  204. }
  205. }
  206. void MidiMessageSequence::extractSysExMessages (MidiMessageSequence& destSequence) const
  207. {
  208. for (int i = 0; i < list.size(); ++i)
  209. {
  210. const MidiMessage& mm = list.getUnchecked(i)->message;
  211. if (mm.isSysEx())
  212. destSequence.addEvent (mm);
  213. }
  214. }
  215. void MidiMessageSequence::deleteMidiChannelMessages (const int channelNumberToRemove)
  216. {
  217. for (int i = list.size(); --i >= 0;)
  218. if (list.getUnchecked(i)->message.isForChannel (channelNumberToRemove))
  219. list.remove(i);
  220. }
  221. void MidiMessageSequence::deleteSysExMessages()
  222. {
  223. for (int i = list.size(); --i >= 0;)
  224. if (list.getUnchecked(i)->message.isSysEx())
  225. list.remove(i);
  226. }
  227. //==============================================================================
  228. void MidiMessageSequence::createControllerUpdatesForTime (const int channelNumber,
  229. const double time,
  230. OwnedArray<MidiMessage>& dest)
  231. {
  232. bool doneProg = false;
  233. bool donePitchWheel = false;
  234. Array <int> doneControllers;
  235. doneControllers.ensureStorageAllocated (32);
  236. for (int i = list.size(); --i >= 0;)
  237. {
  238. const MidiMessage& mm = list.getUnchecked(i)->message;
  239. if (mm.isForChannel (channelNumber) && mm.getTimeStamp() <= time)
  240. {
  241. if (mm.isProgramChange())
  242. {
  243. if (! doneProg)
  244. {
  245. dest.add (new MidiMessage (mm, 0.0));
  246. doneProg = true;
  247. }
  248. }
  249. else if (mm.isController())
  250. {
  251. if (! doneControllers.contains (mm.getControllerNumber()))
  252. {
  253. dest.add (new MidiMessage (mm, 0.0));
  254. doneControllers.add (mm.getControllerNumber());
  255. }
  256. }
  257. else if (mm.isPitchWheel())
  258. {
  259. if (! donePitchWheel)
  260. {
  261. dest.add (new MidiMessage (mm, 0.0));
  262. donePitchWheel = true;
  263. }
  264. }
  265. }
  266. }
  267. }
  268. //==============================================================================
  269. MidiMessageSequence::MidiEventHolder::MidiEventHolder (const MidiMessage& message_)
  270. : message (message_),
  271. noteOffObject (nullptr)
  272. {
  273. }
  274. MidiMessageSequence::MidiEventHolder::~MidiEventHolder()
  275. {
  276. }