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.

345 lines
11KB

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