note display performance.
authornick_m <mainsbridge@gmail.com>
Tue, 3 Jan 2017 13:19:31 +0000 (00:19 +1100)
committernick_m <mainsbridge@gmail.com>
Tue, 3 Jan 2017 13:19:31 +0000 (00:19 +1100)
use boost::unordered_map as a note store for ghost & midi region
views.

as per otiginal method, only notes within regoin bounds are stored.

gtk2_ardour/ghostregion.cc
gtk2_ardour/ghostregion.h
gtk2_ardour/midi_region_view.cc
gtk2_ardour/midi_region_view.h

index f48680cef7fedc297556722d0454e3452825f38b..05286715150b86dc221d645f23de9af6648b1a61 100644 (file)
@@ -381,7 +381,7 @@ MidiGhostRegion::update_note (Note* note, bool hide)
                return;
        }
 
-       GhostEvent* ev = find_event (note);
+       GhostEvent* ev = find_event (note->note());
 
        if (!ev) {
                return;
@@ -414,7 +414,7 @@ MidiGhostRegion::update_hit (Hit* hit, bool hide)
                return;
        }
 
-       GhostEvent* ev = find_event (hit);
+       GhostEvent* ev = find_event (hit->note());
 
        if (!ev) {
                return;
@@ -460,7 +460,7 @@ MidiGhostRegion::remove_note (NoteBase* note)
  */
 
 MidiGhostRegion::GhostEvent *
-MidiGhostRegion::find_event (NoteBase* parent)
+MidiGhostRegion::find_event (boost::shared_ptr<NoteType> parent)
 {
        /* we are using _optimization_iterator to speed up the common case where a caller
           is going through our notes in order.
@@ -468,12 +468,12 @@ MidiGhostRegion::find_event (NoteBase* parent)
 
        if (_optimization_iterator != events.end()) {
                ++_optimization_iterator;
-               if (_optimization_iterator != events.end() && _optimization_iterator->second->event == parent) {
+               if (_optimization_iterator != events.end() && _optimization_iterator->first == parent) {
                        return _optimization_iterator->second;
                }
        }
 
-       _optimization_iterator = events.find (parent->note());
+       _optimization_iterator = events.find (parent);
        if (_optimization_iterator != events.end()) {
                return _optimization_iterator->second;
        }
index 61befbb559c52495e5b7e60470e0d05bc31b8ca6..aa4ec8b2f32b8cfd944cdb186a66070da00ef8ab 100644 (file)
@@ -21,6 +21,7 @@
 #define __ardour_gtk_ghost_region_h__
 
 #include <vector>
+#include <boost/unordered_map.hpp>
 #include "pbd/signals.h"
 
 namespace ArdourCanvas {
@@ -122,10 +123,10 @@ private:
        ArdourCanvas::Rectangle* _tmp_rect;
        ArdourCanvas::Polygon* _tmp_poly;
 
-       MidiGhostRegion::GhostEvent* find_event (NoteBase*);
        typedef Evoral::Note<Evoral::Beats> NoteType;
+       MidiGhostRegion::GhostEvent* find_event (boost::shared_ptr<NoteType>);
 
-       typedef std::map<boost::shared_ptr<NoteType>, MidiGhostRegion::GhostEvent* > EventList;
+       typedef boost::unordered_map<boost::shared_ptr<NoteType>, MidiGhostRegion::GhostEvent* > EventList;
        EventList events;
        EventList::iterator _optimization_iterator;
 };
index ec4b0e7dc27170e81d31f7cfda92b22e5245da6e..a7ee621b69a65e2ecda7f1a8399c9ea6c8b07124 100644 (file)
@@ -113,7 +113,6 @@ MidiRegionView::MidiRegionView (ArdourCanvas::Container*      parent,
        , _channel_selection_scoped_note (0)
        , _mouse_state(None)
        , _pressed_button(0)
-       , _sort_needed (true)
        , _optimization_iterator (_events.end())
        , _list_editor (0)
        , _no_sound_notes (false)
@@ -160,7 +159,6 @@ MidiRegionView::MidiRegionView (ArdourCanvas::Container*      parent,
        , _channel_selection_scoped_note (0)
        , _mouse_state(None)
        , _pressed_button(0)
-       , _sort_needed (true)
        , _optimization_iterator (_events.end())
        , _list_editor (0)
        , _no_sound_notes (false)
@@ -214,7 +212,6 @@ MidiRegionView::MidiRegionView (const MidiRegionView& other)
        , _channel_selection_scoped_note (0)
        , _mouse_state(None)
        , _pressed_button(0)
-       , _sort_needed (true)
        , _optimization_iterator (_events.end())
        , _list_editor (0)
        , _no_sound_notes (false)
@@ -246,7 +243,6 @@ MidiRegionView::MidiRegionView (const MidiRegionView& other, boost::shared_ptr<M
        , _channel_selection_scoped_note (0)
        , _mouse_state(None)
        , _pressed_button(0)
-       , _sort_needed (true)
        , _optimization_iterator (_events.end())
        , _list_editor (0)
        , _no_sound_notes (false)
@@ -1105,18 +1101,18 @@ MidiRegionView::abort_command()
 NoteBase*
 MidiRegionView::find_canvas_note (boost::shared_ptr<NoteType> note)
 {
+
        if (_optimization_iterator != _events.end()) {
                ++_optimization_iterator;
        }
 
-       if (_optimization_iterator != _events.end() && (*_optimization_iterator)->note() == note) {
-               return *_optimization_iterator;
+       if (_optimization_iterator != _events.end() && _optimization_iterator->first == note) {
+               return _optimization_iterator->second;
        }
 
-       for (_optimization_iterator = _events.begin(); _optimization_iterator != _events.end(); ++_optimization_iterator) {
-               if ((*_optimization_iterator)->note() == note) {
-                       return *_optimization_iterator;
-               }
+       _optimization_iterator = _events.find (note);
+       if (_optimization_iterator != _events.end()) {
+               return _optimization_iterator->second;
        }
 
        return 0;
@@ -1129,8 +1125,8 @@ MidiRegionView::find_canvas_note (Evoral::event_id_t id)
        Events::iterator it;
 
        for (it = _events.begin(); it != _events.end(); ++it) {
-               if ((*it)->note()->id() == id) {
-                       return *it;
+               if (it->first->id() == id) {
+                       return it->second;
                }
        }
 
@@ -1158,7 +1154,7 @@ MidiRegionView::get_events (Events& e, Evoral::Sequence<Evoral::Beats>::NoteOper
        for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
                NoteBase* cne = find_canvas_note (*n);
                if (cne) {
-                       e.push_back (cne);
+                       e.insert (make_pair (*n, cne));
                }
        }
 }
@@ -1174,8 +1170,8 @@ MidiRegionView::redisplay_model()
                           touching model.  Leave active notes (with length 0) alone since
                           they are being extended. */
                        for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
-                               if ((*i)->note()->length() > 0) {
-                                       update_note(*i);
+                               if (i->second->note()->length() > 0) {
+                                       update_note(i->second);
                                }
                        }
                        _last_display_zoom = zoom;
@@ -1187,51 +1183,28 @@ MidiRegionView::redisplay_model()
                return;
        }
 
+       for (_optimization_iterator = _events.begin(); _optimization_iterator != _events.end(); ++_optimization_iterator) {
+               _optimization_iterator->second->invalidate();
+       }
+
        bool empty_when_starting = _events.empty();
-       MidiModel::ReadLock lock(_model->read_lock());
-       MidiModel::Notes missing_notes = _model->notes(); // copy
+       _optimization_iterator = _events.begin();
+       MidiModel::Notes missing_notes;
        Note* sus = NULL;
        Hit*  hit = NULL;
 
-       if (!empty_when_starting) {
-               MidiModel::Notes::iterator f;
-               for (Events::iterator i = _events.begin(); i != _events.end(); ) {
-                       NoteBase* cne = (*i);
-                       boost::shared_ptr<NoteType> note = cne->note();
-
-                       /* if event item's note exists in the model, we can just update it.
-                        * don't mark it as missing.
-                       */
-                       if ((f = missing_notes.find (note)) != missing_notes.end()) {
-                               if ((*f) == note) {
-                                       cne->validate();
-                                       missing_notes.erase (f);
-                               } else {
-                                       if (cne->selected()) {
-                                               _marked_for_selection.insert (note);
-                                       }
-
-                                       cne->invalidate();
-                               }
-
-                       } else {
-                               cne->invalidate();
-                       }
-                       /* remove note items that are no longer valid */
-                       if (!cne->valid()) {
+       MidiModel::ReadLock lock(_model->read_lock());
+       MidiModel::Notes& notes (_model->notes());
 
-                               for (vector<GhostRegion*>::iterator j = ghosts.begin(); j != ghosts.end(); ++j) {
-                                       MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*j);
-                                       if (gr) {
-                                               gr->remove_note (cne);
-                                       }
-                               }
+       NoteBase* cne;
+       for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
 
-                               delete cne;
-                               i = _events.erase (i);
+               boost::shared_ptr<NoteType> note (*n);
+               bool visible;
 
-                       } else {
-                               bool visible;
+               if (note_in_region_range (note, visible)) {
+                       if (!empty_when_starting && (cne = find_canvas_note (note)) != 0) {
+                               cne->validate ();
                                bool update = false;
 
                                if (note_in_region_range (note, visible)) {
@@ -1269,11 +1242,36 @@ MidiRegionView::redisplay_model()
                                                }
                                        }
                                }
-                               ++i;
+                       } else {
+                               missing_notes.insert (note);
                        }
                }
        }
 
