Maintain correct tracker state on MIDI overwrite.
authorDavid Robillard <drobilla@leibniz.local>
Fri, 19 Dec 2014 23:09:36 +0000 (18:09 -0500)
committerDavid Robillard <drobilla@leibniz.local>
Sat, 20 Dec 2014 06:13:25 +0000 (01:13 -0500)
This is a little hard-edged in that edits while rolling will prematurely chop
off any playing notes, but at least the state of things actually reflects
reality.  More sophisticated solution hopefully to come...

libs/ardour/ardour/midi_playlist.h
libs/ardour/ardour/midi_ring_buffer.h
libs/ardour/midi_diskstream.cc
libs/ardour/midi_playlist.cc
libs/ardour/midi_region.cc
libs/ardour/midi_ring_buffer.cc
libs/ardour/smf_source.cc

index 2603de45f7e17662eb228d7f05bcce59406f5bf1..5e334d5546517a9385c7cf78c3d6141c3bd70e91 100644 (file)
@@ -63,7 +63,15 @@ public:
 
        std::set<Evoral::Parameter> contained_automation();
 
-       void clear_note_trackers ();
+       /** Clear all note trackers. */
+       void reset_note_trackers ();
+
+       /** Resolve all pending notes and clear all note trackers.
+        *
+        * @param dst Sink to write note offs to.
+        * @param time Time stamp of all written note offs.
+        */
+       void resolve_note_trackers (Evoral::EventSink<framepos_t>& dst, framepos_t time);
 
 protected:
 
index 15e4d2689bafe9134a760779090e9121fecf066c..0d27de3c16caeae5872bc054528446f5ab099226 100644 (file)
@@ -54,7 +54,7 @@ public:
        void flush (framepos_t start, framepos_t end);
 
        void reset_tracker ();
-       void loop_resolve (MidiBuffer& dst, framepos_t);
+       void resolve_tracker (MidiBuffer& dst, framepos_t);
 
 private:
        MidiStateTracker _tracker;
index e2fd6d168144fb09809124e359449a98cd6c3c66..e52a7c3ad7a9ffa6eb857153d07c7ab8839e568a 100644 (file)
@@ -606,12 +606,20 @@ MidiDiskstream::set_pending_overwrite (bool yn)
 int
 MidiDiskstream::overwrite_existing_buffers ()
 {
-       /* This is safe as long as the butler thread is suspended, which it should be */
+       /* Clear the playback buffer contents.  This is safe as long as the butler
+          thread is suspended, which it should be. */
        _playback_buf->reset ();
+       _playback_buf->reset_tracker ();
 
        g_atomic_int_set (&_frames_read_from_ringbuffer, 0);
        g_atomic_int_set (&_frames_written_to_ringbuffer, 0);
 
+       /* Resolve all currently active notes in the playlist.  This is more
+          aggressive than it needs to be: ideally we would only resolve what is
+          absolutely necessary, but this seems difficult and/or impossible without
+          having the old data or knowing what change caused the overwrite. */
+       midi_playlist()->resolve_note_trackers (*_playback_buf, overwrite_frame);
+
        read (overwrite_frame, disk_io_chunk_frames, false);
        file_frame = overwrite_frame; // it was adjusted by ::read()
        overwrite_queued = false;
@@ -1398,7 +1406,7 @@ MidiDiskstream::get_playback (MidiBuffer& dst, framecnt_t nframes)
                           beyond the loop end.
                        */
 
-                       _playback_buf->loop_resolve (dst, 0);
+                       _playback_buf->resolve_tracker (dst, 0);
                }
 
                if (loc->end() >= effective_start && loc->end() < effective_start + nframes) {
@@ -1489,7 +1497,7 @@ MidiDiskstream::reset_tracker ()
        boost::shared_ptr<MidiPlaylist> mp (midi_playlist());
 
        if (mp) {
-               mp->clear_note_trackers ();
+               mp->reset_note_trackers ();
        }
 }
 
index 36b6fce75fa60d3cda434d0655e99b694c82d4df..63c3b4985884134b664354a1cdf935d398912fe4 100644 (file)
@@ -288,14 +288,27 @@ MidiPlaylist::read (Evoral::EventSink<framepos_t>& dst, framepos_t start, framec
 }
 
 void
-MidiPlaylist::clear_note_trackers ()
+MidiPlaylist::reset_note_trackers ()
 {
        Playlist::RegionWriteLock rl (this, false);
 
        for (NoteTrackers::iterator n = _note_trackers.begin(); n != _note_trackers.end(); ++n) {
                delete n->second;
        }
-       DEBUG_TRACE (DEBUG::MidiTrackers, string_compose ("%1 clears all note trackers\n", name()));
+       DEBUG_TRACE (DEBUG::MidiTrackers, string_compose ("%1 reset all note trackers\n", name()));
+       _note_trackers.clear ();
+}
+
+void
+MidiPlaylist::resolve_note_trackers (Evoral::EventSink<framepos_t>& dst, framepos_t time)
+{
+       Playlist::RegionWriteLock rl (this, false);
+
+       for (NoteTrackers::iterator n = _note_trackers.begin(); n != _note_trackers.end(); ++n) {
+               n->second->resolve_notes(dst, time);
+               delete n->second;
+       }
+       DEBUG_TRACE (DEBUG::MidiTrackers, string_compose ("%1 resolve all note trackers\n", name()));
        _note_trackers.clear ();
 }
 
index f79e5ef2034b122b253f9a863f89c52007216fd9..e94e4e485430e304019114b2751439d429fa6dc7 100644 (file)
@@ -414,6 +414,12 @@ MidiRegion::model_changed ()
 void
 MidiRegion::model_contents_changed ()
 {
+       {
+               /* Invalidate source iterator to force reading new contents even if the
+                  calls to read progress linearly. */
+               Glib::Threads::Mutex::Lock lm (midi_source(0)->mutex());
+               midi_source(0)->invalidate (lm);
+       }
        send_change (PropertyChange (Properties::midi_data));
 }
 
index 0da3ba68354065eaf5c5cb27197a14b8ef5694d8..9fbd9dfdff78183d683c495160a3289261954857 100644 (file)
@@ -241,7 +241,7 @@ MidiRingBuffer<T>::reset_tracker ()
 
 template<typename T>
 void
-MidiRingBuffer<T>::loop_resolve (MidiBuffer& dst, framepos_t t)
+MidiRingBuffer<T>::resolve_tracker (MidiBuffer& dst, framepos_t t)
 {
        _tracker.resolve_notes (dst, t);
 }
index 519d8bbf1089ab172e25c81298ef27dd29eb5ff1..d1a82eb685cd5ed794f7af34fc92842b9b4be466 100644 (file)
@@ -282,13 +282,8 @@ SMFSource::read_unlocked (const Lock&                    lock,
 
                if (ev_frame_time < start + duration) {
                        destination.write (ev_frame_time, ev_type, ev_size, ev_buffer);
-
                        if (tracker) {
-                               if (ev_buffer[0] & MIDI_CMD_NOTE_ON) {
-                                       tracker->add (ev_buffer[1], ev_buffer[0] & 0xf);
-                               } else if (ev_buffer[0] & MIDI_CMD_NOTE_OFF) {
-                                       tracker->remove (ev_buffer[1], ev_buffer[0] & 0xf);
-                               }
+                               tracker->track(ev_buffer);
                        }
                } else {
                        break;