From ffaf827d93e56052049fc00234283842564821d5 Mon Sep 17 00:00:00 2001 From: David Robillard Date: Tue, 23 Sep 2008 02:40:29 +0000 Subject: [PATCH] Eliminate a ton of unnecessary complete redrawing in MIDI stream views: 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 | 61 ++++++---- gtk2_ardour/midi_region_view.h | 18 ++- gtk2_ardour/midi_streamview.cc | 203 ++++++++++++++++++++------------ gtk2_ardour/midi_streamview.h | 46 +++++--- gtk2_ardour/midi_time_axis.cc | 15 +-- gtk2_ardour/midi_time_axis.h | 2 +- libs/evoral/evoral/Sequence.hpp | 6 + libs/evoral/src/ControlList.cpp | 6 +- libs/evoral/src/Sequence.cpp | 7 ++ 9 files changed, 230 insertions(+), 134 deletions(-) diff --git a/gtk2_ardour/midi_region_view.cc b/gtk2_ardour/midi_region_view.cc index c67d0a5c5e..90fcce84ea 100644 --- a/gtk2_ardour/midi_region_view.cc +++ b/gtk2_ardour/midi_region_view.cc @@ -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 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::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; - - _height = height; + apply_note_range(midi_stream_view()->lowest_note(), + midi_stream_view()->highest_note(), + height != old_height + FUDGE); + if (name_text) { + name_text->raise_to_top(); + } +} + + +/** 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; - _model->read_lock(); - - for (std::vector::const_iterator i = _events.begin(); i != _events.end(); ++i) { + for (Events::const_iterator i = _events.begin(); i != _events.end(); ++i) { CanvasNoteEvent* event = *i; Item* item = dynamic_cast(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(event)) { + } else if (CanvasHit* hit = dynamic_cast(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::iterator i = _events.begin(); i != _events.end(); ++i) { + for (Events::iterator i = _events.begin(); i != _events.end(); ++i) { if ((note = dynamic_cast(*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::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::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::iterator i = _events.begin(); - i != _events.end(); ++i) { + for (Events::iterator i = _events.begin(); i != _events.end(); ++i) { (*i)->on_channel_selection_change(mask); } diff --git a/gtk2_ardour/midi_region_view.h b/gtk2_ardour/midi_region_view.h index cb5789ab41..fe2655333c 100644 --- a/gtk2_ardour/midi_region_view.h +++ b/gtk2_ardour/midi_region_view.h @@ -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 _model; - std::vector _events; - std::vector< boost::shared_ptr > _pgm_changes; - ArdourCanvas::CanvasNote** _active_notes; - ArdourCanvas::Group* _note_group; - ARDOUR::MidiModel::DeltaCommand* _delta_command; + typedef std::vector Events; + typedef std::vector< boost::shared_ptr > PgmChanges; + + boost::shared_ptr _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; diff --git a/gtk2_ardour/midi_streamview.cc b/gtk2_ardour/midi_streamview.cc index 6c620128bc..d581e66267 100644 --- a/gtk2_ardour/midi_streamview.cc +++ b/gtk2_ardour/midi_streamview.cc @@ -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 r, bool wfd, bool recording) @@ -110,7 +143,7 @@ MidiStreamView::add_region_view_internal (boost::shared_ptr 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(*i), wfd); return NULL; @@ -122,20 +155,10 @@ MidiStreamView::add_region_view_internal (boost::shared_ptr 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(_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 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 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::iterator i, tmp; - for (i = region_views.begin(); i != region_views.end(); ++i) { - (*i)->enable_display(true); // FIXME: double display, remove - (*i)->set_valid (false); - - /* FIXME: slow. MidiRegionView needs a find_note_range method - * that finds the range without wasting time drawing the events */ + _range_dirty = false; + _data_note_min = 127; + _data_note_max = 0; + for (i = region_views.begin(); i != region_views.end(); ++i) { + (*i)->set_valid (false); + (*i)->enable_display (false); + // Load model if it isn't already, to get note range MidiRegionView* mrv = dynamic_cast(*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(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::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::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); } + diff --git a/gtk2_ardour/midi_streamview.h b/gtk2_ardour/midi_streamview.h index 33eb9e5b7d..e6d325c0c5 100644 --- a/gtk2_ardour/midi_streamview.h +++ b/gtk2_ardour/midi_streamview.h @@ -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 NoteRangeChanged; private: void setup_rec_box (); - void rec_data_range_ready (jack_nframes_t start, jack_nframes_t dur, boost::weak_ptr src); - void update_rec_regions (boost::shared_ptr 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 src); + + void update_rec_regions ( + boost::shared_ptr data, + jack_nframes_t start, + jack_nframes_t dur); - RegionView* add_region_view_internal (boost::shared_ptr, bool wait_for_waves, bool recording = false); - void display_region(MidiRegionView* region_view, bool load_model); - void display_diskstream (boost::shared_ptr ds); + RegionView* add_region_view_internal ( + boost::shared_ptr, + bool wait_for_waves, + bool recording = false); + + void display_region(MidiRegionView* region_view, bool load_model); + void display_diskstream (boost::shared_ptr 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; }; diff --git a/gtk2_ardour/midi_time_axis.cc b/gtk2_ardour/midi_time_axis.cc index 7ef924dada..b0cc49f388 100644 --- a/gtk2_ardour/midi_time_axis.cc +++ b/gtk2_ardour/midi_time_axis.cc @@ -83,8 +83,9 @@ using namespace Editing; MidiTimeAxisView::MidiTimeAxisView (PublicEditor& ed, Session& sess, boost::shared_ptr 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(); - //} } diff --git a/gtk2_ardour/midi_time_axis.h b/gtk2_ardour/midi_time_axis.h index f56ce604d4..484cf52652 100644 --- a/gtk2_ardour/midi_time_axis.h +++ b/gtk2_ardour/midi_time_axis.h @@ -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; diff --git a/libs/evoral/evoral/Sequence.hpp b/libs/evoral/evoral/Sequence.hpp index b029e51f33..a8cfe0cf13 100644 --- a/libs/evoral/evoral/Sequence.hpp +++ b/libs/evoral/evoral/Sequence.hpp @@ -170,6 +170,9 @@ public: void add_note_unlocked(const boost::shared_ptr note); void remove_note_unlocked(const boost::shared_ptr 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, std::deque< boost::shared_ptr >, LaterNoteEndComparator> diff --git a/libs/evoral/src/ControlList.cpp b/libs/evoral/src/ControlList.cpp index 2ff5959afc..e1cea8e191 100644 --- a/libs/evoral/src/ControlList.cpp +++ b/libs/evoral/src/ControlList.cpp @@ -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; diff --git a/libs/evoral/src/Sequence.cpp b/libs/evoral/src/Sequence.cpp index 07a0601550..1facd6761e 100644 --- a/libs/evoral/src/Sequence.cpp +++ b/libs/evoral/src/Sequence.cpp @@ -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 new_note(new Note(chan, time, 0, note_num, velocity)); _notes.push_back(new_note); if (!_percussive) {