PlaybackScreen.cpp 13 KB

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