track notes at the region level in MidiPlaylist; resolve them (deliver note offs...
[ardour.git] / libs / ardour / midi_state_tracker.cc
index c2aa19e5c8bf8c60ddd4ede546c90b7f384902dd..e3c66e4df85010e90a731d66b9951058f15a9441 100644 (file)
@@ -1,25 +1,26 @@
 /*
-    Copyright (C) 2006-2008 Paul Davis 
+    Copyright (C) 2006-2008 Paul Davis
     Author: Torben Hohn
-    
+
     This program is free software; you can redistribute it and/or modify it
     under the terms of the GNU General Public License as published by the Free
     Software Foundation; either version 2 of the License, or (at your option)
     any later version.
-    
+
     This program is distributed in the hope that it will be useful, but WITHOUT
     ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
     FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
     for more details.
-    
+
     You should have received a copy of the GNU General Public License along
     with this program; if not, write to the Free Software Foundation, Inc.,
     675 Mass Ave, Cambridge, MA 02139, USA.
 */
 
 #include <iostream>
-#include <ardour/event_type_map.h>
-#include <ardour/midi_state_tracker.h>
+#include "ardour/event_type_map.h"
+#include "ardour/midi_ring_buffer.h"
+#include "ardour/midi_state_tracker.h"
 
 using namespace std;
 using namespace ARDOUR;
@@ -27,53 +28,103 @@ using namespace ARDOUR;
 
 MidiStateTracker::MidiStateTracker ()
 {
-       _active_notes.reset();
+       reset ();
+}
+
+void
+MidiStateTracker::reset ()
+{
+       memset (_active_notes, 0, sizeof (_active_notes));
 }
 
 void
-MidiStateTracker::track_note_onoffs (Evoral::MIDIEvent &event)
+MidiStateTracker::track_note_onoffs (const Evoral::MIDIEvent<MidiBuffer::TimeType>& event)
 {
        if (event.is_note_on()) {
-               _active_notes [event.note() + 128 * event.channel()] = true;
+               _active_notes [event.note() + 128 * event.channel()]++;
        } else if (event.is_note_off()){
-               _active_notes [event.note() + 128 * event.channel()] = false;
+               if (_active_notes[event.note() + 128 * event.channel()]) {
+                       _active_notes [event.note() + 128 * event.channel()]--;
+               }
+       }
+}
+void
+MidiStateTracker::add (uint8_t note, uint8_t chn)
+{
+       cerr << "Added note " << note << " chan " << chn << endl;
+       _active_notes[note + 128 * chn]++;
+}
+
+void
+MidiStateTracker::remove (uint8_t note, uint8_t chn)
+{
+       if (_active_notes[note + 128 * chn]) {
+               cerr << "Removed note " << note << " chan " << chn << endl;
+               _active_notes[note + 128 * chn]--;
        }
 }
 
-bool
-MidiStateTracker::track (const MidiBuffer::iterator &from, const MidiBuffer::iterator &to)
+void
+MidiStateTracker::track (const MidiBuffer::iterator &from, const MidiBuffer::iterator &to, bool& looped)
 {
-       bool ret = false;
+       looped = false;
 
        for (MidiBuffer::iterator i = from; i != to; ++i) {
-               if ((*i).event_type() == LoopEventType) {
-                       ret = true;
+               const Evoral::MIDIEvent<MidiBuffer::TimeType> ev(*i, false);
+               if (ev.event_type() == LoopEventType) {
+                       looped = true;
                        continue;
                }
 
-               track_note_onoffs (*i);
+               track_note_onoffs (ev);
        }
-       return ret;
 }
 
 void
-MidiStateTracker::resolve_notes (MidiBuffer &dst, nframes_t time)
+MidiStateTracker::resolve_notes (MidiBuffer &dst, nframes64_t time)
 {
-       // Dunno if this is actually faster but at least it fills our cacheline.
-       if (_active_notes.none ())
-               return;
-
        for (int channel = 0; channel < 16; ++channel) {
                for (int note = 0; note < 128; ++note) {
-                       if (_active_notes[channel * 128 + note]) {
+                       while (_active_notes[channel * 128 + note]) {
                                uint8_t buffer[3] = { MIDI_CMD_NOTE_OFF | channel, note, 0 };
-                               Evoral::MIDIEvent noteoff (time, MIDI_CMD_NOTE_OFF, 3, buffer, false);
+                               Evoral::MIDIEvent<MidiBuffer::TimeType> noteoff
+                                       (time, MIDI_CMD_NOTE_OFF, 3, buffer, false);
+                               dst.push_back (noteoff);
 
-                               dst.push_back (noteoff);        
+                               _active_notes[channel * 128 + note]--;
+                       }
+               }
+       }
+}
 
-                               _active_notes [channel * 128 + note] = false;
+void
+MidiStateTracker::resolve_notes (MidiRingBuffer<nframes_t> &dst, nframes64_t time)
+{
+       uint8_t buf[3];
+       for (int channel = 0; channel < 16; ++channel) {
+               for (int note = 0; note < 128; ++note) {
+                       while (_active_notes[channel * 128 + note]) {
+                               buf[0] = MIDI_CMD_NOTE_OFF|channel;
+                               buf[1] = note;
+                               buf[2] = 0;
+                               dst.write (time, EventTypeMap::instance().midi_event_type (buf[0]), 3, buf);
+                               _active_notes[channel * 128 + note]--;
                        }
                }
        }
 }
 
+void
+MidiStateTracker::dump (ostream& o)
+{
+       o << "******\n";
+       for (int c = 0; c < 16; ++c) {
+               for (int x = 0; x < 128; ++x) {
+                       if (_active_notes[c * 128 + x]) {
+                               o << "Channel " << c+1 << " Note " << x << " is on ("
+                                 << (int) _active_notes[c*128+x] <<  "times)\n";
+                       }
+               }
+       }
+       o << "+++++\n";
+}