Eliminate a ton of unnecessary complete redrawing in MIDI stream views:
authorDavid Robillard <d@drobilla.net>
Tue, 23 Sep 2008 02:40:29 +0000 (02:40 +0000)
committerDavid Robillard <d@drobilla.net>
Tue, 23 Sep 2008 02:40:29 +0000 (02:40 +0000)
Only resize vertically (don't reaload model) on range changes.
Keep track of range in model while loading (writing to model), rather than double display MIDI regions to find out.
Don't go crazy and chew CPU blinking around and doing nothing on initial show of MIDI track context menu.
Change radio 'full range' and 'contents range' menu items to non-radio actions that just set the range appropriately.
Fix crashes on some esoteric case of control data I can't figure out, but fixed anyway, so I guess it all worked out well in the end for everybody.

git-svn-id: svn://localhost/ardour2/branches/3.0@3794 d708f5d6-7413-0410-9779-e7cbd77b26cf

gtk2_ardour/midi_region_view.cc
gtk2_ardour/midi_region_view.h
gtk2_ardour/midi_streamview.cc
gtk2_ardour/midi_streamview.h
gtk2_ardour/midi_time_axis.cc
gtk2_ardour/midi_time_axis.h
libs/evoral/evoral/Sequence.hpp
libs/evoral/src/ControlList.cpp
libs/evoral/src/Sequence.cpp

index c67d0a5c5e219bc30ae8ca143a75ba4800a56b27..90fcce84eaefb3a16eef937f98fd0b17435f911f 100644 (file)
@@ -65,6 +65,8 @@ MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &
        , _force_channel(-1)
        , _last_channel_selection(0xFFFF)
        , _default_note_length(0.0)
+       , _current_range_min(0)
+       , _current_range_max(0)
        , _active_notes(0)
        , _note_group(new ArdourCanvas::Group(*parent))
        , _delta_command(NULL)
@@ -399,7 +401,7 @@ MidiRegionView::create_note_at(double x, double y, double length)
 
        const boost::shared_ptr<Evoral::Note> new_note(new Evoral::Note(
                        0, new_note_time, new_note_length, (uint8_t)note, 0x40));
-       view->update_bounds(new_note->note());
+       view->update_note_range(new_note->note());
 
        MidiModel::DeltaCommand* cmd = _model->new_delta_command("add note");
        cmd->add(new_note);
@@ -419,7 +421,7 @@ MidiRegionView::clear_events()
                }
        }
 
-       for (std::vector<CanvasNoteEvent*>::iterator i = _events.begin(); i != _events.end(); ++i)
+       for (Events::iterator i = _events.begin(); i != _events.end(); ++i)
                delete *i;
 
        _events.clear();
