OpenShot Audio Library | OpenShotAudio  0.6.0
juce_MidiMessageSequence.cpp
1 /*
2  ==============================================================================
3 
4  This file is part of the JUCE library.
5  Copyright (c) 2022 - Raw Material Software Limited
6 
7  JUCE is an open source library subject to commercial or open-source
8  licensing.
9 
10  The code included in this file is provided under the terms of the ISC license
11  http://www.isc.org/downloads/software-support-policy/isc-license. Permission
12  To use, copy, modify, and/or distribute this software for any purpose with or
13  without fee is hereby granted provided that the above copyright notice and
14  this permission notice appear in all copies.
15 
16  JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
17  EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
18  DISCLAIMED.
19 
20  ==============================================================================
21 */
22 
23 namespace juce
24 {
25 
26 MidiMessageSequence::MidiEventHolder::MidiEventHolder (const MidiMessage& mm) : message (mm) {}
27 MidiMessageSequence::MidiEventHolder::MidiEventHolder (MidiMessage&& mm) : message (std::move (mm)) {}
28 
29 //==============================================================================
30 MidiMessageSequence::MidiMessageSequence()
31 {
32 }
33 
34 MidiMessageSequence::MidiMessageSequence (const MidiMessageSequence& other)
35 {
36  list.addCopiesOf (other.list);
37 
38  for (int i = 0; i < list.size(); ++i)
39  {
40  auto noteOffIndex = other.getIndexOfMatchingKeyUp (i);
41 
42  if (noteOffIndex >= 0)
43  list.getUnchecked (i)->noteOffObject = list.getUnchecked (noteOffIndex);
44  }
45 }
46 
47 MidiMessageSequence& MidiMessageSequence::operator= (const MidiMessageSequence& other)
48 {
49  MidiMessageSequence otherCopy (other);
50  swapWith (otherCopy);
51  return *this;
52 }
53 
54 MidiMessageSequence::MidiMessageSequence (MidiMessageSequence&& other) noexcept
55  : list (std::move (other.list))
56 {
57 }
58 
59 MidiMessageSequence& MidiMessageSequence::operator= (MidiMessageSequence&& other) noexcept
60 {
61  list = std::move (other.list);
62  return *this;
63 }
64 
65 void MidiMessageSequence::swapWith (MidiMessageSequence& other) noexcept
66 {
67  list.swapWith (other.list);
68 }
69 
70 void MidiMessageSequence::clear()
71 {
72  list.clear();
73 }
74 
75 int MidiMessageSequence::getNumEvents() const noexcept
76 {
77  return list.size();
78 }
79 
80 MidiMessageSequence::MidiEventHolder* MidiMessageSequence::getEventPointer (int index) const noexcept
81 {
82  return list[index];
83 }
84 
85 MidiMessageSequence::MidiEventHolder** MidiMessageSequence::begin() noexcept { return list.begin(); }
86 MidiMessageSequence::MidiEventHolder* const* MidiMessageSequence::begin() const noexcept { return list.begin(); }
87 MidiMessageSequence::MidiEventHolder** MidiMessageSequence::end() noexcept { return list.end(); }
88 MidiMessageSequence::MidiEventHolder* const* MidiMessageSequence::end() const noexcept { return list.end(); }
89 
90 double MidiMessageSequence::getTimeOfMatchingKeyUp (int index) const noexcept
91 {
92  if (auto* meh = list[index])
93  if (auto* noteOff = meh->noteOffObject)
94  return noteOff->message.getTimeStamp();
95 
96  return 0;
97 }
98 
99 int MidiMessageSequence::getIndexOfMatchingKeyUp (int index) const noexcept
100 {
101  if (auto* meh = list[index])
102  {
103  if (auto* noteOff = meh->noteOffObject)
104  {
105  for (int i = index; i < list.size(); ++i)
106  if (list.getUnchecked (i) == noteOff)
107  return i;
108 
109  jassertfalse; // we've somehow got a pointer to a note-off object that isn't in the sequence
110  }
111  }
112 
113  return -1;
114 }
115 
116 int MidiMessageSequence::getIndexOf (const MidiEventHolder* event) const noexcept
117 {
118  return list.indexOf (event);
119 }
120 
121 int MidiMessageSequence::getNextIndexAtTime (double timeStamp) const noexcept
122 {
123  auto numEvents = list.size();
124  int i;
125 
126  for (i = 0; i < numEvents; ++i)
127  if (list.getUnchecked (i)->message.getTimeStamp() >= timeStamp)
128  break;
129 
130  return i;
131 }
132 
133 //==============================================================================
134 double MidiMessageSequence::getStartTime() const noexcept
135 {
136  return getEventTime (0);
137 }
138 
139 double MidiMessageSequence::getEndTime() const noexcept
140 {
141  return getEventTime (list.size() - 1);
142 }
143 
144 double MidiMessageSequence::getEventTime (const int index) const noexcept
145 {
146  if (auto* meh = list[index])
147  return meh->message.getTimeStamp();
148 
149  return 0;
150 }
151 
152 //==============================================================================
153 MidiMessageSequence::MidiEventHolder* MidiMessageSequence::addEvent (MidiEventHolder* newEvent, double timeAdjustment)
154 {
155  newEvent->message.addToTimeStamp (timeAdjustment);
156  auto time = newEvent->message.getTimeStamp();
157  int i;
158 
159  for (i = list.size(); --i >= 0;)
160  if (list.getUnchecked (i)->message.getTimeStamp() <= time)
161  break;
162 
163  list.insert (i + 1, newEvent);
164  return newEvent;
165 }
166 
167 MidiMessageSequence::MidiEventHolder* MidiMessageSequence::addEvent (const MidiMessage& newMessage, double timeAdjustment)
168 {
169  return addEvent (new MidiEventHolder (newMessage), timeAdjustment);
170 }
171 
172 MidiMessageSequence::MidiEventHolder* MidiMessageSequence::addEvent (MidiMessage&& newMessage, double timeAdjustment)
173 {
174  return addEvent (new MidiEventHolder (std::move (newMessage)), timeAdjustment);
175 }
176 
177 void MidiMessageSequence::deleteEvent (int index, bool deleteMatchingNoteUp)
178 {
179  if (isPositiveAndBelow (index, list.size()))
180  {
181  if (deleteMatchingNoteUp)
182  deleteEvent (getIndexOfMatchingKeyUp (index), false);
183 
184  list.remove (index);
185  }
186 }
187 
188 void MidiMessageSequence::addSequence (const MidiMessageSequence& other, double timeAdjustment)
189 {
190  for (auto* m : other)
191  {
192  auto newOne = new MidiEventHolder (m->message);
193  newOne->message.addToTimeStamp (timeAdjustment);
194  list.add (newOne);
195  }
196 
197  sort();
198 }
199 
200 void MidiMessageSequence::addSequence (const MidiMessageSequence& other,
201  double timeAdjustment,
202  double firstAllowableTime,
203  double endOfAllowableDestTimes)
204 {
205  for (auto* m : other)
206  {
207  auto t = m->message.getTimeStamp() + timeAdjustment;
208 
209  if (t >= firstAllowableTime && t < endOfAllowableDestTimes)
210  {
211  auto newOne = new MidiEventHolder (m->message);
212  newOne->message.setTimeStamp (t);
213  list.add (newOne);
214  }
215  }
216 
217  sort();
218 }
219 
220 void MidiMessageSequence::sort() noexcept
221 {
222  std::stable_sort (list.begin(), list.end(),
223  [] (const MidiEventHolder* a, const MidiEventHolder* b) { return a->message.getTimeStamp() < b->message.getTimeStamp(); });
224 }
225 
226 void MidiMessageSequence::updateMatchedPairs() noexcept
227 {
228  for (int i = 0; i < list.size(); ++i)
229  {
230  auto* meh = list.getUnchecked (i);
231  auto& m1 = meh->message;
232 
233  if (m1.isNoteOn())
234  {
235  meh->noteOffObject = nullptr;
236  auto note = m1.getNoteNumber();
237  auto chan = m1.getChannel();
238  auto len = list.size();
239 
240  for (int j = i + 1; j < len; ++j)
241  {
242  auto* meh2 = list.getUnchecked (j);
243  auto& m = meh2->message;
244 
245  if (m.getNoteNumber() == note && m.getChannel() == chan)
246  {
247  if (m.isNoteOff())
248  {
249  meh->noteOffObject = meh2;
250  break;
251  }
252 
253  if (m.isNoteOn())
254  {
255  auto newEvent = new MidiEventHolder (MidiMessage::noteOff (chan, note));
256  list.insert (j, newEvent);
257  newEvent->message.setTimeStamp (m.getTimeStamp());
258  meh->noteOffObject = newEvent;
259  break;
260  }
261  }
262  }
263  }
264  }
265 }
266 
267 void MidiMessageSequence::addTimeToMessages (double delta) noexcept
268 {
269  if (! approximatelyEqual (delta, 0.0))
270  for (auto* m : list)
271  m->message.addToTimeStamp (delta);
272 }
273 
274 //==============================================================================
275 void MidiMessageSequence::extractMidiChannelMessages (const int channelNumberToExtract,
276  MidiMessageSequence& destSequence,
277  const bool alsoIncludeMetaEvents) const
278 {
279  for (auto* meh : list)
280  if (meh->message.isForChannel (channelNumberToExtract)
281  || (alsoIncludeMetaEvents && meh->message.isMetaEvent()))
282  destSequence.addEvent (meh->message);
283 }
284 
285 void MidiMessageSequence::extractSysExMessages (MidiMessageSequence& destSequence) const
286 {
287  for (auto* meh : list)
288  if (meh->message.isSysEx())
289  destSequence.addEvent (meh->message);
290 }
291 
292 void MidiMessageSequence::deleteMidiChannelMessages (const int channelNumberToRemove)
293 {
294  for (int i = list.size(); --i >= 0;)
295  if (list.getUnchecked (i)->message.isForChannel (channelNumberToRemove))
296  list.remove (i);
297 }
298 
299 void MidiMessageSequence::deleteSysExMessages()
300 {
301  for (int i = list.size(); --i >= 0;)
302  if (list.getUnchecked (i)->message.isSysEx())
303  list.remove (i);
304 }
305 
306 //==============================================================================
307 class OptionalPitchWheel
308 {
309  Optional<int> value;
310 
311 public:
312  void emit (int channel, Array<MidiMessage>& out) const
313  {
314  if (value.hasValue())
315  out.add (MidiMessage::pitchWheel (channel, *value));
316  }
317 
318  void set (int v)
319  {
320  value = v;
321  }
322 };
323 
324 class OptionalControllerValues
325 {
326  Optional<char> values[128];
327 
328 public:
329  void emit (int channel, Array<MidiMessage>& out) const
330  {
331  for (auto it = std::begin (values); it != std::end (values); ++it)
332  if (it->hasValue())
333  out.add (MidiMessage::controllerEvent (channel, (int) std::distance (std::begin (values), it), **it));
334  }
335 
336  void set (int controller, int value)
337  {
338  values[controller] = (char) value;
339  }
340 };
341 
342 class OptionalProgramChange
343 {
344  Optional<char> value, bankLSB, bankMSB;
345 
346 public:
347  void emit (int channel, double time, Array<MidiMessage>& out) const
348  {
349  if (! value.hasValue())
350  return;
351 
352  if (bankLSB.hasValue() && bankMSB.hasValue())
353  {
354  out.add (MidiMessage::controllerEvent (channel, 0x00, *bankMSB).withTimeStamp (time));
355  out.add (MidiMessage::controllerEvent (channel, 0x20, *bankLSB).withTimeStamp (time));
356  }
357 
358  out.add (MidiMessage::programChange (channel, *value).withTimeStamp (time));
359  }
360 
361  // Returns true if this is a bank number change, and false otherwise.
362  bool trySetBank (int controller, int v)
363  {
364  switch (controller)
365  {
366  case 0x00: bankMSB = (char) v; return true;
367  case 0x20: bankLSB = (char) v; return true;
368  }
369 
370  return false;
371  }
372 
373  void setProgram (int v) { value = (char) v; }
374 };
375 
376 class ParameterNumberState
377 {
378  enum class Kind { rpn, nrpn };
379 
380  Optional<char> newestRpnLsb, newestRpnMsb, newestNrpnLsb, newestNrpnMsb, lastSentLsb, lastSentMsb;
381  Kind lastSentKind = Kind::rpn, newestKind = Kind::rpn;
382 
383 public:
384  // If the effective parameter number has changed since the last time this function was called,
385  // this will emit the current parameter in full (MSB and LSB).
386  // This should be called before each data message (entry, increment, decrement: 0x06, 0x26, 0x60, 0x61)
387  // to ensure that the data message operates on the correct parameter number.
388  void sendIfNecessary (int channel, double time, Array<MidiMessage>& out)
389  {
390  const auto newestMsb = newestKind == Kind::rpn ? newestRpnMsb : newestNrpnMsb;
391  const auto newestLsb = newestKind == Kind::rpn ? newestRpnLsb : newestNrpnLsb;
392 
393  auto lastSent = std::tie (lastSentKind, lastSentMsb, lastSentLsb);
394  const auto newest = std::tie (newestKind, newestMsb, newestLsb);
395 
396  if (lastSent == newest || ! newestMsb.hasValue() || ! newestLsb.hasValue())
397  return;
398 
399  out.add (MidiMessage::controllerEvent (channel, newestKind == Kind::rpn ? 0x65 : 0x63, *newestMsb).withTimeStamp (time));
400  out.add (MidiMessage::controllerEvent (channel, newestKind == Kind::rpn ? 0x64 : 0x62, *newestLsb).withTimeStamp (time));
401 
402  lastSent = newest;
403  }
404 
405  // Returns true if this is a parameter number change, and false otherwise.
406  bool trySetProgramNumber (int controller, int value)
407  {
408  switch (controller)
409  {
410  case 0x65: newestRpnMsb = (char) value; newestKind = Kind::rpn; return true;
411  case 0x64: newestRpnLsb = (char) value; newestKind = Kind::rpn; return true;
412  case 0x63: newestNrpnMsb = (char) value; newestKind = Kind::nrpn; return true;
413  case 0x62: newestNrpnLsb = (char) value; newestKind = Kind::nrpn; return true;
414  }
415 
416  return false;
417  }
418 };
419 
420 void MidiMessageSequence::createControllerUpdatesForTime (int channel, double time, Array<MidiMessage>& dest)
421 {
422  OptionalProgramChange programChange;
423  OptionalControllerValues controllers;
424  OptionalPitchWheel pitchWheel;
425  ParameterNumberState parameterNumberState;
426 
427  for (const auto& item : list)
428  {
429  const auto& mm = item->message;
430 
431  if (! (mm.isForChannel (channel) && mm.getTimeStamp() <= time))
432  continue;
433 
434  if (mm.isController())
435  {
436  const auto num = mm.getControllerNumber();
437 
438  if (parameterNumberState.trySetProgramNumber (num, mm.getControllerValue()))
439  continue;
440 
441  if (programChange.trySetBank (num, mm.getControllerValue()))
442  continue;
443 
444  constexpr int passthroughs[] { 0x06, 0x26, 0x60, 0x61 };
445 
446  if (std::find (std::begin (passthroughs), std::end (passthroughs), num) != std::end (passthroughs))
447  {
448  parameterNumberState.sendIfNecessary (channel, mm.getTimeStamp(), dest);
449  dest.add (mm);
450  }
451  else
452  {
453  controllers.set (num, mm.getControllerValue());
454  }
455  }
456  else if (mm.isProgramChange())
457  {
458  programChange.setProgram (mm.getProgramChangeNumber());
459  }
460  else if (mm.isPitchWheel())
461  {
462  pitchWheel.set (mm.getPitchWheelValue());
463  }
464  }
465 
466  pitchWheel.emit (channel, dest);
467  controllers.emit (channel, dest);
468 
469  // Also emits bank change messages if necessary.
470  programChange.emit (channel, time, dest);
471 
472  // Set the parameter number to its final state.
473  parameterNumberState.sendIfNecessary (channel, time, dest);
474 }
475 
476 //==============================================================================
477 //==============================================================================
478 #if JUCE_UNIT_TESTS
479 
480 struct MidiMessageSequenceTest final : public UnitTest
481 {
482  MidiMessageSequenceTest()
483  : UnitTest ("MidiMessageSequence", UnitTestCategories::midi)
484  {}
485 
486  void runTest() override
487  {
488  MidiMessageSequence s;
489 
490  s.addEvent (MidiMessage::noteOn (1, 60, 0.5f).withTimeStamp (0.0));
491  s.addEvent (MidiMessage::noteOff (1, 60, 0.5f).withTimeStamp (4.0));
492  s.addEvent (MidiMessage::noteOn (1, 30, 0.5f).withTimeStamp (2.0));
493  s.addEvent (MidiMessage::noteOff (1, 30, 0.5f).withTimeStamp (8.0));
494 
495  beginTest ("Start & end time");
496  expectEquals (s.getStartTime(), 0.0);
497  expectEquals (s.getEndTime(), 8.0);
498  expectEquals (s.getEventTime (1), 2.0);
499 
500  beginTest ("Matching note off & ons");
501  s.updateMatchedPairs();
502  expectEquals (s.getTimeOfMatchingKeyUp (0), 4.0);
503  expectEquals (s.getTimeOfMatchingKeyUp (1), 8.0);
504  expectEquals (s.getIndexOfMatchingKeyUp (0), 2);
505  expectEquals (s.getIndexOfMatchingKeyUp (1), 3);
506 
507  beginTest ("Time & indices");
508  expectEquals (s.getNextIndexAtTime (0.5), 1);
509  expectEquals (s.getNextIndexAtTime (2.5), 2);
510  expectEquals (s.getNextIndexAtTime (9.0), 4);
511 
512  beginTest ("Deleting events");
513  s.deleteEvent (0, true);
514  expectEquals (s.getNumEvents(), 2);
515 
516  beginTest ("Merging sequences");
517  MidiMessageSequence s2;
518  s2.addEvent (MidiMessage::noteOn (2, 25, 0.5f).withTimeStamp (0.0));
519  s2.addEvent (MidiMessage::noteOn (2, 40, 0.5f).withTimeStamp (1.0));
520  s2.addEvent (MidiMessage::noteOff (2, 40, 0.5f).withTimeStamp (5.0));
521  s2.addEvent (MidiMessage::noteOn (2, 80, 0.5f).withTimeStamp (3.0));
522  s2.addEvent (MidiMessage::noteOff (2, 80, 0.5f).withTimeStamp (7.0));
523  s2.addEvent (MidiMessage::noteOff (2, 25, 0.5f).withTimeStamp (9.0));
524 
525  s.addSequence (s2, 0.0, 0.0, 8.0); // Intentionally cut off the last note off
526  s.updateMatchedPairs();
527 
528  expectEquals (s.getNumEvents(), 7);
529  expectEquals (s.getIndexOfMatchingKeyUp (0), -1); // Truncated note, should be no note off
530  expectEquals (s.getTimeOfMatchingKeyUp (1), 5.0);
531 
532  struct ControlValue { int control, value; };
533 
534  struct DataEntry
535  {
536  int controllerBase, channel, parameter, value;
537  double time;
538 
539  std::array<ControlValue, 4> getControlValues() const
540  {
541  return { { { controllerBase + 1, (parameter >> 7) & 0x7f },
542  { controllerBase + 0, (parameter >> 0) & 0x7f },
543  { 0x06, (value >> 7) & 0x7f },
544  { 0x26, (value >> 0) & 0x7f } } };
545  }
546 
547  void addToSequence (MidiMessageSequence& s) const
548  {
549  for (const auto& pair : getControlValues())
550  s.addEvent (MidiMessage::controllerEvent (channel, pair.control, pair.value), time);
551  }
552 
553  bool matches (const MidiMessage* begin, const MidiMessage* end) const
554  {
555  const auto isEqual = [this] (const ControlValue& cv, const MidiMessage& msg)
556  {
557  return exactlyEqual (msg.getTimeStamp(), time)
558  && msg.isController()
559  && msg.getChannel() == channel
560  && msg.getControllerNumber() == cv.control
561  && msg.getControllerValue() == cv.value;
562  };
563 
564  const auto pairs = getControlValues();
565  return std::equal (pairs.begin(), pairs.end(), begin, end, isEqual);
566  }
567  };
568 
569  const auto addNrpn = [&] (MidiMessageSequence& seq, int channel, int parameter, int value, double time = 0.0)
570  {
571  DataEntry { 0x62, channel, parameter, value, time }.addToSequence (seq);
572  };
573 
574  const auto addRpn = [&] (MidiMessageSequence& seq, int channel, int parameter, int value, double time = 0.0)
575  {
576  DataEntry { 0x64, channel, parameter, value, time }.addToSequence (seq);
577  };
578 
579  const auto checkNrpn = [&] (const MidiMessage* begin, const MidiMessage* end, int channel, int parameter, int value, double time = 0.0)
580  {
581  expect (DataEntry { 0x62, channel, parameter, value, time }.matches (begin, end));
582  };
583 
584  const auto checkRpn = [&] (const MidiMessage* begin, const MidiMessage* end, int channel, int parameter, int value, double time = 0.0)
585  {
586  expect (DataEntry { 0x64, channel, parameter, value, time }.matches (begin, end));
587  };
588 
589  beginTest ("createControllerUpdatesForTime should emit (N)RPN components in the correct order");
590  {
591  const auto channel = 1;
592  const auto number = 200;
593  const auto value = 300;
594 
595  MidiMessageSequence sequence;
596  addNrpn (sequence, channel, number, value);
597 
598  Array<MidiMessage> m;
599  sequence.createControllerUpdatesForTime (channel, 1.0, m);
600 
601  checkNrpn (m.begin(), m.end(), channel, number, value);
602  }
603 
604  beginTest ("createControllerUpdatesForTime ignores (N)RPNs after the final requested time");
605  {
606  const auto channel = 2;
607  const auto number = 123;
608  const auto value = 456;
609 
610  MidiMessageSequence sequence;
611  addRpn (sequence, channel, number, value, 0.5);
612  addRpn (sequence, channel, 111, 222, 1.5);
613  addRpn (sequence, channel, 333, 444, 2.5);
614 
615  Array<MidiMessage> m;
616  sequence.createControllerUpdatesForTime (channel, 1.0, m);
617 
618  checkRpn (m.begin(), std::next (m.begin(), 4), channel, number, value, 0.5);
619  }
620 
621  beginTest ("createControllerUpdatesForTime should emit separate (N)RPN messages when appropriate");
622  {
623  const auto channel = 2;
624  const auto numberA = 1111;
625  const auto valueA = 9999;
626 
627  const auto numberB = 8888;
628  const auto valueB = 2222;
629 
630  const auto numberC = 7777;
631  const auto valueC = 3333;
632 
633  const auto numberD = 6666;
634  const auto valueD = 4444;
635 
636  const auto time = 0.5;
637 
638  MidiMessageSequence sequence;
639  addRpn (sequence, channel, numberA, valueA, time);
640  addRpn (sequence, channel, numberB, valueB, time);
641  addNrpn (sequence, channel, numberC, valueC, time);
642  addNrpn (sequence, channel, numberD, valueD, time);
643 
644  Array<MidiMessage> m;
645  sequence.createControllerUpdatesForTime (channel, time * 2, m);
646 
647  checkRpn (std::next (m.begin(), 0), std::next (m.begin(), 4), channel, numberA, valueA, time);
648  checkRpn (std::next (m.begin(), 4), std::next (m.begin(), 8), channel, numberB, valueB, time);
649  checkNrpn (std::next (m.begin(), 8), std::next (m.begin(), 12), channel, numberC, valueC, time);
650  checkNrpn (std::next (m.begin(), 12), std::next (m.begin(), 16), channel, numberD, valueD, time);
651  }
652 
653  beginTest ("createControllerUpdatesForTime correctly emits (N)RPN messages on multiple channels");
654  {
655  struct Info { int channel, number, value; };
656 
657  const Info infos[] { { 2, 1111, 9999 },
658  { 8, 8888, 2222 },
659  { 5, 7777, 3333 },
660  { 1, 6666, 4444 } };
661 
662  const auto time = 0.5;
663 
664  MidiMessageSequence sequence;
665 
666  for (const auto& info : infos)
667  addRpn (sequence, info.channel, info.number, info.value, time);
668 
669  for (const auto& info : infos)
670  {
671  Array<MidiMessage> m;
672  sequence.createControllerUpdatesForTime (info.channel, time * 2, m);
673  checkRpn (std::next (m.begin(), 0), std::next (m.begin(), 4), info.channel, info.number, info.value, time);
674  }
675  }
676 
677  const auto messagesAreEqual = [] (const MidiMessage& a, const MidiMessage& b)
678  {
679  return std::equal (a.getRawData(), a.getRawData() + a.getRawDataSize(),
680  b.getRawData(), b.getRawData() + b.getRawDataSize());
681  };
682 
683  beginTest ("createControllerUpdatesForTime sends bank select messages when the next program is in a new bank");
684  {
685  MidiMessageSequence sequence;
686 
687  const auto time = 0.0;
688  const auto channel = 1;
689 
690  sequence.addEvent (MidiMessage::programChange (channel, 5), time);
691 
692  sequence.addEvent (MidiMessage::controllerEvent (channel, 0x00, 128), time);
693  sequence.addEvent (MidiMessage::controllerEvent (channel, 0x20, 64), time);
694  sequence.addEvent (MidiMessage::programChange (channel, 63), time);
695 
696  const Array<MidiMessage> finalEvents { MidiMessage::controllerEvent (channel, 0x00, 50),
697  MidiMessage::controllerEvent (channel, 0x20, 40),
698  MidiMessage::programChange (channel, 30) };
699 
700  for (const auto& e : finalEvents)
701  sequence.addEvent (e);
702 
703  Array<MidiMessage> m;
704  sequence.createControllerUpdatesForTime (channel, 1.0, m);
705 
706  expect (std::equal (m.begin(), m.end(), finalEvents.begin(), finalEvents.end(), messagesAreEqual));
707  }
708 
709  beginTest ("createControllerUpdatesForTime preserves all Data Increment and Data Decrement messages");
710  {
711  MidiMessageSequence sequence;
712 
713  const auto time = 0.0;
714  const auto channel = 1;
715 
716  const Array<MidiMessage> messages { MidiMessage::controllerEvent (channel, 0x60, 0),
717  MidiMessage::controllerEvent (channel, 0x06, 100),
718  MidiMessage::controllerEvent (channel, 0x26, 50),
719  MidiMessage::controllerEvent (channel, 0x60, 10),
720  MidiMessage::controllerEvent (channel, 0x61, 10),
721  MidiMessage::controllerEvent (channel, 0x06, 20),
722  MidiMessage::controllerEvent (channel, 0x26, 30),
723  MidiMessage::controllerEvent (channel, 0x61, 10),
724  MidiMessage::controllerEvent (channel, 0x61, 20) };
725 
726  for (const auto& m : messages)
727  sequence.addEvent (m, time);
728 
729  Array<MidiMessage> m;
730  sequence.createControllerUpdatesForTime (channel, 1.0, m);
731 
732  expect (std::equal (m.begin(), m.end(), messages.begin(), messages.end(), messagesAreEqual));
733  }
734 
735  beginTest ("createControllerUpdatesForTime does not emit redundant parameter number changes");
736  {
737  MidiMessageSequence sequence;
738 
739  const auto time = 0.0;
740  const auto channel = 1;
741 
742  const Array<MidiMessage> messages { MidiMessage::controllerEvent (channel, 0x65, 0),
743  MidiMessage::controllerEvent (channel, 0x64, 100),
744  MidiMessage::controllerEvent (channel, 0x63, 50),
745  MidiMessage::controllerEvent (channel, 0x62, 10),
746  MidiMessage::controllerEvent (channel, 0x06, 10) };
747 
748  for (const auto& m : messages)
749  sequence.addEvent (m, time);
750 
751  Array<MidiMessage> m;
752  sequence.createControllerUpdatesForTime (channel, 1.0, m);
753 
754  const Array<MidiMessage> expected { MidiMessage::controllerEvent (channel, 0x63, 50),
755  MidiMessage::controllerEvent (channel, 0x62, 10),
756  MidiMessage::controllerEvent (channel, 0x06, 10) };
757 
758  expect (std::equal (m.begin(), m.end(), expected.begin(), expected.end(), messagesAreEqual));
759  }
760 
761  beginTest ("createControllerUpdatesForTime sets parameter number correctly at end of sequence");
762  {
763  MidiMessageSequence sequence;
764 
765  const auto time = 0.0;
766  const auto channel = 1;
767 
768  const Array<MidiMessage> messages { MidiMessage::controllerEvent (channel, 0x65, 0),
769  MidiMessage::controllerEvent (channel, 0x64, 100),
770  MidiMessage::controllerEvent (channel, 0x63, 50),
771  MidiMessage::controllerEvent (channel, 0x62, 10),
772  MidiMessage::controllerEvent (channel, 0x06, 10),
773  MidiMessage::controllerEvent (channel, 0x64, 5) };
774 
775  for (const auto& m : messages)
776  sequence.addEvent (m, time);
777 
778  const auto finalTime = 1.0;
779 
780  Array<MidiMessage> m;
781  sequence.createControllerUpdatesForTime (channel, finalTime, m);
782 
783  const Array<MidiMessage> expected { MidiMessage::controllerEvent (channel, 0x63, 50),
784  MidiMessage::controllerEvent (channel, 0x62, 10),
785  MidiMessage::controllerEvent (channel, 0x06, 10),
786  // Note: we should send both the MSB and LSB!
787  MidiMessage::controllerEvent (channel, 0x65, 0).withTimeStamp (finalTime),
788  MidiMessage::controllerEvent (channel, 0x64, 5).withTimeStamp (finalTime) };
789 
790  expect (std::equal (m.begin(), m.end(), expected.begin(), expected.end(), messagesAreEqual));
791  }
792 
793  beginTest ("createControllerUpdatesForTime does not emit duplicate parameter number change messages");
794  {
795  MidiMessageSequence sequence;
796 
797  const auto time = 0.0;
798  const auto channel = 1;
799 
800  const Array<MidiMessage> messages { MidiMessage::controllerEvent (channel, 0x65, 1),
801  MidiMessage::controllerEvent (channel, 0x64, 2),
802  MidiMessage::controllerEvent (channel, 0x63, 3),
803  MidiMessage::controllerEvent (channel, 0x62, 4),
804  MidiMessage::controllerEvent (channel, 0x06, 10),
805  MidiMessage::controllerEvent (channel, 0x63, 30),
806  MidiMessage::controllerEvent (channel, 0x62, 40),
807  MidiMessage::controllerEvent (channel, 0x63, 3),
808  MidiMessage::controllerEvent (channel, 0x62, 4),
809  MidiMessage::controllerEvent (channel, 0x60, 5),
810  MidiMessage::controllerEvent (channel, 0x65, 10) };
811 
812  for (const auto& m : messages)
813  sequence.addEvent (m, time);
814 
815  const auto finalTime = 1.0;
816 
817  Array<MidiMessage> m;
818  sequence.createControllerUpdatesForTime (channel, finalTime, m);
819 
820  const Array<MidiMessage> expected { MidiMessage::controllerEvent (channel, 0x63, 3),
821  MidiMessage::controllerEvent (channel, 0x62, 4),
822  MidiMessage::controllerEvent (channel, 0x06, 10),
823  // Parameter number is set to (30, 40) then back to (3, 4),
824  // so there is no need to resend it
825  MidiMessage::controllerEvent (channel, 0x60, 5),
826  // Set parameter number to final value
827  MidiMessage::controllerEvent (channel, 0x65, 10).withTimeStamp (finalTime),
828  MidiMessage::controllerEvent (channel, 0x64, 2) .withTimeStamp (finalTime) };
829 
830  expect (std::equal (m.begin(), m.end(), expected.begin(), expected.end(), messagesAreEqual));
831  }
832 
833  beginTest ("createControllerUpdatesForTime emits bank change messages immediately before program change");
834  {
835  MidiMessageSequence sequence;
836 
837  const auto time = 0.0;
838  const auto channel = 1;
839 
840  const Array<MidiMessage> messages { MidiMessage::controllerEvent (channel, 0x00, 1),
841  MidiMessage::controllerEvent (channel, 0x20, 2),
842  MidiMessage::controllerEvent (channel, 0x65, 0),
843  MidiMessage::controllerEvent (channel, 0x64, 0),
844  MidiMessage::programChange (channel, 5) };
845 
846  for (const auto& m : messages)
847  sequence.addEvent (m, time);
848 
849  const auto finalTime = 1.0;
850 
851  Array<MidiMessage> m;
852  sequence.createControllerUpdatesForTime (channel, finalTime, m);
853 
854  const Array<MidiMessage> expected { MidiMessage::controllerEvent (channel, 0x00, 1),
855  MidiMessage::controllerEvent (channel, 0x20, 2),
856  MidiMessage::programChange (channel, 5),
857  MidiMessage::controllerEvent (channel, 0x65, 0).withTimeStamp (finalTime),
858  MidiMessage::controllerEvent (channel, 0x64, 0).withTimeStamp (finalTime) };
859 
860 
861  expect (std::equal (m.begin(), m.end(), expected.begin(), expected.end(), messagesAreEqual));
862  }
863  }
864 };
865 
866 static MidiMessageSequenceTest midiMessageSequenceTests;
867 
868 #endif
869 
870 } // namespace juce
void add(const ElementType &newElement)
Definition: juce_Array.h:418
MidiEventHolder * addEvent(const MidiMessage &newMessage, double timeAdjustment=0)
double getTimeStamp() const noexcept
static MidiMessage controllerEvent(int channel, int controllerType, int value) noexcept
void addToTimeStamp(double delta) noexcept
static MidiMessage programChange(int channel, int programNumber) noexcept
MidiMessage withTimeStamp(double newTimestamp) const
void expectEquals(ValueType actual, ValueType expected, String failureMessage=String())
void beginTest(const String &testName)
void expect(bool testResult, const String &failureMessage=String())
virtual void runTest()=0