Browse Source

note off message support

Fabian Peter Hammerle 10 năm trước cách đây
mục cha
commit
1ab970d4e4
7 tập tin đã thay đổi với 262 bổ sung27 xóa
  1. 8 3
      BeatSequence.cpp
  2. 4 3
      BeatSequence.h
  3. 132 18
      PlaybackScreen.cpp
  4. 5 0
      PlaybackScreen.h
  5. 8 2
      colors.h
  6. 1 1
      midi
  7. 104 0
      multiset.h

+ 8 - 3
BeatSequence.cpp

@@ -107,6 +107,11 @@ const BeatSequence::BeatIndexMultiset& BeatSequence::getNoteOffBeatIndices(Chann
     return getNoteInfo(channel, pitch).offBeatIndices;
 }
 
+const BeatSequenceNoteInformation& BeatSequence::getNoteInfo(Channel channel, Pitch pitch)
+{
+    return getMutableNoteInfo(channel, pitch);
+}
+
 void BeatSequence::printRegistry(std::ostream& stream) const
 {
     stream << "beat sequence note info registry: \n";
@@ -119,7 +124,7 @@ void BeatSequence::printRegistry(std::ostream& stream) const
     stream.flush();
 }
 
-BeatSequenceNoteInformation& BeatSequence::getNoteInfo(Channel channel, Pitch pitch)
+BeatSequenceNoteInformation& BeatSequence::getMutableNoteInfo(Channel channel, Pitch pitch)
 {
     ChannelPitchPair key(channel, pitch);
     return noteInfos[key];
@@ -142,7 +147,7 @@ void BeatSequence::registerMessage(BeatIndex beatIndex, const std::shared_ptr<mi
     std::shared_ptr<midi::NoteMessage> noteMsg_ptr
         = std::dynamic_pointer_cast<midi::NoteMessage>(msg_ptr);
     if(noteMsg_ptr) {
-        getNoteInfo(noteMsg_ptr->channel, noteMsg_ptr->pitch)
+        getMutableNoteInfo(noteMsg_ptr->channel, noteMsg_ptr->pitch)
             .registerMessage(beatIndex, noteMsg_ptr);
     }
 }
@@ -152,7 +157,7 @@ void BeatSequence::unregisterMessage(BeatIndex beatIndex, const std::shared_ptr<
     std::shared_ptr<midi::NoteMessage> noteMsg_ptr
         = std::dynamic_pointer_cast<midi::NoteMessage>(msg_ptr);
     if(noteMsg_ptr) {
-        getNoteInfo(noteMsg_ptr->channel, noteMsg_ptr->pitch)
+        getMutableNoteInfo(noteMsg_ptr->channel, noteMsg_ptr->pitch)
             .unregisterMessage(beatIndex, noteMsg_ptr);
     }
 }

+ 4 - 3
BeatSequence.h

@@ -1,12 +1,12 @@
 #include "midi/BeatSequence.h"
-#include <set>
+#include "multiset.h"
 #include <map>
 #include <ostream>
 
 class BeatSequenceNoteInformation
 {
 public:
-    typedef std::multiset<midi::BeatSequence::BeatIndex> BeatIndexMultiset;
+    typedef multiset<midi::BeatSequence::BeatIndex> BeatIndexMultiset;
     
     BeatIndexMultiset onBeatIndices, offBeatIndices;
 
@@ -39,10 +39,11 @@ public:
     void reduceErasingConflicts(BeatIndex factor);
     const BeatIndexMultiset& getNoteOnBeatIndices(Channel channel, Pitch pitch);
     const BeatIndexMultiset& getNoteOffBeatIndices(Channel channel, Pitch pitch);
+    const BeatSequenceNoteInformation& getNoteInfo(Channel channel, Pitch pitch);
     void printRegistry(std::ostream& stream) const;
 
 private:
-    BeatSequenceNoteInformation& getNoteInfo(Channel channel, Pitch pitch);
+    BeatSequenceNoteInformation& getMutableNoteInfo(Channel channel, Pitch pitch);
     void refreshRegistry();
     void registerMessage(BeatIndex beatIndex, const std::shared_ptr<midi::Message>& msg_ptr);
     void unregisterMessage(BeatIndex beatIndex, const std::shared_ptr<midi::Message>& msg_ptr);

+ 132 - 18
PlaybackScreen.cpp

@@ -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;
+    }
+}

+ 5 - 0
PlaybackScreen.h

