PlaybackScreen.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  1. #include "Sequencer.h"
  2. #include "PlaybackScreen.h"
  3. #include "colors.h"
  4. #include <iostream>
  5. #include <cassert>
  6. PlaybackScreen::PlaybackScreen(Sequencer& seq)
  7. : parent(seq), beatDisplayOffset(0)
  8. {
  9. }
  10. void PlaybackScreen::enable()
  11. {
  12. if(sequencer.beats.size() == 0) {
  13. beatDisplayOffset = 0;
  14. } else {
  15. beatDisplayOffset = std::min(
  16. beatDisplayOffset,
  17. (Player::BeatIndex)(sequencer.beats.size() - 1) / beatDisplayWidth * beatDisplayWidth
  18. );
  19. }
  20. refreshAll();
  21. parent::enable();
  22. }
  23. void PlaybackScreen::beforeBeat(Player::BeatIndex beat)
  24. {
  25. // std::cout << "before beat #" << beat << std::endl;
  26. beatDisplayOffset = beat / beatDisplayWidth * beatDisplayWidth;
  27. refreshAll();
  28. }
  29. void PlaybackScreen::afterBeat(Player::BeatIndex beat)
  30. {
  31. }
  32. void PlaybackScreen::keyPressed(unsigned char x, unsigned char y)
  33. {
  34. // std::cout << "clicked x=" << (int)x << ", y=" << (int)y << std::endl;
  35. if(x == 8) { // very right
  36. if(y < sequencer.messages.size()) {
  37. std::shared_ptr<midi::Message> msg_ptr = sequencer.messages[y];
  38. msg_ptr->print(std::cout);
  39. sequencer.midiOut.sendMessage(*msg_ptr);
  40. }
  41. } else if(y == 8) { // very top
  42. bool playing = sequencer.player.isPlaying();
  43. switch(x) {
  44. case 2: // left
  45. if(!playing && beatDisplayOffset > 0) {
  46. beatDisplayOffset = std::max((Player::BeatIndex)0, beatDisplayOffset - beatDisplayWidth);
  47. }
  48. break;
  49. case 3: // right
  50. if(!playing && (beatDisplayOffset + beatDisplayWidth) < sequencer.beats.size()) {
  51. beatDisplayOffset += beatDisplayWidth;
  52. }
  53. break;
  54. case 5:
  55. disable();
  56. sequencer.configScreen.enable();
  57. break;
  58. case 7: // start / stop
  59. if(playing) {
  60. sequencer.player.stop();
  61. } else {
  62. sequencer.player.loop();
  63. }
  64. break;
  65. }
  66. } else {
  67. Player::BeatIndex beatIndex = x + beatDisplayOffset;
  68. if(beatIndex < sequencer.beats.size() && y < sequencer.messages.size()) {
  69. std::shared_ptr<midi::NoteOnMessage> noteOnMessage_ptr
  70. = std::dynamic_pointer_cast<midi::NoteOnMessage>(sequencer.messages[y]);
  71. if(noteOnMessage_ptr) {
  72. toggleOnMessage(noteOnMessage_ptr, beatIndex);
  73. }
  74. }
  75. }
  76. refreshAll();
  77. }
  78. void PlaybackScreen::keyReleased(unsigned char x, unsigned char y)
  79. {
  80. if(x == 8) { // very right
  81. if(y < sequencer.messages.size()) {
  82. std::shared_ptr<midi::NoteOnMessage> msgOn_ptr
  83. = std::dynamic_pointer_cast<midi::NoteOnMessage>(sequencer.messages[y]);
  84. if(msgOn_ptr) {
  85. sequencer.midiOut.sendMessage(msgOn_ptr->toOffMessage(0));
  86. }
  87. }
  88. }
  89. refreshAll();
  90. }
  91. void PlaybackScreen::refresh(unsigned char x, unsigned char y)
  92. {
  93. if(x == 8) { // very right
  94. if(y < sequencer.messages.size()) {
  95. setColor(x, y, getKeyPressed(x, y) ? colors::activeOption : colors::inactiveOption);
  96. } else {
  97. setColor(x, y, colors::dark);
  98. }
  99. } else if(y == 8) { // very top
  100. bool playing = sequencer.player.isPlaying();
  101. switch(x) {
  102. case 2: // left
  103. if(!playing && beatDisplayOffset > 0) {
  104. setColor(x, y, colors::inactiveOption);
  105. } else {
  106. setColor(x, y, colors::dark);
  107. }
  108. break;
  109. case 3: // right
  110. if(!playing && (beatDisplayOffset + beatDisplayWidth) < sequencer.beats.size()) {
  111. setColor(x, y, colors::inactiveOption);
  112. } else {
  113. setColor(x, y, colors::dark);
  114. }
  115. break;
  116. case 5:
  117. setColor(x, y, colors::inactiveOption);
  118. break;
  119. case 7: // start / stop
  120. if(playing) {
  121. setColor(x, y, colors::activeOption);
  122. } else {
  123. setColor(x, y, colors::inactiveOption);
  124. }
  125. break;
  126. }
  127. } else {
  128. Player::BeatIndex beatIndex = x + beatDisplayOffset;
  129. if(beatIndex < sequencer.beats.size() && y < sequencer.messages.size()) {
  130. std::shared_ptr<midi::NoteOnMessage> noteOnMessage_ptr
  131. = std::dynamic_pointer_cast<midi::NoteOnMessage>(sequencer.messages[y]);
  132. if(noteOnMessage_ptr) {
  133. setColor(x, y, getBeatColor(noteOnMessage_ptr, beatIndex));
  134. }
  135. } else {
  136. setColor(x, y, colors::dark);
  137. }
  138. }
  139. }
  140. void PlaybackScreen::toggleOnMessage(std::shared_ptr<midi::NoteOnMessage> onMsg_ptr, Player::BeatIndex beatIndex)
  141. {
  142. onMsg_ptr->print(std::cout);
  143. midi::NoteOffMessage offMsg = onMsg_ptr->toOffMessage(0);
  144. const BeatSequenceNoteInformation& noteInfo
  145. = sequencer.beats.getNoteInfo(onMsg_ptr->channel, onMsg_ptr->pitch);
  146. // preceding on msg
  147. BeatIndexMultiset::const_iterator precedingOnBeatIndex_it
  148. = noteInfo.onBeatIndices.less_or_max(beatIndex);
  149. const midi::MessageList& beat = sequencer.beats[beatIndex];
  150. midi::MessageList::const_iterator beatOnMsg_it = beat.find(*onMsg_ptr);
  151. if(beatOnMsg_it == beat.end()) {
  152. // further steps depend on whether a off message has been inserted at the current position
  153. midi::MessageList::const_iterator beatOffMsg_it = beat.find(onMsg_ptr->toOffMessage(0));
  154. BeatIndex nextBeatIndex = (beatIndex + 1) % sequencer.beats.size();
  155. if(beatOffMsg_it == beat.end()) {
  156. // is there a ongoing msg, that has to be stopped first?
  157. BeatIndexMultiset::const_iterator precedingOnMsgBeatIndex_it
  158. = noteInfo.onBeatIndices.less_or_max(beatIndex);
  159. if(precedingOnMsgBeatIndex_it != noteInfo.onBeatIndices.end()) {
  160. // where is this preceding note being stopped?
  161. BeatIndexMultiset::const_iterator precedingOnFollowingOffMsgBeatIndex_it
  162. = noteInfo.offBeatIndices.greater_or_min(*precedingOnMsgBeatIndex_it);
  163. std::cout << "preceding at " << *precedingOnMsgBeatIndex_it << std::endl;
  164. std::cout << "\tstop of preceding at " << *precedingOnFollowingOffMsgBeatIndex_it << std::endl;
  165. std::cout << "beatsInSequence(" << *precedingOnBeatIndex_it << ", " << beatIndex + 1 << ", " << *precedingOnFollowingOffMsgBeatIndex_it << ")" << std::endl;
  166. if(precedingOnFollowingOffMsgBeatIndex_it == noteInfo.offBeatIndices.end()) {
  167. // this preceding note was never stopped
  168. // so stop it before the new note
  169. sequencer.beats.pushFrontMessage(beatIndex, std::shared_ptr<midi::Message>(new midi::NoteOffMessage(offMsg)));
  170. } else if (beatsInSequence(*precedingOnBeatIndex_it, beatIndex + 1, *precedingOnFollowingOffMsgBeatIndex_it)) {
  171. // this preceding note is currently being stopped after the beat
  172. // of the new note. so stop it before the new note.
  173. sequencer.beats.pushFrontMessage(beatIndex, std::shared_ptr<midi::Message>(new midi::NoteOffMessage(offMsg)));
  174. // since the new note will be stopped on the next beat,
  175. // remove this extra off message, which no longer stops the preceding note.
  176. const midi::MessageList& precedingOnFollowingOffBeat
  177. = sequencer.beats[*precedingOnFollowingOffMsgBeatIndex_it];
  178. const midi::MessageList::const_iterator precedingOnFollowingOffMsg_it
  179. = precedingOnFollowingOffBeat.find(offMsg);
  180. assert(precedingOnFollowingOffMsg_it != precedingOnFollowingOffBeat.end());
  181. sequencer.beats.eraseMessage(
  182. *precedingOnFollowingOffMsgBeatIndex_it,
  183. precedingOnFollowingOffMsg_it
  184. );
  185. }
  186. }
  187. // on msg on current beat & off msg on next beat
  188. sequencer.beats.pushBackMessage(beatIndex, std::dynamic_pointer_cast<midi::Message>(onMsg_ptr));
  189. } else {
  190. // move off to next beat
  191. sequencer.beats.eraseMessage(beatIndex, beatOffMsg_it);
  192. }
  193. // stop at following beat
  194. sequencer.beats.pushFrontMessage(nextBeatIndex, std::shared_ptr<midi::Message>(new midi::NoteOffMessage(offMsg)));
  195. } else {
  196. // remove following off message, if not relevant for other on message.
  197. BeatIndexMultiset::const_iterator followingOffBeatIndex_it
  198. = noteInfo.offBeatIndices.greater_or_min(beatIndex);
  199. if(followingOffBeatIndex_it != noteInfo.offBeatIndices.end()) {
  200. BeatIndex followingOffBeatIndex = *followingOffBeatIndex_it;
  201. BeatIndexMultiset::const_iterator offPrecedingOnBeatIndex_it
  202. = noteInfo.onBeatIndices.less_or_max(followingOffBeatIndex);
  203. assert(offPrecedingOnBeatIndex_it != noteInfo.onBeatIndices.end());
  204. assert(*offPrecedingOnBeatIndex_it >= beatIndex);
  205. if(*offPrecedingOnBeatIndex_it == beatIndex) {
  206. // no relevant on message before off found. so off may be removed.
  207. const midi::MessageList& followingOffBeat = sequencer.beats[followingOffBeatIndex];
  208. midi::MessageList::const_iterator followingOffMsg_it = followingOffBeat.find(offMsg);
  209. assert(followingOffMsg_it != followingOffBeat.end());
  210. sequencer.beats.eraseMessage(*followingOffBeatIndex_it, followingOffMsg_it);
  211. }
  212. }
  213. // previous on without off before to-be-removed on ?
  214. if(precedingOnBeatIndex_it != noteInfo.onBeatIndices.end()) {
  215. std::cout << "previous: " << *precedingOnBeatIndex_it << std::endl;
  216. }
  217. // remove on message from beat
  218. sequencer.beats.eraseMessage(beatIndex, beatOnMsg_it);
  219. }
  220. sequencer.beats.printRegistry(std::cout);
  221. }
  222. midi::LaunchpadColor PlaybackScreen::getBeatColor(std::shared_ptr<midi::NoteOnMessage> onMsg_ptr, BeatIndex beatIndex)
  223. {
  224. const BeatSequenceNoteInformation& noteInfo
  225. = sequencer.beats.getNoteInfo(onMsg_ptr->channel, onMsg_ptr->pitch);
  226. BeatIndexMultiset::const_iterator onIndex_it
  227. = noteInfo.onBeatIndices.equal_or_less_or_max(beatIndex);
  228. if(onIndex_it == noteInfo.onBeatIndices.end()) {
  229. return colors::dark;
  230. } else {
  231. bool isNextBeat = (sequencer.player.getNextBeat() == beatIndex);
  232. if(*onIndex_it == beatIndex) {
  233. // on msg on current beat
  234. if(noteInfo.offBeatIndices.find(*onIndex_it) != noteInfo.offBeatIndices.end()) {
  235. return isNextBeat ? colors::activeOffOnBeat : colors::inactiveOffOnBeat;
  236. } else {
  237. return isNextBeat ? colors::activeOnBeat : colors::inactiveOnBeat;
  238. }
  239. } else {
  240. // continuing msg from previous beat ?
  241. BeatIndexMultiset::const_iterator offIndex_it
  242. = noteInfo.offBeatIndices.greater_or_min(*onIndex_it);
  243. if(offIndex_it == noteInfo.offBeatIndices.end()
  244. || beatsInSequence(*onIndex_it, beatIndex, (*offIndex_it - 1) % sequencer.beats.size())) {
  245. return isNextBeat ? colors::activeContinuingBeat : colors::inactiveContinuingBeat;
  246. }
  247. }
  248. }
  249. }
  250. bool PlaybackScreen::beatsInSequence(BeatIndex a, BeatIndex b, BeatIndex c)
  251. {
  252. if(a <= b) {
  253. return b <= c || c < a;
  254. } else {
  255. return b <= c && c < a;
  256. }
  257. }