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