+       if (!empty_when_starting) {
+               MidiModel::Notes::iterator f;
+               for (Events::iterator i = _events.begin(); i != _events.end(); ) {
+
+                       NoteBase* cne = i->second;
+
+                       /* remove note items that are no longer valid */
+                       if (!cne->valid()) {
+
+                               for (vector<GhostRegion*>::iterator j = ghosts.begin(); j != ghosts.end(); ++j) {
+                                       MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*j);
+                                       if (gr) {
+                                               gr->remove_note (cne);
+                                       }
+                               }
+
+                               delete cne;
+                               i = _events.erase (i);
+
+                       } else {
+                               ++i;
+                       }
+               }
+       }
 
        for (MidiModel::Notes::iterator n = missing_notes.begin(); n != missing_notes.end(); ++n) {
                boost::shared_ptr<NoteType> note (*n);
@@ -1306,12 +1304,6 @@ MidiRegionView::redisplay_model()
        _marked_for_velocity.clear ();
        _pending_note_selection.clear ();
 
-       /* we may have caused _events to contain things out of order (e.g. if a note
-          moved earlier or later). we don't generally need them in time order, but
-          make a note that a sort is required for those cases that require it.
-       */
-
-       _sort_needed = true;
 }
 
 void
@@ -1494,12 +1486,12 @@ MidiRegionView::reset_width_dependent_items (double pixel_width)
        bool hide_all = false;
        PatchChanges::iterator x = _patch_changes.begin();
        if (x != _patch_changes.end()) {
-               hide_all = (*x).second->flag()->width() >= _pixel_width;
+               hide_all = x->second->flag()->width() >= _pixel_width;
        }
 
        if (hide_all) {
                for (; x != _patch_changes.end(); ++x) {
-                       (*x).second->hide();
+                       x->second->hide();
                }
        }
 