@@ -7,6 +7,9 @@ class PlaybackScreen : public Screen
 {
     typedef Screen parent;
 
+    typedef BeatSequence::BeatIndex BeatIndex;
+    typedef BeatSequenceNoteInformation::BeatIndexMultiset BeatIndexMultiset;
+
     Player::BeatIndex beatDisplayOffset;
 
 public:
@@ -30,5 +33,7 @@ protected:
 private:
 
     void toggleOnMessage(std::shared_ptr<midi::NoteOnMessage> noteOnMessage_ptr, Player::BeatIndex beatIndex);
+    midi::LaunchpadColor getBeatColor(std::shared_ptr<midi::NoteOnMessage> noteOnMessage_ptr, BeatIndex beatIndex);
+    static bool beatsInSequence(BeatIndex a, BeatIndex b, BeatIndex c);
 };
 	

+ 8 - 2
colors.h

@@ -7,11 +7,17 @@ namespace colors
 typedef const midi::LaunchpadColor Color;
 
 Color dark(0, 0);
-Color inactiveMessage(1, 2);
-Color activeMessage(3, 2);
+
 Color activeOption(3, 3);
 Color inactiveOption(1, 2);
 
+Color inactiveMessage(1, 2);
+Color activeMessage(3, 2);
+Color inactiveOnBeat(2, 0);
+Color activeOnBeat(3, 1);
+Color inactiveContinuingBeat(1, 1);
+Color activeContinuingBeat(2, 2);
+
 Color inactiveBeatsCount(1, 0);
 Color activeBeatsCount(3, 1);
 Color sequenceExpansionButton(0, 1);

+ 1 - 1
midi

@@ -1 +1 @@
-Subproject commit defc63e484af3a00b0cee9087280b3f455fc92e1
+Subproject commit 2acee77f4d46e075e3d56f0482dcd329d75b4be4

+ 104 - 0
multiset.h

@@ -0,0 +1,104 @@
+#include <set>
+
+template<
+    class Key,
+    class Compare = std::less<Key>,
+    class Allocator = std::allocator<Key>
+> class multiset : public std::multiset<Key, Compare, Allocator>
+{
+    typedef multiset<Key, Compare, Allocator> self;
+    typedef std::multiset<Key, Compare, Allocator> parent;
+
+public:
+
+    typedef typename parent::iterator iterator;
+    typedef typename parent::const_iterator const_iterator;
+
+    // returns iterator poiting to first element equal to key.
+    const_iterator equal( const Key& key ) const
+    {
+        const_iterator eq = self::lower_bound(key);
+        if(eq == self::end() || *eq != key) {
+            return self::end();
+        } else {
+            return eq;
+        }
+    }
+
+    // returns iterator poiting to first element equal to key.
+    // if no such element exists, return iterator pointing to last element less than key.
+    const_iterator equal_or_less( const Key& key ) const
+    {
+        if(self::size() == 0) {
+            return self::end();
+        } else {
+            const_iterator equal_or_greater = self::lower_bound(key);
+            if(equal_or_greater == self::end()) {
+                // no element is equal or greater, so take the last
+                return --self::end();        
+            } else if(*equal_or_greater == key) {
+                return equal_or_greater;
+            } else if(equal_or_greater == self::begin()) {
+                return self::end();
+            } else {
+                return --equal_or_greater;
+            }
+        }
+    }
+
+    const_iterator equal_or_less_or_max( const Key& key ) const
+    {
+        const_iterator eq_or_less = self::equal_or_less(key);
+        if(eq_or_less == self::end()) {
+            return self::max();
+        } else {
+            return eq_or_less;
+        }
+    }
+
+    const_iterator greater_or_min( const Key& key ) const
+    {
+        const_iterator greater = self::upper_bound(key);
+        if(greater == self::end()) {
+            return min();
+        } else {
+            return greater;
+        }
+    }
+
+    // returns an iterator pointing to the last element that is less than key.
+    const_iterator less( const Key& key ) const
+    {
+        const_iterator lower = self::lower_bound(key);
+        if(lower == self::begin()) {
+            return self::end();
+        } else {
+            lower--;
+            return lower;
+        }
+    }
+
+    const_iterator less_or_max( const Key& key ) const
+    {
+        const_iterator less = self::less(key);
+        if(less == self::end()) {
+            return self::max();
+        } else {
+            return less;
+        }
+    }
+
+    const_iterator max() const
+    {
+        const_iterator max = self::end();
+        if(self::size() > 0) {
+            max--;
+        } 
+        return max;
+    }
+
+    const_iterator min() const
+    {
+        return self::begin();
+    }
+};