@@ -613,25 +615,41 @@ MidiRegionView::reset_width_dependent_items (double pixel_width)
 void
 MidiRegionView::set_height (gdouble height)
 {
+       static const double FUDGE = 2;
+       const double old_height = _height;
        RegionView::set_height(height);
+       _height = height - FUDGE;
        
-       // FIXME: ick
-       height -= 2;
+       apply_note_range(midi_stream_view()->lowest_note(),
+                        midi_stream_view()->highest_note(),
+                        height != old_height + FUDGE);
        
-       _height = height;
-       
-       if (_enable_display) {
+       if (name_text) {
+               name_text->raise_to_top();
+       }
+}
 
-               _model->read_lock();
 
-               for (std::vector<CanvasNoteEvent*>::const_iterator i = _events.begin(); i != _events.end(); ++i) {
+/** Apply the current note range from the stream view
+ * by repositioning/hiding notes as necessary
+ */
+void
+MidiRegionView::apply_note_range (uint8_t min, uint8_t max, bool force)
+{
+       if (_enable_display) {
+               if (!force && _current_range_min == min && _current_range_max == max) {
+                       return;
+               }
+               
+               _current_range_min = min;
+               _current_range_max = max;
+
+               for (Events::const_iterator i = _events.begin(); i != _events.end(); ++i) {
                        CanvasNoteEvent* event = *i;
                        Item* item = dynamic_cast<Item*>(event);
                        assert(item);
                        if (event && event->note()) {
-                               if (event->note()->note() < midi_stream_view()->lowest_note() ||
-                                  event->note()->note() > midi_stream_view()->highest_note()) {
-                                       
+                               if (event->note()->note() < _current_range_min || event->note()->note() > _current_range_max) {
                                        if (canvas_item_visible(item)) {
                                                item->hide();
                                        }
@@ -647,8 +665,7 @@ MidiRegionView::set_height (gdouble height)
 
                                                note->property_y1() = y1;
                                                note->property_y2() = y2;
-                                       }
-                                       if (CanvasHit* hit = dynamic_cast<CanvasHit*>(event)) {
+                                       } else if (CanvasHit* hit = dynamic_cast<CanvasHit*>(event)) {
                                                double x = trackview.editor.frame_to_pixel((nframes64_t)
                                                                event->note()->time() - _region->start());
                                                const double diamond_size = midi_stream_view()->note_height() / 2.0;
@@ -666,11 +683,6 @@ MidiRegionView::set_height (gdouble height)
                        }
                }
 
-               _model->read_unlock();
-       }
-
-       if (name_text) {
-               name_text->raise_to_top();
        }
 }
 
@@ -700,7 +712,7 @@ MidiRegionView::add_ghost (TimeAxisView& tv)
        ghost->set_duration (_region->length() / samples_per_unit);
        ghosts.push_back (ghost);
 
-       for (std::vector<CanvasNoteEvent*>::iterator i = _events.begin(); i != _events.end(); ++i) {
+       for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
                if ((note = dynamic_cast<CanvasNote*>(*i)) != 0) {
                        ghost->add_note(note);
                }
@@ -977,7 +989,7 @@ MidiRegionView::update_drag_selection(double x1, double x2, double y1, double y2
 #endif
 
        if (x1 < x2) {
-               for (std::vector<CanvasNoteEvent*>::iterator i = _events.begin(); i != _events.end(); ++i) {
+               for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
 #ifndef NDEBUG
                        // Events should always be sorted by increasing x1() here
                        assert((*i)->x1() >= last_x1);
@@ -996,7 +1008,7 @@ MidiRegionView::update_drag_selection(double x1, double x2, double y1, double y2
                        }
                }
        } else {
-               for (std::vector<CanvasNoteEvent*>::iterator i = _events.begin(); i != _events.end(); ++i) {
+               for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
 #ifndef NDEBUG
                        // Events should always be sorted by increasing x1() here
                        assert((*i)->x1() >= last_x1);
@@ -1106,7 +1118,7 @@ MidiRegionView::note_dropped(CanvasNoteEvent* ev, double dt, uint8_t dnote)
                
                // care about notes being moved beyond the upper/lower bounds on the canvas
                if (lowest_note_in_selection  < midi_stream_view()->lowest_note() ||
-                  highest_note_in_selection > midi_stream_view()->highest_note()) {
+                               highest_note_in_selection > midi_stream_view()->highest_note()) {
                        midi_stream_view()->set_note_range(MidiStreamView::ContentsRange);
                }
        }
@@ -1378,8 +1390,7 @@ MidiRegionView::midi_channel_mode_changed(ChannelMode mode, uint16_t mask)
        };
 
        // Update notes for selection
-       for (std::vector<ArdourCanvas::CanvasNoteEvent*>::iterator i = _events.begin();
-                       i != _events.end(); ++i) {
+       for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
                (*i)->on_channel_selection_change(mask);
        }
 
index cb5789ab41ce4c22313316ebc0219e7dd14ec729..fe2655333cc0e4737cdcaca42117f372fc6a57e3 100644 (file)
@@ -72,6 +72,7 @@ class MidiRegionView : public RegionView
                { return midi_view()->midi_view(); }
 
        void set_height (double);
+       void apply_note_range(uint8_t lowest, uint8_t highest, bool force=false);
 
        void set_frame_color();
 
@@ -223,13 +224,18 @@ class MidiRegionView : public RegionView
        int8_t   _force_channel;
        uint16_t _last_channel_selection;
        double   _default_note_length;
+       uint8_t  _current_range_min;
+       uint8_t  _current_range_max;
 
-       boost::shared_ptr<ARDOUR::MidiModel>        _model;
-       std::vector<ArdourCanvas::CanvasNoteEvent*> _events;
-       std::vector< boost::shared_ptr<ArdourCanvas::CanvasProgramChange> > _pgm_changes;
-       ArdourCanvas::CanvasNote**                  _active_notes;
-       ArdourCanvas::Group*                        _note_group;
-       ARDOUR::MidiModel::DeltaCommand*            _delta_command;
+       typedef std::vector<ArdourCanvas::CanvasNoteEvent*> Events;
+       typedef std::vector< boost::shared_ptr<ArdourCanvas::CanvasProgramChange> > PgmChanges;
+       
+       boost::shared_ptr<ARDOUR::MidiModel> _model;
+       Events                               _events;
+       PgmChanges                           _pgm_changes;
+       ArdourCanvas::CanvasNote**           _active_notes;
+       ArdourCanvas::Group*                 _note_group;
+       ARDOUR::MidiModel::DeltaCommand*     _delta_command;
 
        MouseState _mouse_state;
        int _pressed_button;
index 6c620128bcac10c6a7f5ceaee6e8aaf959a0c6c0..d581e662677fa954d36c62da1e915ca51b93fad8 100644 (file)
@@ -55,10 +55,12 @@ using namespace Editing;
 MidiStreamView::MidiStreamView (MidiTimeAxisView& tv)
        : StreamView (tv)
        , note_range_adjustment(0.0f, 0.0f, 0.0f)
-       , _range(ContentsRange)
+       , _range_dirty(false)
        , _range_sum_cache(-1.0)
        , _lowest_note(60)
-       , _highest_note(60)
+       , _highest_note(71)
+       , _data_note_min(60)
+       , _data_note_max(71)
 {
        if (tv.is_track())
                stream_base_color = ARDOUR_UI::config()->canvasvar_MidiTrackBase.get();
@@ -83,14 +85,45 @@ MidiStreamView::MidiStreamView (MidiTimeAxisView& tv)
        _note_lines->signal_event().connect (bind (mem_fun (_trackview.editor, &PublicEditor::canvas_stream_view_event), _note_lines, &_trackview));
        _note_lines->lower_to_bottom();
 
-       note_range_adjustment.signal_value_changed().connect (mem_fun (*this, &MidiStreamView::note_range_adjustment_changed));
        ColorsChanged.connect(mem_fun(*this, &MidiStreamView::draw_note_lines));
+       
+       note_range_adjustment.set_page_size(_highest_note - _lowest_note);
+       note_range_adjustment.set_value(_lowest_note);
+       
+       note_range_adjustment.signal_value_changed().connect (mem_fun (*this, &MidiStreamView::note_range_adjustment_changed));
 }
 
 MidiStreamView::~MidiStreamView ()
 {
 }
 
+static void
+veto_note_range(uint8_t& min, uint8_t& max)
+{
+       /* Legal notes, thanks */
+       if (max > 127)
+               max = 127;
+       if (min > 127)
+               min = 127;
+       
+       /* Always display at least one octave in [0, 127] */
+       if (max == 127) {
+               if (min > (127 - 11)) {
+                       min = 127 - 11;
+               }
+       } else if (max < min + 11) {
+               uint8_t d = 11 - (max - min);
+               if (max + d/2 > 127) {
+                       min -= d;
+               } else {
+                       min -= d / 2;
+                       max += d / 2;
+               }
+       }
+       assert(max - min >= 11);
+       assert(max < 127);
+       assert(min < 127);
+}
 
 RegionView*
 MidiStreamView::add_region_view_internal (boost::shared_ptr<Region> r, bool wfd, bool recording)
@@ -110,7 +143,7 @@ MidiStreamView::add_region_view_internal (boost::shared_ptr<Region> r, bool wfd,
                        /* great. we already have a MidiRegionView for this Region. use it again. */
 
                        (*i)->set_valid (true);
-                       (*i)->enable_display(wfd);
+                       
                        display_region(dynamic_cast<MidiRegionView*>(*i), wfd);
 
                        return NULL;
@@ -122,20 +155,10 @@ MidiStreamView::add_region_view_internal (boost::shared_ptr<Region> r, bool wfd,
                
        region_view->init (region_color, false);
        region_views.push_front (region_view);
-       
-       /* follow global waveform setting */
-
-       if (wfd) {
-               region_view->enable_display(true);
-               region_view->midi_region()->midi_source(0)->load_model();
-       }
-
+                       
        /* display events and find note range */
        display_region(region_view, wfd);
 
-       /* always display at least 1 octave range */
-       _highest_note = max(_highest_note, static_cast<uint8_t>(_lowest_note + 11));
-
        /* catch regionview going away */
        region->GoingAway.connect (bind (mem_fun (*this, &MidiStreamView::remove_region_view), region));
        
@@ -149,17 +172,18 @@ MidiStreamView::display_region(MidiRegionView* region_view, bool load_model)
 {
        if ( ! region_view)
                return;
+                       
+       region_view->enable_display(true);
 
        boost::shared_ptr<MidiSource> source(region_view->midi_region()->midi_source(0));
 
        if (load_model)
                source->load_model();
 
-       // Find our note range
-       if (source->model())
-               for (size_t i=0; i < source->model()->n_notes(); ++i)
-                       update_bounds(source->model()->note_at(i)->note());
-       
+       _range_dirty = update_data_note_range(
+                       source->model()->lowest_note(),
+                       source->model()->highest_note());
+
        // Display region contents
        region_view->display_model(source->model());
 }
@@ -171,6 +195,21 @@ MidiStreamView::display_diskstream (boost::shared_ptr<Diskstream> ds)
        draw_note_lines();
        NoteRangeChanged();
 }
+                       
+bool
+MidiStreamView::update_data_note_range(uint8_t min, uint8_t max)
+{
+       bool dirty = false;
+       if (min < _data_note_min) {
+               _data_note_min = min;
+               dirty = true;
+       }
+       if (max > _data_note_max) {
+               _data_note_max = max;
+               dirty = true;
+       }
+       return dirty;
+}
 
 // FIXME: code duplication with AudioStreamView
 void
@@ -178,32 +217,49 @@ MidiStreamView::redisplay_diskstream ()
 {
        list<RegionView *>::iterator i, tmp;
 
+       _range_dirty = false;
+       _data_note_min = 127;
+       _data_note_max = 0;
+
        for (i = region_views.begin(); i != region_views.end(); ++i) {
-               (*i)->enable_display(true); // FIXME: double display, remove
                (*i)->set_valid (false);
+               (*i)->enable_display (false);
                
-               /* FIXME: slow.  MidiRegionView needs a find_note_range method
-                * that finds the range without wasting time drawing the events */
-
                // Load model if it isn't already, to get note range
                MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
-               mrv->midi_region()->midi_source(0)->load_model();
+               if (mrv) {
+                       mrv->midi_region()->midi_source(0)->load_model();
+                       _range_dirty = update_data_note_range(
+                                       mrv->midi_region()->model()->lowest_note(),
+                                       mrv->midi_region()->model()->highest_note());
+               }
+       }
+
+       // No notes, use default range
+       if (!_range_dirty) {
+               _data_note_min = 60;
+               _data_note_max = 71;
        }
        
+       bool range_changed = false;
+
+       // Extend visible range to show newly recorded data, if necessary
+       if (_data_note_min < _lowest_note) {
+               _lowest_note = _data_note_min;
+               range_changed = true;
+       }
+       if (_data_note_max > _highest_note) {
+               _highest_note = _data_note_max;
+               range_changed = true;
+       }
+       
+       veto_note_range(_lowest_note, _highest_note);
+       
        if (_trackview.is_midi_track()) {
                _trackview.get_diskstream()->playlist()->foreach_region (
                                static_cast<StreamView*>(this), &StreamView::add_region_view);
        }
 
-       /* Always display at least one octave */
-       if (_highest_note == 127) {
-               if (_lowest_note > (127 - 11)) {
-                       _lowest_note = 127 - 11;
-               }
-       } else if (_highest_note < _lowest_note + 11) {
-               _highest_note = _lowest_note + 11;
-       }
-       
        RegionViewList copy;
        
        /* Place regions */
@@ -256,15 +312,12 @@ MidiStreamView::redisplay_diskstream ()
        
        /* Fix canvas layering */
        for (RegionViewList::iterator j = copy.begin(); j != copy.end(); ++j) {
-               (*j)->enable_display(true); // FIXME: do this?
                region_layered (*j);
        }
        
-       /* Update note range and draw note lines */
-       note_range_adjustment.set_page_size(_highest_note - _lowest_note);
-       note_range_adjustment.set_value(_lowest_note);
+       /* Update note range and re-draw note lines if necessary */
+       apply_note_range(_lowest_note, _highest_note);
        NoteRangeChanged();
-       draw_note_lines();
 }
 
 
@@ -285,12 +338,12 @@ MidiStreamView::draw_note_lines()
 
        _note_lines->clear();
 
-       for(int i = _lowest_note; i <= _highest_note; ++i) {
+       for (int i = lowest_note(); i <= highest_note(); ++i) {
                y = floor(note_to_y(i));
                
                _note_lines->add_line(prev_y, 1.0, ARDOUR_UI::config()->canvasvar_PianoRollBlackOutline.get());
 
-               switch(i % 12) {
+               switch (i % 12) {
                case 1:
                case 3:
                case 6:
@@ -303,56 +356,54 @@ MidiStreamView::draw_note_lines()
                        break;
                }
 
-               if(i == _highest_note) {
+               if (i == highest_note()) {
                        _note_lines->add_line(y, prev_y - y, color);
-               }
-               else {
+               } else {
                        _note_lines->add_line(y + 1.0, prev_y - y - 1.0, color);
                }
 
                prev_y = y;
        }
 }
-       
 
 void
 MidiStreamView::set_note_range(VisibleNoteRange r)
 {
-       _range = r;
        if (r == FullRange) {
                _lowest_note = 0;
                _highest_note = 127;
        } else {
-               _lowest_note = 60;
-               _highest_note = 60;
+               _lowest_note = _data_note_min;
+               _highest_note = _data_note_max;
        }
-       redisplay_diskstream();
+
+       apply_note_range(_lowest_note, _highest_note);
 }
 
 void
-MidiStreamView::set_note_range(uint8_t lowest, uint8_t highest) {
-       if(_range == ContentsRange) {
-               _lowest_note = lowest;
-               _highest_note = highest;
-
-               list<RegionView *>::iterator i;
-               for (i = region_views.begin(); i != region_views.end(); ++i) {
-                       (*i)->set_height(height); // apply note range
-               }
+MidiStreamView::apply_note_range(uint8_t lowest, uint8_t highest)
+{
+       _highest_note = highest;
+       _lowest_note = lowest;
+       note_range_adjustment.set_page_size(_highest_note - _lowest_note);
+       note_range_adjustment.set_value(_lowest_note);
+       draw_note_lines();
+       
+       for (list<RegionView*>::iterator i = region_views.begin(); i != region_views.end(); ++i) {
+               ((MidiRegionView*)(*i))->apply_note_range(lowest, highest);
        }
 
-       draw_note_lines();
        NoteRangeChanged();
 }
-       
+
 void 
-MidiStreamView::update_bounds(uint8_t note_num)
+MidiStreamView::update_note_range(uint8_t note_num)
 {
-       _lowest_note = min(_lowest_note, note_num);
-       _highest_note = max(_highest_note, note_num);
+       assert(note_num <= 127);
+       _data_note_min = min(_data_note_min, note_num);
+       _data_note_max = max(_data_note_max, note_num);
 }
-
-
+       
 void
 MidiStreamView::setup_rec_box ()
 {
@@ -628,7 +679,6 @@ MidiStreamView::rec_data_range_ready (jack_nframes_t start, jack_nframes_t dur,
 void
 MidiStreamView::color_handler ()
 {
-
        //case cMidiTrackBase:
        if (_trackview.is_midi_track()) {
                //canvas_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_MidiTrackBase.get();
@@ -641,27 +691,30 @@ MidiStreamView::color_handler ()
 }
 
 void
-MidiStreamView::note_range_adjustment_changed() {
+MidiStreamView::note_range_adjustment_changed()
+{
        double sum = note_range_adjustment.get_value() + note_range_adjustment.get_page_size();
        int lowest = (int) floor(note_range_adjustment.get_value());
        int highest;
 
-       if(sum == _range_sum_cache) {
+       if (sum == _range_sum_cache) {
                //cerr << "cached" << endl;
                highest = (int) floor(sum);
-       }
-       else {
+       } else {
                //cerr << "recalc" << endl;
                highest = lowest + (int) floor(note_range_adjustment.get_page_size());
                _range_sum_cache = sum;
        }
 
-       if(lowest == lowest_note() && highest == highest_note()) {
+       if (lowest == _lowest_note && highest == _highest_note) {
                return;
        }
 
-       //cerr << "note range changed: " << lowest << " " << highest << endl;
+       //cerr << "note range adjustment changed: " << lowest << " " << highest << endl;
        //cerr << "  val=" << v_zoom_adjustment.get_value() << " page=" << v_zoom_adjustment.get_page_size() << " sum=" << v_zoom_adjustment.get_value() + v_zoom_adjustment.get_page_size() << endl;
 
-       set_note_range(lowest, highest);
+       _lowest_note = lowest;
+       _highest_note = highest;
+       apply_note_range(lowest, highest);
 }
+
index 33eb9e5b7d952a64f40d8aae146731e35454717e..e6d325c0c501606d4bb12465fb9a2381e31dc978 100644 (file)
@@ -69,14 +69,12 @@ class MidiStreamView : public StreamView
        Gtk::Adjustment note_range_adjustment;
        ArdourCanvas::Group* midi_underlay_group;
 
-       VisibleNoteRange note_range() { return _range; }
        void set_note_range(VisibleNoteRange r);
-       void set_note_range(uint8_t lowest, uint8_t highest);
 
-       uint8_t lowest_note()  const { return (_range == FullRange) ? 0 : _lowest_note; }
-       uint8_t highest_note() const { return (_range == FullRange) ? 127 : _highest_note; }
+       inline uint8_t lowest_note()  const { return _lowest_note; }
+       inline uint8_t highest_note() const { return _highest_note; }
        
-       void update_bounds(uint8_t note_num);
+       void update_note_range(uint8_t note_num);
        
        void redisplay_diskstream ();
        
@@ -85,41 +83,57 @@ class MidiStreamView : public StreamView
        
        inline double note_to_y(uint8_t note) const
                { return contents_height()
-                       - (note + 1 - _lowest_note) * note_height() + 1; }
+                       - (note + 1 - lowest_note()) * note_height() + 1; }
        
        inline uint8_t y_to_note(double y) const
                { return (uint8_t)((contents_height() - y - 1)
                                / contents_height() * (double)contents_note_range())
-                               + _lowest_note; }
+                               + lowest_note(); }
        
        inline double note_height() const
                { return contents_height() / (double)contents_note_range(); }
        
        inline uint8_t contents_note_range() const
-               { return _highest_note - _lowest_note + 1; }
+               { return highest_note() - lowest_note() + 1; }
        
        sigc::signal<void> NoteRangeChanged;
 
   private:
        void setup_rec_box ();
-       void rec_data_range_ready (jack_nframes_t start, jack_nframes_t dur, boost::weak_ptr<ARDOUR::Source> src); 
-       void update_rec_regions (boost::shared_ptr<ARDOUR::MidiModel> data, jack_nframes_t start, jack_nframes_t dur);
+
+       void rec_data_range_ready (
+                       jack_nframes_t start,
+                       jack_nframes_t dur,
+                       boost::weak_ptr<ARDOUR::Source> src); 
+
+       void update_rec_regions (
+                       boost::shared_ptr<ARDOUR::MidiModel> data,
+                       jack_nframes_t start,
+                       jack_nframes_t dur);
        
-       RegionView* add_region_view_internal (boost::shared_ptr<ARDOUR::Region>, bool wait_for_waves, bool recording = false);
-       void        display_region(MidiRegionView* region_view, bool load_model);
-       void        display_diskstream (boost::shared_ptr<ARDOUR::Diskstream> ds);
+       RegionView* add_region_view_internal (
+                       boost::shared_ptr<ARDOUR::Region>,
+                       bool wait_for_waves,
+                       bool recording = false);
+
+       void display_region(MidiRegionView* region_view, bool load_model);
+       void display_diskstream (boost::shared_ptr<ARDOUR::Diskstream> ds);
        
        void update_contents_height ();
        void draw_note_lines();
+       void apply_note_range(uint8_t lowest, uint8_t highest);
+       bool update_data_note_range(uint8_t min, uint8_t max);
 
        void color_handler ();
 
        void note_range_adjustment_changed();
 
-       VisibleNoteRange          _range;
+       bool                      _range_dirty;
        double                    _range_sum_cache;
-       uint8_t                   _lowest_note;
-       uint8_t                   _highest_note;
+       uint8_t                   _lowest_note;   ///< currently visible
+       uint8_t                   _highest_note;  ///< currently visible
+       uint8_t                   _data_note_min; ///< in data
+       uint8_t                   _data_note_max; ///< in data
        ArdourCanvas::Lineset*    _note_lines;
 };
 
index 7ef924dada5bbf4d459be8f6e82bf0edb8095c1d..b0cc49f388ad1ad7878d2f3bdc18edc31254a022 100644 (file)
@@ -83,8 +83,9 @@ using namespace Editing;
 
 
 MidiTimeAxisView::MidiTimeAxisView (PublicEditor& ed, Session& sess, boost::shared_ptr<Route> rt, Canvas& canvas)
-       : AxisView(sess) // FIXME: won't compile without this, why??
+       : AxisView(sess) // virtually inherited
        , RouteTimeAxisView(ed, sess, rt, canvas)
+       , _ignore_signals(false)  
        , _range_scroomer(0)
        , _piano_roll_header(0)
        , _note_mode(Sustained)
@@ -200,18 +201,14 @@ MidiTimeAxisView::append_extra_display_menu_items ()
        MenuList& range_items = range_menu->items();
        range_menu->set_name ("ArdourContextMenu");
        
-       RadioMenuItem::Group range_group;
-
-       range_items.push_back (RadioMenuElem (range_group, _("Show Full Range"), bind (
+       range_items.push_back (MenuElem (_("Show Full Range"), bind (
                        mem_fun(*this, &MidiTimeAxisView::set_note_range),
                        MidiStreamView::FullRange)));
        
-       range_items.push_back (RadioMenuElem (range_group, _("Fit Contents"), bind (
+       range_items.push_back (MenuElem (_("Fit Contents"), bind (
                        mem_fun(*this, &MidiTimeAxisView::set_note_range),
                        MidiStreamView::ContentsRange)));
 
-       ((Gtk::CheckMenuItem&)range_items.back()).set_active(true);
-
        items.push_back (MenuElem (_("Note range"), *range_menu));
 }
 
@@ -273,10 +270,8 @@ MidiTimeAxisView::set_note_mode(NoteMode mode)
 void
 MidiTimeAxisView::set_note_range(MidiStreamView::VisibleNoteRange range)
 {
-       //if (midi_view()->note_range() != range) {
+       if (!_ignore_signals)
                midi_view()->set_note_range(range);
-               midi_view()->redisplay_diskstream();
-       //}
 }
 
 
index f56ce604d458c0df9a7169195d33f79f5fdaedc9..484cf5265286669b64497d6f4bcaea112bb3a5ca 100644 (file)
@@ -94,8 +94,8 @@ class MidiTimeAxisView : public RouteTimeAxisView
        
        void channel_selector_toggled();
        
+       bool                         _ignore_signals;
        Gtk::Menu                    _subplugin_menu;
-
        MidiScroomer*                _range_scroomer;
        PianoRollHeader*             _piano_roll_header;
        ARDOUR::NoteMode             _note_mode;
index b029e51f33da58eb53153137098a77deace952de..a8cfe0cf13d8b18435069f83c52d101b1a00d6f0 100644 (file)
@@ -170,6 +170,9 @@ public:
        void add_note_unlocked(const boost::shared_ptr<Note> note);
        void remove_note_unlocked(const boost::shared_ptr<const Note> note);
        
+       uint8_t lowest_note()  const { return _lowest_note; }
+       uint8_t highest_note() const { return _highest_note; }
+       
 protected:
        mutable const_iterator _read_iter;
        bool                   _edited;
@@ -198,6 +201,9 @@ private:
        mutable nframes_t      _next_read;
        bool                   _percussive;
 
+       uint8_t _lowest_note;
+       uint8_t _highest_note;
+
        typedef std::priority_queue<
                        boost::shared_ptr<Note>, std::deque< boost::shared_ptr<Note> >,
                        LaterNoteEndComparator>
index 2ff5959afc7bf85e6689dbfee3187c9b617f0ee9..e1cea8e191d7d295f40af305d4baa52d2e78c03d 100644 (file)
@@ -1043,6 +1043,10 @@ ControlList::rt_safe_earliest_event_linear_unlocked (double start, double end, d
                const ControlEvent* first = NULL;
                const ControlEvent* next = NULL;
 
+               /* No events past start (maybe?) */
+               if (next && next->when < start)
+                       return false;
+
                /* Step is after first */
                if (range.first == _events.begin() || (*range.first)->when == start) {
                        first = *range.first;
@@ -1068,7 +1072,7 @@ ControlList::rt_safe_earliest_event_linear_unlocked (double start, double end, d
                        return true;
                }
                        
-               if (abs(first->value - next->value) <= 1) {
+               if (fabs(first->value - next->value) <= 1) {
                        if (next->when <= end && (!inclusive || next->when > start)) {
                                x = next->when;
                                y = next->value;
index 07a06015506e3b0e8acbf6cd918f3a9aed816894..1facd6761ef1befea21206218c72dffeb103f581 100644 (file)
@@ -314,6 +314,8 @@ Sequence::Sequence(const TypeMap& type_map, size_t size)
        , _end_iter(*this, DBL_MAX)
        , _next_read(UINT32_MAX)
        , _percussive(false)
+       , _lowest_note(127)
+       , _highest_note(0)
 {
        debugout << "Sequence (size " << size << ") constructed: " << this << endl;
        assert(_end_iter._is_end);
@@ -568,6 +570,11 @@ Sequence::append_note_on_unlocked(uint8_t chan, EventTime time, uint8_t note_num
        assert(_writing);
        _edited = true;
 
+       if (note_num < _lowest_note)
+               _lowest_note = note_num;
+       if (note_num > _highest_note)
+               _highest_note = note_num;
+
        boost::shared_ptr<Note> new_note(new Note(chan, time, 0, note_num, velocity));
        _notes.push_back(new_note);
        if (!_percussive) {