@@ -1572,11 +1564,11 @@ MidiRegionView::add_ghost (TimeAxisView& tv)
        ghost->set_duration (_region->length() / samples_per_pixel);
 
        for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
-               ghost->add_note(*i);
+               ghost->add_note(i->second);
        }
 
        ghosts.push_back (ghost);
-
+       enable_display (true);
        return ghost;
 }
 
@@ -1873,7 +1865,7 @@ MidiRegionView::add_note(const boost::shared_ptr<NoteType> note, bool visible)
                }
 
                event->on_channel_selection_change (get_selected_channels());
-               _events.push_back(event);
+               _events.insert (make_pair (event->note(), event));
 
                if (visible) {
                        event->show();
@@ -2239,7 +2231,7 @@ MidiRegionView::select_all_notes ()
        clear_editor_note_selection ();
 
        for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
-               add_to_selection (*i);
+               add_to_selection (i->second);
        }
 }
 
@@ -2249,9 +2241,9 @@ MidiRegionView::select_range (framepos_t start, framepos_t end)
        clear_editor_note_selection ();
 
        for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
-               framepos_t t = source_beats_to_absolute_frames((*i)->note()->time());
+               framepos_t t = source_beats_to_absolute_frames(i->first->time());
                if (t >= start && t <= end) {
-                       add_to_selection (*i);
+                       add_to_selection (i->second);
                }
        }
 }
@@ -2260,10 +2252,10 @@ void
 MidiRegionView::invert_selection ()
 {
        for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
-               if ((*i)->selected()) {
-                       remove_from_selection(*i);
+               if (i->second->selected()) {
+                       remove_from_selection(i->second);
                } else {
-                       add_to_selection (*i);
+                       add_to_selection (i->second);
                }
        }
 }
@@ -2423,11 +2415,10 @@ MidiRegionView::note_selected (NoteBase* ev, bool add, bool extend)
 
                        /* find notes entirely within OR spanning the earliest..latest range */
 
