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.

250 lines
6.4KB

  1. #include "MidiLock.h"
  2. #include "MidiTrack.h"
  3. #include <assert.h>
  4. #include <algorithm>
  5. #ifdef _DEBUG
  6. int MidiEvent::_count = 0;
  7. #endif
  8. MidiTrack::MidiTrack(std::shared_ptr<MidiLock> l) : lock(l)
  9. {
  10. }
  11. int MidiTrack::size() const
  12. {
  13. return (int) events.size();
  14. }
  15. void MidiTrack::assertValid() const
  16. {
  17. int numEnds = 0;
  18. bool lastIsEnd = false;
  19. (void) lastIsEnd;
  20. float lastEnd = 0;
  21. MidiEvent::time_t startTime = 0;
  22. MidiEvent::time_t totalDur = 0;
  23. for (const_iterator it = begin(); it != end(); ++it) {
  24. it->second->assertValid();
  25. assertGE(it->second->startTime, startTime);
  26. startTime = it->second->startTime;
  27. if (it->second->type == MidiEvent::Type::End) {
  28. numEnds++;
  29. lastIsEnd = true;
  30. totalDur = startTime;
  31. } else {
  32. lastIsEnd = false;
  33. }
  34. MidiNoteEventPtr note = safe_cast<MidiNoteEvent>(it->second);
  35. if (note) {
  36. lastEnd = std::max(lastEnd, startTime + note->duration);
  37. } else {
  38. lastEnd = startTime;
  39. }
  40. // Check for indexing errors
  41. assertEQ(it->first, it->second->startTime);
  42. }
  43. assert(lastIsEnd);
  44. assertEQ(numEnds, 1);
  45. assertLE(lastEnd, totalDur);
  46. }
  47. void MidiTrack::insertEvent(MidiEventPtr evIn)
  48. {
  49. assert(lock);
  50. assert(lock->locked());
  51. events.insert(std::pair<MidiEvent::time_t, MidiEventPtr>(evIn->startTime, evIn));
  52. }
  53. float MidiTrack::getLength() const
  54. {
  55. const_reverse_iterator it = events.rbegin();
  56. MidiEventPtr end = it->second;
  57. MidiEndEventPtr ret = safe_cast<MidiEndEvent>(end);
  58. return ret->startTime;
  59. }
  60. std::shared_ptr<MidiEndEvent> MidiTrack::getEndEvent()
  61. {
  62. const_reverse_iterator it = events.rbegin();
  63. MidiEventPtr end = it->second;
  64. MidiEndEventPtr ret = safe_cast<MidiEndEvent>(end);
  65. return ret;
  66. }
  67. void MidiTrack::deleteEvent(const MidiEvent& evIn)
  68. {
  69. assert(lock);
  70. assert(lock->locked());
  71. auto candidateRange = events.equal_range(evIn.startTime);
  72. for (auto it = candidateRange.first; it != candidateRange.second; it++) {
  73. if (*it->second == evIn) {
  74. events.erase(it);
  75. return;
  76. }
  77. }
  78. printf("could not delete event %p\n", &evIn);
  79. this->_dump();
  80. fflush(stdout);
  81. assert(false); // If you get here it means the event to be deleted was not in the track
  82. }
  83. void MidiTrack::_dump() const
  84. {
  85. const_iterator it;
  86. for (auto it : events) {
  87. float ti = it.first;
  88. std::shared_ptr<const MidiEvent> evt = it.second;
  89. std::string type = "Note";
  90. switch (evt->type) {
  91. case MidiEvent::Type::End:
  92. type = "End";
  93. break;
  94. case MidiEvent::Type::Note:
  95. type = "Note";
  96. break;
  97. }
  98. const void* addr = evt.get();
  99. printf("time = %f, type=%s addr=%p\n", ti, type.c_str(), addr);
  100. }
  101. fflush(stdout);
  102. }
  103. std::vector<MidiEventPtr> MidiTrack::_testGetVector() const
  104. {
  105. std::vector<MidiEventPtr> ret;
  106. std::for_each(events.begin(), events.end(), [&](std::pair<MidiEvent::time_t, const MidiEventPtr&> event) {
  107. ret.push_back(event.second);
  108. });
  109. assert(ret.size() == events.size());
  110. return ret;
  111. }
  112. MidiTrack::iterator_pair MidiTrack::timeRange(MidiEvent::time_t start, MidiEvent::time_t end) const
  113. {
  114. return iterator_pair(events.lower_bound(start), events.upper_bound(end));
  115. }
  116. MidiTrack::note_iterator_pair MidiTrack::timeRangeNotes(MidiEvent::time_t start, MidiEvent::time_t end) const
  117. {
  118. note_iterator::filter_func lambda = [this](MidiTrack::const_iterator ii) {
  119. const MidiEventPtr me = ii->second;
  120. bool ret = false;
  121. MidiNoteEventPtr note = safe_cast<MidiNoteEvent>(me);
  122. if (note) {
  123. ret = true; // accept all notes
  124. }
  125. return ret;
  126. };
  127. // raw will be pair of track::const_iterator
  128. const auto rawIterators = this->timeRange(start, end);
  129. return note_iterator_pair(note_iterator(rawIterators.first, rawIterators.second, lambda),
  130. note_iterator(rawIterators.second, rawIterators.second, lambda));
  131. }
  132. void MidiTrack::insertEnd(MidiEvent::time_t time)
  133. {
  134. assert(lock);
  135. assert(lock->locked());
  136. MidiEndEventPtr end = std::make_shared<MidiEndEvent>();
  137. end->startTime = time;
  138. insertEvent(end);
  139. }
  140. MidiTrack::const_iterator MidiTrack::findEventDeep(const MidiEvent& ev)
  141. {
  142. iterator_pair range = timeRange(ev.startTime, ev.startTime);
  143. for (const_iterator it = range.first; it != range.second; ++it) {
  144. const MidiEventPtr p = it->second;
  145. if (*p == ev) {
  146. return it;
  147. }
  148. }
  149. // didn't find it, return end iterator
  150. return events.end();
  151. }
  152. MidiTrack::const_iterator MidiTrack::findEventPointer(MidiEventPtrC ev)
  153. {
  154. iterator_pair range = timeRange(ev->startTime, ev->startTime);
  155. for (const_iterator it = range.first; it != range.second; ++it) {
  156. const MidiEventPtr p = it->second;
  157. if (p == ev) {
  158. return it;
  159. }
  160. }
  161. // didn't find it, return end iterator
  162. return events.end();
  163. }
  164. MidiNoteEventPtr MidiTrack::getFirstNote()
  165. {
  166. for (auto it : events) {
  167. MidiNoteEventPtr note = safe_cast<MidiNoteEvent>(it.second);
  168. if (note) {
  169. return note;
  170. }
  171. }
  172. return nullptr;
  173. }
  174. MidiTrackPtr MidiTrack::makeTest(TestContent content, std::shared_ptr<MidiLock> lock)
  175. {
  176. MidiTrackPtr ret;
  177. switch (content) {
  178. case TestContent::eightQNotes:
  179. ret = makeTest1(lock);
  180. break;
  181. case TestContent::empty:
  182. ret = makeTestEmpty(lock);
  183. break;
  184. default:
  185. assert(false);
  186. }
  187. return ret;
  188. }
  189. /**
  190. * makes a track of 8 1/4 notes, each of 1/8 note duration (50%).
  191. * pitch is ascending in semitones from 3:0 (c)
  192. */
  193. MidiTrackPtr MidiTrack::makeTest1(std::shared_ptr<MidiLock> lock)
  194. {
  195. auto track = std::make_shared<MidiTrack>(lock);
  196. int semi = 0;
  197. MidiEvent::time_t time = 0;
  198. for (int i = 0; i < 8; ++i) {
  199. MidiNoteEventPtr ev = std::make_shared<MidiNoteEvent>();
  200. ev->startTime = time;
  201. ev->setPitch(3, semi);
  202. ev->duration = .5;
  203. track->insertEvent(ev);
  204. ++semi;
  205. time += 1;
  206. }
  207. track->insertEnd(time);
  208. return track;
  209. }
  210. MidiTrackPtr MidiTrack::makeTestEmpty(std::shared_ptr<MidiLock> lock)
  211. {
  212. auto track = std::make_shared<MidiTrack>(lock);
  213. track->insertEnd(8.f); // make two empty bars
  214. return track;
  215. }