|
@@ -2,6 +2,7 @@
|
|
|
#include "PlaybackScreen.h"
|
|
|
#include "colors.h"
|
|
|
#include <iostream>
|
|
|
+#include <cassert>
|
|
|
|
|
|
PlaybackScreen::PlaybackScreen(Sequencer& seq)
|
|
|
: parent(seq), beatDisplayOffset(0)
|
|
@@ -126,16 +127,10 @@ void PlaybackScreen::refresh(unsigned char x, unsigned char y)
|
|
|
} else {
|
|
|
Player::BeatIndex beatIndex = x + beatDisplayOffset;
|
|
|
if(beatIndex < sequencer.beats.size() && y < sequencer.messages.size()) {
|
|
|
- const midi::MessageList& beat = sequencer.beats[beatIndex];
|
|
|
- std::shared_ptr<midi::Message> msg_ptr = sequencer.messages[y];
|
|
|
- if(beat.contains(*msg_ptr)) {
|
|
|
- if(sequencer.player.getNextBeat() == beatIndex) {
|
|
|
- setColor(x, y, colors::activeMessage);
|
|
|
- } else {
|
|
|
- setColor(x, y, colors::inactiveMessage);
|
|
|
- }
|
|
|
- } else {
|
|
|
- setColor(x, y, colors::dark);
|
|
|
+ std::shared_ptr<midi::NoteOnMessage> noteOnMessage_ptr
|
|
|
+ = std::dynamic_pointer_cast<midi::NoteOnMessage>(sequencer.messages[y]);
|
|
|
+ if(noteOnMessage_ptr) {
|
|
|
+ setColor(x, y, getBeatColor(noteOnMessage_ptr, beatIndex));
|
|
|
}
|
|
|
} else {
|
|
|
setColor(x, y, colors::dark);
|
|
@@ -143,18 +138,137 @@ void PlaybackScreen::refresh(unsigned char x, unsigned char y)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-void PlaybackScreen::toggleOnMessage(std::shared_ptr<midi::NoteOnMessage> noteOnMessage_ptr, Player::BeatIndex beatIndex)
|
|
|
+void PlaybackScreen::toggleOnMessage(std::shared_ptr<midi::NoteOnMessage> onMsg_ptr, Player::BeatIndex beatIndex)
|
|
|
{
|
|
|
- noteOnMessage_ptr->print(std::cout);
|
|
|
+ onMsg_ptr->print(std::cout);
|
|
|
+
|
|
|
+ midi::NoteOffMessage offMsg = onMsg_ptr->toOffMessage(0);
|
|
|
+
|
|
|
+ const BeatSequenceNoteInformation& noteInfo
|
|
|
+ = sequencer.beats.getNoteInfo(onMsg_ptr->channel, onMsg_ptr->pitch);
|
|
|
+ // preceding on msg
|
|
|
+ BeatIndexMultiset::const_iterator precedingOnBeatIndex_it
|
|
|
+ = noteInfo.onBeatIndices.less_or_max(beatIndex);
|
|
|
|
|
|
const midi::MessageList& beat = sequencer.beats[beatIndex];
|
|
|
- midi::MessageList::const_iterator beat_msg_it = beat.find(*noteOnMessage_ptr);
|
|
|
- if(beat_msg_it == beat.end()) {
|
|
|
- // insert message at beat
|
|
|
- sequencer.beats.pushBackMessage(beatIndex, std::dynamic_pointer_cast<midi::Message>(noteOnMessage_ptr));
|
|
|
+ midi::MessageList::const_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::const_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(" << *precedingOnBeatIndex_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(*precedingOnBeatIndex_it, beatIndex + 1, *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.
|
|
|
+ const midi::MessageList& precedingOnFollowingOffBeat
|
|
|
+ = sequencer.beats[*precedingOnFollowingOffMsgBeatIndex_it];
|
|
|
+ const midi::MessageList::const_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 message from beat
|
|
|
- sequencer.beats.eraseMessage(beatIndex, beat_msg_it);
|
|
|
+ // 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.
|
|
|
+ const midi::MessageList& followingOffBeat = sequencer.beats[followingOffBeatIndex];
|
|
|
+ midi::MessageList::const_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 ?
|
|
|
+ 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
|
|
|
+ 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;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ const midi::MessageList& beat = sequencer.beats[beatIndex];
|
|
|
+ if(beat.contains(*onMsg_ptr)) {
|
|
|
+ if(sequencer.player.getNextBeat() == beatIndex) {
|
|
|
+ return colors::activeMessage;
|
|
|
+ } else {
|
|
|
+ return colors::inactiveMessage;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ return colors::dark;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+bool PlaybackScreen::beatsInSequence(BeatIndex a, BeatIndex b, BeatIndex c)
|
|
|
+{
|
|
|
+ if(a <= b) {
|
|
|
+ return b <= c || c < a;
|
|
|
+ } else {
|
|
|
+ return b <= c && c < a;
|
|
|
+ }
|
|
|
+}
|