-                       if (((*i)->note()->time() >= earliest && (*i)->note()->end_time() <= latest) ||
-                           ((*i)->note()->time() <= earliest && (*i)->note()->end_time() >= latest)) {
-                               add_to_selection (*i);
+                       if ((i->first->time() >= earliest && i->first->end_time() <= latest) ||
+                           (i->first->time() <= earliest && i->first->end_time() >= latest)) {
+                               add_to_selection (i->second);
                        }
-
                }
        }
 }
@@ -2456,14 +2447,14 @@ MidiRegionView::update_drag_selection(framepos_t start, framepos_t end, double g
        // We probably need a tree to be able to find events in O(log(n)) time.
 
        for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
-               if ((*i)->x0() < x1 && (*i)->x1() > x0 && (*i)->y0() < y1 && (*i)->y1() > y0) {
+               if (i->second->x0() < x1 && i->second->x1() > x0 && i->second->y0() < y1 && i->second->y1() > y0) {
                        // Rectangles intersect
-                       if (!(*i)->selected()) {
-                               add_to_selection (*i);
+                       if (!i->second->selected()) {
+                               add_to_selection (i->second);
                        }
-               } else if ((*i)->selected() && !extend) {
+               } else if (i->second->selected() && !extend) {
                        // Rectangles do not intersect
-                       remove_from_selection (*i);
+                       remove_from_selection (i->second);
                }
        }
 
@@ -2498,13 +2489,13 @@ MidiRegionView::update_vertical_drag_selection (double y1, double y2, bool exten
        // We probably need a tree to be able to find events in O(log(n)) time.
 
        for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
-               if (((*i)->y1() >= y1 && (*i)->y1() <= y2)) {
+               if ((i->second->y1() >= y1 && i->second->y1() <= y2)) {
                        // within y- (note-) range
-                       if (!(*i)->selected()) {
-                               add_to_selection (*i);
+                       if (!i->second->selected()) {
+                               add_to_selection (i->second);
                        }
-               } else if ((*i)->selected() && !extend) {
-                       remove_from_selection (*i);
+               } else if (i->second->selected() && !extend) {
+                       remove_from_selection (i->second);
                }
        }
 }
@@ -3494,7 +3485,7 @@ MidiRegionView::midi_channel_mode_changed ()
 
        // Update notes for selection
        for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
-               (*i)->on_channel_selection_change (mask);
+               i->second->on_channel_selection_change (mask);
        }
 
        _patch_changes.clear ();
@@ -3663,53 +3654,47 @@ struct EventNoteTimeEarlyFirstComparator {
        }
 };
 
-void
-MidiRegionView::time_sort_events ()
-{
-       if (!_sort_needed) {
-               return;
-       }
-
-       EventNoteTimeEarlyFirstComparator cmp;
-       _events.sort (cmp);
-
-       _sort_needed = false;
-}
-
 void
 MidiRegionView::goto_next_note (bool add_to_selection)
 {
        bool use_next = false;
 
-       if (_events.back()->selected()) {
-               return;
-       }
-
-       time_sort_events ();
-
        MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
        uint16_t const channel_mask = mtv->midi_track()->get_playback_channel_mask();
+       NoteBase* first_note = 0;
 
-       for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
-               if ((*i)->selected()) {
-                       use_next = true;
-                       continue;
-               } else if (use_next) {
-                       if (channel_mask & (1 << (*i)->note()->channel())) {
-                               if (!add_to_selection) {
-                                       unique_select (*i);
-                               } else {
-                                       note_selected (*i, true, false);
+       MidiModel::ReadLock lock(_model->read_lock());
+       MidiModel::Notes& notes (_model->notes());
+
+       for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
+               NoteBase* cne = 0;
+               if ((cne = find_canvas_note (*n))) {
+
+                       if (!first_note && (channel_mask & (1 << (*n)->channel()))) {
+                               first_note = cne;
+                       }
+
+                       if (cne->selected()) {
+                               use_next = true;
+                               continue;
+                       } else if (use_next) {
+                               if (channel_mask & (1 << (*n)->channel())) {
+                                       if (!add_to_selection) {
+                                               unique_select (cne);
+                                       } else {
+                                               note_selected (cne, true, false);
+                                       }
+
+                                       return;
                                }
-                               return;
                        }
                }
        }
 
        /* use the first one */
 
-       if (!_events.empty() && (channel_mask & (1 << _events.front()->note()->channel ()))) {
-               unique_select (_events.front());
+       if (!_events.empty() && first_note) {
+               unique_select (first_note);
        }
 }
 
@@ -3718,35 +3703,43 @@ MidiRegionView::goto_previous_note (bool add_to_selection)
 {
        bool use_next = false;
 
-       if (_events.front()->selected()) {
-               return;
-       }
-
-       time_sort_events ();
-
        MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
        uint16_t const channel_mask = mtv->midi_track()->get_playback_channel_mask ();
+       NoteBase* last_note = 0;
 
-       for (Events::reverse_iterator i = _events.rbegin(); i != _events.rend(); ++i) {
-               if ((*i)->selected()) {
-                       use_next = true;
-                       continue;
-               } else if (use_next) {
-                       if (channel_mask & (1 << (*i)->note()->channel())) {
-                               if (!add_to_selection) {
-                                       unique_select (*i);
-                               } else {
-                                       note_selected (*i, true, false);
+       MidiModel::ReadLock lock(_model->read_lock());
+       MidiModel::Notes& notes (_model->notes());
+
+       for (MidiModel::Notes::reverse_iterator n = notes.rbegin(); n != notes.rend(); ++n) {
+               NoteBase* cne = 0;
+               if ((cne = find_canvas_note (*n))) {
+
+                       if (!last_note && (channel_mask & (1 << (*n)->channel()))) {
+                               last_note = cne;
+                       }
+
+                       if (cne->selected()) {
+                               use_next = true;
+                               continue;
+
+                       } else if (use_next) {
+                               if (channel_mask & (1 << (*n)->channel())) {
+                                       if (!add_to_selection) {
+                                               unique_select (cne);
+                                       } else {
+                                               note_selected (cne, true, false);
+                                       }
+
+                                       return;
                                }
-                               return;
                        }
                }
        }
 
        /* use the last one */
 
-       if (!_events.empty() && (channel_mask & (1 << (*_events.rbegin())->note()->channel ()))) {
-               unique_select (*(_events.rbegin()));
+       if (!_events.empty() && last_note) {
+               unique_select (last_note);
        }
 }
 
@@ -3755,18 +3748,18 @@ MidiRegionView::selection_as_notelist (Notes& selected, bool allow_all_if_none_s
 {
        bool had_selected = false;
 
-       time_sort_events ();
+       /* we previously time sorted events here, but Notes is a multiset sorted by time */
 
        for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
-               if ((*i)->selected()) {
-                       selected.insert ((*i)->note());
+               if (i->second->selected()) {
+                       selected.insert (i->first);
                        had_selected = true;
                }
        }
 
        if (allow_all_if_none_selected && !had_selected) {
                for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
-                       selected.insert ((*i)->note());
+                       selected.insert (i->first);
                }
        }
 }
@@ -3899,8 +3892,8 @@ MidiRegionView::maybe_select_by_position (GdkEventButton* ev, double /*x*/, doub
        }
 
        for (Events::iterator i = e.begin(); i != e.end(); ++i) {
-               if (_selection.insert (*i).second) {
-                       (*i)->set_selected (true);
+               if (_selection.insert (i->second).second) {
+                       i->second->set_selected (true);
                }
        }
 
@@ -3919,7 +3912,7 @@ MidiRegionView::color_handler ()
        _patch_change_fill = UIConfiguration::instance().color_mod ("midi patch change fill", "midi patch change fill");
 
        for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
-               (*i)->set_selected ((*i)->selected()); // will change color
+               i->second->set_selected (i->second->selected()); // will change color
        }
 
        /* XXX probably more to do here */
index a608ad810c0a91ef60d8e2e7edc164fedd78f933..e1af49abceb7421d67048fa29da1a792ef7433fc 100644 (file)
@@ -409,9 +409,9 @@ private:
        uint8_t  _current_range_min;
        uint8_t  _current_range_max;
 
-       typedef std::list<NoteBase*>                          Events;
-       typedef std::map<ARDOUR::MidiModel::PatchChangePtr, boost::shared_ptr<PatchChange> > PatchChanges;
-       typedef std::vector< boost::shared_ptr<SysEx> >       SysExes;
+       typedef boost::unordered_map<boost::shared_ptr<NoteType>, NoteBase*>                 Events;
+       typedef boost::unordered_map<ARDOUR::MidiModel::PatchChangePtr, boost::shared_ptr<PatchChange> > PatchChanges;
+       typedef std::vector< boost::shared_ptr<SysEx> >                                      SysExes;
 
        ARDOUR::BeatsFramesConverter _region_relative_time_converter;
        ARDOUR::BeatsFramesConverter _source_relative_time_converter;
@@ -438,9 +438,6 @@ private:
        /** Currently selected NoteBase objects */
        Selection _selection;
 
-       bool _sort_needed;
-       void time_sort_events ();
-
        MidiCutBuffer* selection_as_cut_buffer () const;
 
        /** New notes (created in the current command) which should be selected