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.

233 lines
6.9KB

  1. #include "ReplaceDataCommand.h"
  2. #include "MidiLock.h"
  3. #include "MidiSequencer.h"
  4. #include "MidiSong.h"
  5. #include "MidiTrack.h"
  6. #include <assert.h>
  7. ReplaceDataCommand::ReplaceDataCommand(
  8. MidiSongPtr song,
  9. MidiSelectionModelPtr selection,
  10. std::shared_ptr<MidiEditorContext> unused,
  11. int trackNumber,
  12. const std::vector<MidiEventPtr>& inRemove,
  13. const std::vector<MidiEventPtr>& inAdd)
  14. : song(song), trackNumber(trackNumber), selection(selection), removeData(inRemove), addData(inAdd)
  15. {
  16. assert(song->getTrack(trackNumber));
  17. }
  18. void ReplaceDataCommand::execute()
  19. {
  20. MidiTrackPtr mt = song->getTrack(trackNumber);
  21. assert(mt);
  22. MidiLocker l(mt->lock);
  23. for (auto it : addData) {
  24. mt->insertEvent(it);
  25. }
  26. for (auto it : removeData) {
  27. mt->deleteEvent(*it);
  28. }
  29. // clone the selection, clear real selection, add stuff back correctly
  30. // at the very least we must clear the selection, as those notes are no
  31. // longer in the track.
  32. MidiSelectionModelPtr reference = selection->clone();
  33. selection->clear();
  34. for (auto it : addData) {
  35. auto foundIter = mt->findEventDeep(*it); // find an event in the track that matches the one we just inserted
  36. assert(foundIter != mt->end());
  37. MidiEventPtr evt = foundIter->second;
  38. selection->extendSelection(evt);
  39. }
  40. }
  41. void ReplaceDataCommand::undo()
  42. {
  43. MidiTrackPtr mt = song->getTrack(trackNumber);
  44. assert(mt);
  45. MidiLocker l(mt->lock);
  46. // to undo the insertion, delete all of them
  47. for (auto it : addData) {
  48. mt->deleteEvent(*it);
  49. }
  50. for (auto it : removeData) {
  51. mt->insertEvent(it);
  52. }
  53. selection->clear();
  54. for (auto it : removeData) {
  55. auto foundIter = mt->findEventDeep(*it); // find an event in the track that matches the one we just inserted
  56. assert(foundIter != mt->end());
  57. MidiEventPtr evt = foundIter->second;
  58. selection->extendSelection(evt);
  59. }
  60. // TODO: move cursor
  61. }
  62. ReplaceDataCommandPtr ReplaceDataCommand::makeDeleteCommand(MidiSequencerPtr seq)
  63. {
  64. std::vector<MidiEventPtr> toRemove;
  65. std::vector<MidiEventPtr> toAdd;
  66. auto track = seq->context->getTrack();
  67. for (auto it : *seq->selection) {
  68. MidiEventPtr ev = it;
  69. toRemove.push_back(ev);
  70. }
  71. ReplaceDataCommandPtr ret = std::make_shared<ReplaceDataCommand>(
  72. seq->song,
  73. seq->selection,
  74. seq->context,
  75. seq->context->getTrackNumber(),
  76. toRemove,
  77. toAdd);
  78. return ret;
  79. }
  80. ReplaceDataCommandPtr ReplaceDataCommand::makeChangeNoteCommand(
  81. Ops op,
  82. std::shared_ptr<MidiSequencer> seq,
  83. Xform xform,
  84. bool canChangeLength)
  85. {
  86. std::vector<MidiEventPtr> toAdd;
  87. std::vector<MidiEventPtr> toRemove;
  88. if (canChangeLength) {
  89. // Figure out the duration of the track after xforming the notes
  90. MidiSelectionModelPtr clonedSelection = seq->selection->clone();
  91. // find required length
  92. MidiEndEventPtr end = seq->context->getTrack()->getEndEvent();
  93. float endTime = end->startTime;
  94. for (auto it : *clonedSelection) {
  95. MidiEventPtr ev = it;
  96. xform(ev);
  97. float t = ev->startTime;
  98. MidiNoteEventPtrC note = safe_cast<MidiNoteEvent>(ev);
  99. if (note) {
  100. t += note->duration;
  101. //printf("note extends to %f\n", t);
  102. }
  103. endTime = std::max(endTime, t);
  104. }
  105. // printf("when done, end Time = %f\n", endTime);
  106. // set up events to extend to that length
  107. if (endTime > end->startTime) {
  108. extendTrackToMinDuration(seq, endTime, toAdd, toRemove);
  109. }
  110. }
  111. MidiSelectionModelPtr clonedSelection = seq->selection->clone();
  112. // will remove existing selection
  113. for (auto it : *seq->selection) {
  114. auto note = safe_cast<MidiNoteEvent>(it);
  115. if (note) {
  116. toRemove.push_back(note);
  117. }
  118. }
  119. // and add back the transformed notes
  120. for (auto it : *clonedSelection) {
  121. MidiEventPtr event = it;
  122. xform(event);
  123. toAdd.push_back(event);
  124. }
  125. ReplaceDataCommandPtr ret = std::make_shared<ReplaceDataCommand>(
  126. seq->song,
  127. seq->selection,
  128. seq->context,
  129. seq->context->getTrackNumber(),
  130. toRemove,
  131. toAdd);
  132. return ret;
  133. }
  134. ReplaceDataCommandPtr ReplaceDataCommand::makeChangePitchCommand(MidiSequencerPtr seq, int semitones)
  135. {
  136. const float deltaCV = PitchUtils::semitone * semitones;
  137. Xform xform = [deltaCV](MidiEventPtr event) {
  138. MidiNoteEventPtr note = safe_cast<MidiNoteEvent>(event);
  139. if (note) {
  140. note->pitchCV += deltaCV;
  141. }
  142. };
  143. return makeChangeNoteCommand(Ops::Pitch, seq, xform, false);
  144. }
  145. ReplaceDataCommandPtr ReplaceDataCommand::makeChangeStartTimeCommand(MidiSequencerPtr seq, float delta)
  146. {
  147. Xform xform = [delta](MidiEventPtr event) {
  148. MidiNoteEventPtr note = safe_cast<MidiNoteEvent>(event);
  149. if (note) {
  150. note->startTime += delta;
  151. note->startTime = std::max(0.f, note->startTime);
  152. }
  153. };
  154. return makeChangeNoteCommand(Ops::Start, seq, xform, true);
  155. }
  156. ReplaceDataCommandPtr ReplaceDataCommand::makeChangeDurationCommand(MidiSequencerPtr seq, float delta)
  157. {
  158. Xform xform = [delta](MidiEventPtr event) {
  159. MidiNoteEventPtr note = safe_cast<MidiNoteEvent>(event);
  160. if (note) {
  161. note->duration += delta;
  162. // arbitrary min limit.
  163. note->duration = std::max(.001f, note->duration);
  164. }
  165. };
  166. return makeChangeNoteCommand(Ops::Duration, seq, xform, true);
  167. }
  168. ReplaceDataCommandPtr ReplaceDataCommand::makeInsertNoteCommand(MidiSequencerPtr seq, MidiNoteEventPtrC origNote)
  169. {
  170. MidiNoteEventPtr note = origNote->clonen();
  171. // Make the delete end / inserts end to extend track.
  172. // Make it long enough to hold insert note.
  173. std::vector<MidiEventPtr> toRemove;
  174. std::vector<MidiEventPtr> toAdd;
  175. extendTrackToMinDuration(seq, note->startTime + note->duration, toAdd, toRemove);
  176. toAdd.push_back(note);
  177. ReplaceDataCommandPtr ret = std::make_shared<ReplaceDataCommand>(
  178. seq->song,
  179. seq->selection,
  180. seq->context,
  181. seq->context->getTrackNumber(),
  182. toRemove,
  183. toAdd);
  184. return ret;
  185. }
  186. void ReplaceDataCommand::extendTrackToMinDuration(
  187. MidiSequencerPtr seq,
  188. float neededLength,
  189. std::vector<MidiEventPtr>& toAdd,
  190. std::vector<MidiEventPtr>& toDelete)
  191. {
  192. auto track = seq->context->getTrack();
  193. float curLength = track->getLength();
  194. if (neededLength > curLength) {
  195. float need = neededLength;
  196. float needBars = need / 4.f;
  197. float roundedBars = std::round(needBars + 1.f);
  198. float duration = roundedBars * 4;
  199. std::shared_ptr<MidiEndEvent> end = track->getEndEvent();
  200. track->deleteEvent(*end);
  201. track->insertEnd(duration);
  202. }
  203. }