123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314 |
- #include "Sequencer.h"
- #include "PlaybackScreen.h"
- #include "colors.h"
- #include <iostream>
- #include <cassert>
- PlaybackScreen::PlaybackScreen(Sequencer& seq)
- : parent(seq), beatDisplayOffset(0), messageDisplayOffset(0)
- {
- }
- void PlaybackScreen::enable()
- {
- if(sequencer.beats.size() == 0) {
- beatDisplayOffset = 0;
- } else {
- beatDisplayOffset = std::min(
- beatDisplayOffset,
- (Player::BeatIndex)(sequencer.beats.size() - 1) / beatDisplayWidth * beatDisplayWidth
- );
- }
- if(messageDisplayOffset >= sequencer.messages.size()) {
- messageDisplayOffset = sequencer.messages.size() - 1;
- }
- refreshAll();
- parent::enable();
- }
- void PlaybackScreen::beforeBeat(Player::BeatIndex beat)
- {
- // std::cout << "before beat #" << beat << std::endl;
- beatDisplayOffset = beat / beatDisplayWidth * beatDisplayWidth;
- refreshAll();
- }
- void PlaybackScreen::afterBeat(Player::BeatIndex beat)
- {
- }
- void PlaybackScreen::keyPressed(unsigned char x, unsigned char y)
- {
- // std::cout << "clicked x=" << (int)x << ", y=" << (int)y << std::endl;
- if(x == 8) { // very right
- std::size_t messageIndex = y + messageDisplayOffset;
- if(messageIndex < sequencer.messages.size()) {
- std::shared_ptr<midi::Message> msg_ptr = sequencer.messages[messageIndex];
- msg_ptr->print(std::cout);
- sequencer.midiOut.sendMessage(*msg_ptr);
- }
- } else if(y == 8) { // very top
- bool playing = sequencer.player.isPlaying();
- switch(x) {
- case 0: // down
- if(messageDisplayOffset > 0) {
- int offset = messageDisplayOffset;
- offset -= messageDisplayShift;
- messageDisplayOffset = std::max(0, offset);
- }
- break;
- case 1: // up
- if(messageDisplayOffset + beatsHeight < sequencer.messages.size()) {
- messageDisplayOffset = std::min(
- (MessageDisplayOffset)sequencer.messages.size() - 1,
- messageDisplayOffset + messageDisplayShift
- );
- }
- break;
- case 2: // left
- if(!playing && beatDisplayOffset > 0) {
- beatDisplayOffset = std::max(0, (int)beatDisplayOffset - beatDisplayWidth);
- }
- break;
- case 3: // right
- if(!playing && (beatDisplayOffset + beatDisplayWidth) < sequencer.beats.size()) {
- beatDisplayOffset += beatDisplayWidth;
- }
- break;
- case 5:
- disable();
- sequencer.configScreen.enable();
- break;
- case 7: // start / stop
- if(playing) {
- sequencer.player.stop();
- } else {
- sequencer.player.loop();
- }
- break;
- }
- } else {
- std::size_t messageIndex = y + messageDisplayOffset;
- Player::BeatIndex beatIndex = x + beatDisplayOffset;
- if(beatIndex < sequencer.beats.size() && messageIndex < sequencer.messages.size()) {
- std::shared_ptr<midi::NoteOnMessage> noteOnMessage_ptr
- = std::dynamic_pointer_cast<midi::NoteOnMessage>(sequencer.messages[messageIndex]);
- if(noteOnMessage_ptr) {
- toggleOnMessage(noteOnMessage_ptr, beatIndex);
- }
- }
- }
- refreshAll();
- }
- void PlaybackScreen::keyReleased(unsigned char x, unsigned char y)
- {
- if(x == 8) { // very right
- // this might not be the right message
- std::size_t messageIndex = y + messageDisplayOffset;
- if(messageIndex < sequencer.messages.size()) {
- std::shared_ptr<midi::NoteOnMessage> msgOn_ptr
- = std::dynamic_pointer_cast<midi::NoteOnMessage>(sequencer.messages[messageIndex]);
- if(msgOn_ptr) {
- sequencer.midiOut.sendMessage(msgOn_ptr->toOffMessage(0));
- }
- }
- }
- refreshAll();
- }
- void PlaybackScreen::refresh(unsigned char x, unsigned char y)
- {
- if(x == 8) { // very right
- std::size_t messageIndex = y + messageDisplayOffset;
- if(messageIndex < sequencer.messages.size()) {
- setColor(x, y, getKeyPressed(x, y) ? colors::activeOption : colors::inactiveOption);
- } else {
- setColor(x, y, colors::dark);
- }
- } else if(y == 8) { // very top
- bool playing = sequencer.player.isPlaying();
- switch(x) {
- case 0: // down
- if(messageDisplayOffset > 0) {
- setColor(x, y, colors::inactiveOption);
- } else {
- setColor(x, y, colors::dark);
- }
- break;
- case 1: // up
- if(messageDisplayOffset + beatsHeight < sequencer.messages.size()) {
- setColor(x, y, colors::inactiveOption);
- } else {
- setColor(x, y, colors::dark);
- }
- break;
- case 2: // left
- if(!playing && beatDisplayOffset > 0) {
- setColor(x, y, colors::inactiveOption);
- } else {
- setColor(x, y, colors::dark);
- }
- break;
- case 3: // right
- if(!playing && (beatDisplayOffset + beatDisplayWidth) < sequencer.beats.size()) {
- setColor(x, y, colors::inactiveOption);
- } else {
- setColor(x, y, colors::dark);
- }
- break;
- case 5:
- setColor(x, y, colors::inactiveOption);
- break;
- case 7: // start / stop
- if(playing) {
- setColor(x, y, colors::activeOption);
- } else {
- setColor(x, y, colors::inactiveOption);
- }
- break;
- }
- } else {
- std::size_t messageIndex = y + messageDisplayOffset;
- Player::BeatIndex beatIndex = x + beatDisplayOffset;
- if(beatIndex < sequencer.beats.size() && messageIndex < sequencer.messages.size()) {
- std::shared_ptr<midi::NoteOnMessage> noteOnMessage_ptr
- = std::dynamic_pointer_cast<midi::NoteOnMessage>(sequencer.messages[messageIndex]);
- if(noteOnMessage_ptr) {
- setColor(x, y, getBeatColor(noteOnMessage_ptr, beatIndex));
- }
- } else {
- setColor(x, y, colors::dark);
- }
- }
- }
-
- void PlaybackScreen::toggleOnMessage(std::shared_ptr<midi::NoteOnMessage> onMsg_ptr, Player::BeatIndex beatIndex)
- {
- onMsg_ptr->print(std::cout);
- midi::NoteOffMessage offMsg = onMsg_ptr->toOffMessage(0);
- const BeatSequenceNoteInformation& noteInfo
- = sequencer.beats.getNoteInfo(onMsg_ptr->channel, onMsg_ptr->pitch);
- midi::MessageList& beat = sequencer.beats[beatIndex];
- midi::MessageList::iterator beatOnMsg_it = beat.find(*onMsg_ptr);
- if(beatOnMsg_it == beat.end()) {
- // further steps depend on whether a off message has been inserted at the current position
- midi::MessageList::iterator beatOffMsg_it = beat.find(onMsg_ptr->toOffMessage(0));
- BeatIndex nextBeatIndex = (beatIndex + 1) % sequencer.beats.size();
- if(beatOffMsg_it == beat.end()) {
- // is there a ongoing msg, that has to be stopped first?
- BeatIndexMultiset::const_iterator precedingOnMsgBeatIndex_it
- = noteInfo.onBeatIndices.less_or_max(beatIndex);
- if(precedingOnMsgBeatIndex_it != noteInfo.onBeatIndices.end()) {
- // where is this preceding note being stopped?
- BeatIndexMultiset::const_iterator precedingOnFollowingOffMsgBeatIndex_it
- = noteInfo.offBeatIndices.greater_or_min(*precedingOnMsgBeatIndex_it);
- std::cout << "preceding at " << *precedingOnMsgBeatIndex_it << std::endl;
- std::cout << "\tstop of preceding at " << *precedingOnFollowingOffMsgBeatIndex_it << std::endl;
- std::cout << "beatsInSequence(" << *precedingOnMsgBeatIndex_it << ", " << beatIndex + 1 << ", " << *precedingOnFollowingOffMsgBeatIndex_it << ")" << std::endl;
- if(precedingOnFollowingOffMsgBeatIndex_it == noteInfo.offBeatIndices.end()) {
- // this preceding note was never stopped
- // so stop it before the new note
- sequencer.beats.pushFrontMessage(beatIndex, std::shared_ptr<midi::Message>(new midi::NoteOffMessage(offMsg)));
- } else if (beatsInSequence(*precedingOnMsgBeatIndex_it, beatIndex, *precedingOnFollowingOffMsgBeatIndex_it)) {
- // this preceding note is currently being stopped after the beat
- // of the new note. so stop it before the new note.
- sequencer.beats.pushFrontMessage(beatIndex, std::shared_ptr<midi::Message>(new midi::NoteOffMessage(offMsg)));
- // since the new note will be stopped on the next beat,
- // remove this extra off message, which no longer stops the preceding note.
- midi::MessageList& precedingOnFollowingOffBeat
- = sequencer.beats[*precedingOnFollowingOffMsgBeatIndex_it];
- const midi::MessageList::iterator precedingOnFollowingOffMsg_it
- = precedingOnFollowingOffBeat.find(offMsg);
- assert(precedingOnFollowingOffMsg_it != precedingOnFollowingOffBeat.end());
- sequencer.beats.eraseMessage(
- *precedingOnFollowingOffMsgBeatIndex_it,
- precedingOnFollowingOffMsg_it
- );
- }
- }
- // on msg on current beat & off msg on next beat
- sequencer.beats.pushBackMessage(beatIndex, std::dynamic_pointer_cast<midi::Message>(onMsg_ptr));
- } else {
- // move off to next beat
- sequencer.beats.eraseMessage(beatIndex, beatOffMsg_it);
- }
- // stop at following beat
- sequencer.beats.pushFrontMessage(nextBeatIndex, std::shared_ptr<midi::Message>(new midi::NoteOffMessage(offMsg)));
- } else {
- // remove following off message, if not relevant for other on message.
- BeatIndexMultiset::const_iterator followingOffBeatIndex_it
- = noteInfo.offBeatIndices.greater_or_min(beatIndex);
- if(followingOffBeatIndex_it != noteInfo.offBeatIndices.end()) {
- BeatIndex followingOffBeatIndex = *followingOffBeatIndex_it;
- BeatIndexMultiset::const_iterator offPrecedingOnBeatIndex_it
- = noteInfo.onBeatIndices.less_or_max(followingOffBeatIndex);
- assert(offPrecedingOnBeatIndex_it != noteInfo.onBeatIndices.end());
- assert(*offPrecedingOnBeatIndex_it >= beatIndex);
- if(*offPrecedingOnBeatIndex_it == beatIndex) {
- // no relevant on message before off found. so off may be removed.
- midi::MessageList& followingOffBeat = sequencer.beats[followingOffBeatIndex];
- midi::MessageList::iterator followingOffMsg_it = followingOffBeat.find(offMsg);
- assert(followingOffMsg_it != followingOffBeat.end());
- sequencer.beats.eraseMessage(*followingOffBeatIndex_it, followingOffMsg_it);
- }
- }
- // previous on without off before to-be-removed on ?
- BeatIndexMultiset::const_iterator precedingOnBeatIndex_it
- = noteInfo.onBeatIndices.less_or_max(beatIndex);
- if(precedingOnBeatIndex_it != noteInfo.onBeatIndices.end()) {
- std::cout << "previous: " << *precedingOnBeatIndex_it << std::endl;
- }
- // remove on message from beat
- sequencer.beats.eraseMessage(beatIndex, beatOnMsg_it);
- }
- sequencer.beats.printRegistry(std::cout);
- }
-
- midi::LaunchpadColor PlaybackScreen::getBeatColor(std::shared_ptr<midi::NoteOnMessage> onMsg_ptr, BeatIndex beatIndex)
- {
- const BeatSequenceNoteInformation& noteInfo
- = sequencer.beats.getNoteInfo(onMsg_ptr->channel, onMsg_ptr->pitch);
-
- BeatIndexMultiset::const_iterator onIndex_it
- = noteInfo.onBeatIndices.equal_or_less_or_max(beatIndex);
- if(onIndex_it == noteInfo.onBeatIndices.end()) {
- return colors::dark;
- } else {
- bool isNextBeat = (sequencer.player.getNextBeat() == beatIndex);
- if(*onIndex_it == beatIndex) {
- // on msg on current beat
- if(noteInfo.offBeatIndices.find(*onIndex_it) != noteInfo.offBeatIndices.end()) {
- return isNextBeat ? colors::activeOffOnBeat : colors::inactiveOffOnBeat;
- } else {
- return isNextBeat ? colors::activeOnBeat : colors::inactiveOnBeat;
- }
- } else {
- // continuing msg from previous beat ?
- BeatIndexMultiset::const_iterator offIndex_it
- = noteInfo.offBeatIndices.greater_or_min(*onIndex_it);
- if(offIndex_it == noteInfo.offBeatIndices.end()
- || beatsInSequence(*onIndex_it, beatIndex, (*offIndex_it - 1) % sequencer.beats.size())) {
- return isNextBeat ? colors::activeContinuingBeat : colors::inactiveContinuingBeat;
- }
- }
- }
- }
-
- bool PlaybackScreen::beatsInSequence(BeatIndex a, BeatIndex b, BeatIndex c)
- {
- if(a <= b) {
- return b <= c || c < a;
- } else {
- return b <= c && c < a;
- }
- }
|