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.

603 lines
17KB

  1. #include "asserts.h"
  2. #include "MidiEditor.h"
  3. #include "MidiEvent.h"
  4. #include "MidiSelectionModel.h"
  5. #include "MidiSequencer.h"
  6. #include "MidiTrack.h"
  7. #include "MidiSong.h"
  8. static int _trackNumber = 0;
  9. // sequencer factory - helper function
  10. MidiSequencerPtr makeTest(bool empty = false)
  11. {
  12. MidiSongPtr song = empty ?
  13. MidiSong::MidiSong::makeTest(MidiTrack::TestContent::empty, _trackNumber) :
  14. MidiSong::MidiSong::makeTest(MidiTrack::TestContent::eightQNotes, _trackNumber);
  15. MidiSequencerPtr sequencer = std::make_shared<MidiSequencer>(song);
  16. sequencer->makeEditor();
  17. sequencer->context->setTrackNumber(_trackNumber);
  18. sequencer->context->setStartTime(0);
  19. sequencer->context->setEndTime(
  20. sequencer->context->startTime() + 8);
  21. sequencer->context->setPitchLow(PitchUtils::pitchToCV(3, 0));
  22. sequencer->context->setPitchHi(PitchUtils::pitchToCV(5, 0));
  23. sequencer->assertValid();
  24. return sequencer;
  25. }
  26. static bool cursorOnSelection(MidiSequencerPtr seq)
  27. {
  28. if (seq->selection->empty()) {
  29. return true;
  30. }
  31. assert(seq->selection->size() == 1); // haven't done multi yet
  32. MidiEventPtr sel = *seq->selection->begin();
  33. MidiNoteEventPtr note = safe_cast<MidiNoteEvent>(sel);
  34. assert(note);
  35. // for now, do exact match
  36. if ((note->startTime == seq->context->cursorTime()) &&
  37. (note->pitchCV == seq->context->cursorPitch())) {
  38. return true;
  39. }
  40. return false;
  41. }
  42. // from a null selection, select next
  43. static void testNext1()
  44. {
  45. MidiSequencerPtr seq = makeTest();
  46. seq->editor->selectNextNote();
  47. assertEQ(seq->selection->size(), 1); // should be one note selected
  48. // note should be first one
  49. MidiEventPtr firstEvent = seq->context->getTrack()->begin()->second;
  50. assert(seq->selection->isSelected(firstEvent));
  51. assert(cursorOnSelection(seq));
  52. }
  53. // from a null selection, select previous. should select last note
  54. static void testNext1b()
  55. {
  56. MidiSequencerPtr seq = makeTest();
  57. seq->editor->selectPrevNote();
  58. assertEQ(seq->selection->size(), 1); // should be one note selected
  59. // note should be last one
  60. auto it = seq->context->getTrack()->end();
  61. it--;
  62. it--;
  63. MidiEventPtr lastEvent = it->second;
  64. assert(lastEvent->type == MidiEvent::Type::Note);
  65. assert(seq->selection->isSelected(lastEvent));
  66. assert(cursorOnSelection(seq));
  67. }
  68. // from a non-null selection, select next
  69. static void testNext2()
  70. {
  71. MidiSequencerPtr seq = makeTest();
  72. seq->editor->selectNextNote();
  73. assertEQ(seq->selection->size(), 1); // should be one note selected
  74. MidiEventPtr firstEvent = seq->context->getTrack()->begin()->second;
  75. assert(seq->selection->isSelected(firstEvent));
  76. // Above is just test1, so now first event selected
  77. seq->editor->selectNextNote();
  78. assertEQ(seq->selection->size(), 1); // should be one note selected
  79. auto iter = seq->context->getTrack()->begin();
  80. ++iter;
  81. MidiEventPtr secondEvent = iter->second;
  82. assert(seq->selection->isSelected(secondEvent));
  83. }
  84. // from a non-null selection, select previous
  85. static void testNext2b()
  86. {
  87. MidiSequencerPtr seq = makeTest();
  88. // Select the second note in the Seq
  89. seq->editor->selectNextNote();
  90. seq->editor->selectNextNote();
  91. assertEQ(seq->selection->size(), 1);
  92. // Verify that second note is selected
  93. auto iter = seq->context->getTrack()->begin();
  94. ++iter;
  95. MidiEventPtr secondEvent = iter->second;
  96. assert(seq->selection->isSelected(secondEvent));
  97. // Above is just test1, so now second event selected
  98. seq->editor->selectPrevNote();
  99. assertEQ(seq->selection->size(), 1); // should be one note selected
  100. iter = seq->context->getTrack()->begin();
  101. MidiEventPtr firstEvent = iter->second;
  102. assert(seq->selection->isSelected(firstEvent));
  103. }
  104. // from a null selection, select next in null track
  105. static void testNext3()
  106. {
  107. MidiSequencerPtr seq = makeTest(true);
  108. seq->editor->selectNextNote();
  109. assertEQ(seq->selection->size(), 0); // should be nothing selected
  110. assert(seq->selection->empty());
  111. }
  112. // from a null selection, select previous in null track
  113. static void testNext3b()
  114. {
  115. MidiSequencerPtr seq = makeTest(true);
  116. seq->editor->selectPrevNote();
  117. assertEQ(seq->selection->size(), 0); // should be nothing selected
  118. assert(seq->selection->empty());
  119. }
  120. // select one after another until end
  121. static void testNext4()
  122. {
  123. MidiSequencerPtr seq = makeTest();
  124. int notes = 0;
  125. for (bool done = false; !done; ) {
  126. seq->editor->selectNextNote();
  127. if (seq->selection->empty()) {
  128. done = true;
  129. } else {
  130. ++notes;
  131. }
  132. }
  133. assertEQ(notes, 8);
  134. }
  135. // select next that off way out of viewport
  136. static void testNext5()
  137. {
  138. MidiSequencerPtr seq = makeTest();
  139. seq->editor->selectNextNote();
  140. assertEQ(seq->selection->size(), 1);
  141. // Give the (first) note a pitch and start time that are
  142. // way outside viewport
  143. seq->editor->changePitch(50);
  144. //temporary kludge, because change pitch has a bug.
  145. // We need to re-select the note in question
  146. {
  147. printf("remove this hack\n");
  148. seq->selection->clear();
  149. seq->editor->selectNextNote();
  150. }
  151. seq->editor->changeStartTime(false, 50);
  152. assertEQ(seq->selection->size(), 1);
  153. seq->assertValid();
  154. seq->editor->assertCursorInSelection();
  155. seq->editor->selectPrevNote();
  156. assertEQ(seq->selection->size(), 1);
  157. seq->assertValid();
  158. seq->editor->assertCursorInSelection();
  159. seq->editor->selectNextNote();
  160. assertEQ(seq->selection->size(), 1);
  161. seq->assertValid();
  162. seq->editor->assertCursorInSelection();
  163. }
  164. static void testPrev1()
  165. {
  166. MidiSequencerPtr seq = makeTest(false);
  167. seq->editor->selectNextNote(); // now first is selected
  168. assert(!seq->selection->empty());
  169. seq->editor->assertCursorInSelection();
  170. seq->editor->selectPrevNote();
  171. assert(seq->selection->empty());
  172. }
  173. // transpose one semi
  174. static void testTrans1()
  175. {
  176. MidiSequencerPtr seq = makeTest(false);
  177. seq->editor->selectNextNote(); // now first is selected
  178. const float firstNotePitch = PitchUtils::pitchToCV(3, 0);
  179. assertClose(seq->context->cursorPitch(), firstNotePitch, .0001);
  180. MidiEventPtr firstEvent = seq->context->getTrack()->begin()->second;
  181. MidiNoteEventPtr firstNote = safe_cast<MidiNoteEvent>(firstEvent);
  182. const float p0 = firstNote->pitchCV;
  183. seq->editor->changePitch(1);
  184. //seq->assertValid();
  185. // after transpose, need to find first note again.
  186. firstEvent = seq->context->getTrack()->begin()->second;
  187. firstNote = safe_cast<MidiNoteEvent>(firstEvent);
  188. const float p1 = firstNote->pitchCV;
  189. assertClose(p1 - p0, 1.f / 12.f, .000001);
  190. const float transposedPitch = PitchUtils::pitchToCV(3, 1);
  191. assertClose(seq->context->cursorPitch(), transposedPitch, .0001);
  192. seq->assertValid();
  193. seq->editor->assertCursorInSelection();
  194. }
  195. static void testTrans3()
  196. {
  197. MidiSequencerPtr seq = makeTest(false);
  198. seq->editor->selectNextNote(); // now first is selected
  199. const float firstNotePitch = PitchUtils::pitchToCV(3, 0);
  200. assertClose(seq->context->cursorPitch(), firstNotePitch, .0001);
  201. MidiEventPtr firstEvent = seq->context->getTrack()->begin()->second;
  202. MidiNoteEventPtr firstNote = safe_cast<MidiNoteEvent>(firstEvent);
  203. const float p0 = firstNote->pitchCV;
  204. seq->editor->changePitch(50); // transpose off screen
  205. seq->assertValid();
  206. seq->editor->assertCursorInSelection();
  207. }
  208. static void testShiftTime1()
  209. {
  210. MidiSequencerPtr seq = makeTest(false);
  211. seq->editor->selectNextNote(); // now first is selected
  212. MidiNoteEventPtr firstNote = seq->context->getTrack()->getFirstNote();
  213. const float s0 = firstNote->startTime;
  214. seq->editor->changeStartTime(false, 1); // delay one unit (1/16 6h)
  215. firstNote = seq->context->getTrack()->getFirstNote();
  216. const float s1 = firstNote->startTime;
  217. assertClose(s1 - s0, 1.f / 4.f, .000001);
  218. seq->editor->changeStartTime(false, -50);
  219. firstNote = seq->context->getTrack()->getFirstNote();
  220. const float s2 = firstNote->startTime;
  221. assertEQ(s2, 0);
  222. seq->assertValid();
  223. seq->editor->assertCursorInSelection();
  224. }
  225. static void testShiftTimex(int units)
  226. {
  227. MidiSequencerPtr seq = makeTest(false);
  228. seq->editor->selectNextNote(); // now first is selected
  229. MidiEventPtr firstEvent = seq->context->getTrack()->begin()->second;
  230. MidiNoteEventPtr firstNote = safe_cast<MidiNoteEvent>(firstEvent);
  231. const float s0 = firstNote->startTime;
  232. seq->editor->changeStartTime(false, units); // delay n units
  233. seq->assertValid();
  234. assertEQ(seq->selection->size(), 1);
  235. seq->editor->assertCursorInSelection();
  236. }
  237. static void testShiftTime2()
  238. {
  239. testShiftTimex(20);
  240. }
  241. static void testShiftTime3()
  242. {
  243. testShiftTimex(50);
  244. }
  245. static void testChangeDuration1()
  246. {
  247. MidiSequencerPtr seq = makeTest(false);
  248. seq->editor->selectNextNote(); // now first is selected
  249. MidiNoteEventPtr firstNote = seq->context->getTrack()->getFirstNote();
  250. const float d0 = firstNote->duration;
  251. seq->editor->changeDuration(false, 1); // lengthen one unit
  252. firstNote = seq->context->getTrack()->getFirstNote();
  253. const float d1 = firstNote->duration;
  254. assertClose(d1 - d0, 1.f / 4.f, .000001);
  255. seq->assertValid();
  256. // try to make negative, should not go below 1
  257. seq->editor->changeDuration(false, -50);
  258. firstNote = seq->context->getTrack()->getFirstNote();
  259. const float d2 = firstNote->duration;
  260. assertGT(d2, 0);
  261. seq->assertValid();
  262. }
  263. // transpose multi
  264. static void testTrans2()
  265. {
  266. MidiSequencerPtr seq = makeTest(false);
  267. seq->editor->selectNextNote(); // now first is selected
  268. MidiEventPtr firstEvent = seq->context->getTrack()->begin()->second;
  269. MidiNoteEventPtr firstNote = safe_cast<MidiNoteEvent>(firstEvent);
  270. const float p0 = firstNote->pitchCV;
  271. seq->editor->changePitch(1);
  272. firstEvent = seq->context->getTrack()->begin()->second;
  273. firstNote = safe_cast<MidiNoteEvent>(firstEvent);
  274. const float p1 = firstNote->pitchCV;
  275. assertClose(p1 - p0, 1.f / 12.f, .000001);
  276. seq->assertValid();
  277. assert(seq->undo->canUndo());
  278. seq->undo->undo();
  279. MidiNoteEventPtr firstNoteAfterUndo = safe_cast<MidiNoteEvent>(seq->context->getTrack()->begin()->second);
  280. const float p3 = firstNoteAfterUndo->pitchCV;
  281. assertClose(p3, p0, .000001);
  282. seq->undo->redo();
  283. MidiNoteEventPtr firstNoteAfterRedo = safe_cast<MidiNoteEvent>(seq->context->getTrack()->begin()->second);
  284. const float p4 = firstNoteAfterRedo->pitchCV;
  285. assertClose(p4, p1, .000001);
  286. }
  287. static void testCursor1()
  288. {
  289. MidiSequencerPtr seq = makeTest(false);
  290. assertEQ(seq->context->cursorTime(), 0);
  291. assertEQ(seq->context->cursorPitch(), 0)
  292. assertEQ(seq->context->startTime(), 0);
  293. }
  294. static void testCursor2()
  295. {
  296. MidiSequencerPtr seq = makeTest(false);
  297. seq->editor->advanceCursor(false, 1);
  298. assertEQ(seq->context->cursorTime(), 1.f / 4.f);
  299. seq->editor->advanceCursor(false, -4);
  300. assertEQ(seq->context->cursorTime(), 0);
  301. assertEQ(seq->context->startTime(), 0);
  302. }
  303. static void testCursor3()
  304. {
  305. MidiSequencerPtr seq = makeTest(false);
  306. seq->editor->selectNextNote();
  307. // Select first note to put cursor in it
  308. assertEQ(seq->context->cursorTime(), 0);
  309. MidiNoteEvent note;
  310. note.setPitch(3, 0);
  311. assertEQ(seq->context->cursorPitch(), note.pitchCV);
  312. // Now advance a 1/4 note
  313. seq->editor->advanceCursor(false, 4);
  314. assertEQ(seq->context->cursorTime(), 1.f);
  315. assert(seq->selection->empty());
  316. assertEQ(seq->context->startTime(), 0);
  317. }
  318. // move multiple times in two directions
  319. static void testCursor4()
  320. {
  321. MidiSequencerPtr seq = makeTest(false);
  322. seq->editor->selectNextNote();
  323. // Select first note to put cursor in it
  324. assertEQ(seq->context->cursorTime(), 0);
  325. MidiNoteEvent note;
  326. note.setPitch(3, 0);
  327. assertEQ(seq->context->cursorPitch(), note.pitchCV);
  328. // Now advance up 3
  329. seq->editor->changeCursorPitch(1);
  330. seq->editor->changeCursorPitch(1);
  331. seq->editor->changeCursorPitch(1);
  332. for (int i = 0; i < 12; ++i) {
  333. seq->editor->advanceCursor(false, 1);
  334. }
  335. assert(!seq->selection->empty());
  336. assertEQ(seq->context->startTime(), 0);
  337. }
  338. // move up to scroll viewport
  339. static void testCursor4b()
  340. {
  341. MidiSequencerPtr seq = makeTest(false);
  342. seq->editor->selectNextNote();
  343. // Select first note to put cursor in it
  344. assertEQ(seq->context->cursorTime(), 0);
  345. MidiNoteEvent note;
  346. note.setPitch(3, 0);
  347. assertEQ(seq->context->cursorPitch(), note.pitchCV);
  348. // Now advance up 3 octaves
  349. seq->editor->changeCursorPitch(3 * 12);
  350. assert(seq->selection->empty());
  351. seq->assertValid();
  352. }
  353. // just past end of note
  354. static void testCursor5()
  355. {
  356. MidiSequencerPtr seq = makeTest(false);
  357. seq->editor->selectNextNote();
  358. // Select first note to put cursor in it
  359. assertEQ(seq->context->cursorTime(), 0);
  360. MidiNoteEvent note;
  361. note.setPitch(3, 0);
  362. assertEQ(seq->context->cursorPitch(), note.pitchCV);
  363. // Now advance two units right, to end of note
  364. seq->editor->advanceCursor(false, 1);
  365. seq->editor->advanceCursor(false, 1);
  366. assert(seq->selection->empty());
  367. assertEQ(seq->context->startTime(), 0);
  368. }
  369. // move past the end of the second bar
  370. static void testCursor6()
  371. {
  372. MidiSequencerPtr seq = makeTest(false);
  373. assertEQ(seq->context->startTime(), 0);
  374. seq->assertValid();
  375. // go up two bars and 1/16
  376. seq->editor->advanceCursor(false, 16 * 2 + 1);
  377. // bar 2 should be new start time
  378. assertEQ(seq->context->startTime(), 2 * 4);
  379. }
  380. static void testInsertSub(int advancUnits)
  381. {
  382. MidiSequencerPtr seq = makeTest(true);
  383. assert(seq->selection->empty());
  384. const int initialSize = seq->context->getTrack()->size();
  385. seq->editor->advanceCursor(false, advancUnits); // move up a half note
  386. float pitch = seq->context->cursorPitch();
  387. seq->editor->insertNote();
  388. auto it = seq->context->getTrack()->begin();
  389. assert(it != seq->context->getTrack()->end());
  390. MidiEventPtr ev = it->second;
  391. MidiNoteEventPtr note = safe_cast<MidiNoteEvent>(ev);
  392. assert(note);
  393. assertEQ(note->pitchCV, pitch);
  394. assertEQ(seq->selection->size(), 1);
  395. assert(seq->selection->isSelected(note));
  396. seq->assertValid();
  397. const int insertSize = seq->context->getTrack()->size();
  398. assertGT(insertSize, initialSize);
  399. printf("finish undo for insert note\n");
  400. #if 0
  401. assert(seq->undo->canUndo());
  402. seq->undo->undo();
  403. const int undoSize = seq->context->getTrack()->size();
  404. assert(undoSize == initialSize);
  405. #endif
  406. }
  407. static void testInsert()
  408. {
  409. testInsertSub(8);
  410. }
  411. static void testInsert2()
  412. {
  413. testInsertSub(34); //middle of second bar
  414. }
  415. static void testDelete()
  416. {
  417. MidiSequencerPtr seq = makeTest(false);
  418. seq->editor->selectNextNote();
  419. auto it = seq->context->getTrack()->begin();
  420. assert(it != seq->context->getTrack()->end());
  421. MidiEventPtr ev = it->second;
  422. MidiNoteEventPtr firstNote = safe_cast<MidiNoteEvent>(ev);
  423. assert(firstNote);
  424. assertEQ(firstNote->startTime, 0);
  425. assertEQ(seq->selection->size(), 1);
  426. seq->editor->deleteNote();
  427. assert(seq->selection->empty());
  428. it = seq->context->getTrack()->begin();
  429. assert(it != seq->context->getTrack()->end());
  430. ev = it->second;
  431. MidiNoteEventPtr secondNote = safe_cast<MidiNoteEvent>(ev);
  432. assert(secondNote);
  433. assertEQ(secondNote->startTime, 1.f);
  434. seq->assertValid();
  435. }
  436. // delete a note with undo/redo
  437. static void testDelete2()
  438. {
  439. MidiSequencerPtr seq = makeTest(false);
  440. seq->editor->selectNextNote();
  441. const int trackSizeBefore = seq->context->getTrack()->size();
  442. seq->editor->deleteNote();
  443. const int trackSizeAfter = seq->context->getTrack()->size();
  444. assertLT(trackSizeAfter, trackSizeBefore);
  445. assert(seq->undo->canUndo());
  446. seq->undo->undo();
  447. const int trackSizeAfterUndo = seq->context->getTrack()->size();
  448. assertEQ(trackSizeAfterUndo, trackSizeBefore);
  449. }
  450. void testMidiEditorSub(int trackNumber)
  451. {
  452. _trackNumber = trackNumber;
  453. testNext1();
  454. testNext1b();
  455. testNext2();
  456. testNext2b();
  457. testNext3();
  458. testNext3b();
  459. testPrev1();
  460. testNext4();
  461. testTrans1();
  462. testShiftTime1();
  463. testShiftTime2();
  464. testShiftTime3();
  465. testChangeDuration1();
  466. testTrans2();
  467. testTrans3();
  468. testCursor1();
  469. testCursor2();
  470. testCursor3();
  471. testCursor4();
  472. testCursor4b();
  473. testCursor5();
  474. testCursor6();
  475. testInsert();
  476. testInsert2();
  477. testDelete();
  478. testDelete2();
  479. testNext5();
  480. }
  481. void testMidiEditor()
  482. {
  483. testMidiEditorSub(0);
  484. testMidiEditorSub(2);
  485. }