From 5f319d0a08a4950f4cb8ee1bdc39f35bfce088bc Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Wed, 9 Sep 2009 16:46:18 +0000 Subject: [PATCH] re-use canvas note items when the model changes ; slightly more efficient (probably) and avoids invalidating references to said items in, for example, ResizeData in a copied region git-svn-id: svn://localhost/ardour2/branches/3.0@5650 d708f5d6-7413-0410-9779-e7cbd77b26cf --- gtk2_ardour/canvas-note-event.cc | 13 +++ gtk2_ardour/canvas-note-event.h | 5 + gtk2_ardour/editor_drag.cc | 1 + gtk2_ardour/midi_region_view.cc | 160 ++++++++++++++++++++++--------- gtk2_ardour/midi_region_view.h | 6 ++ 5 files changed, 142 insertions(+), 43 deletions(-) diff --git a/gtk2_ardour/canvas-note-event.cc b/gtk2_ardour/canvas-note-event.cc index 9404c15c2d..55e31db4e1 100644 --- a/gtk2_ardour/canvas-note-event.cc +++ b/gtk2_ardour/canvas-note-event.cc @@ -47,6 +47,7 @@ CanvasNoteEvent::CanvasNoteEvent(MidiRegionView& region, Item* item, , _state(None) , _note(note) , _selected(false) + , _valid (true) { } @@ -60,6 +61,18 @@ CanvasNoteEvent::~CanvasNoteEvent() delete _channel_selector_widget; } +void +CanvasNoteEvent::invalidate () +{ + _valid = false; +} + +void +CanvasNoteEvent::validate () +{ + _valid = true; +} + void CanvasNoteEvent::move_event(double dx, double dy) { diff --git a/gtk2_ardour/canvas-note-event.h b/gtk2_ardour/canvas-note-event.h index ec306d29b8..6b394e1bb5 100644 --- a/gtk2_ardour/canvas-note-event.h +++ b/gtk2_ardour/canvas-note-event.h @@ -66,6 +66,10 @@ public: virtual void hide() = 0; virtual bool on_event(GdkEvent* ev); + bool valid() const { return _valid; } + void invalidate (); + void validate (); + bool selected() const { return _selected; } void selected(bool yn); @@ -129,6 +133,7 @@ protected: const boost::shared_ptr _note; bool _own_note; bool _selected; + bool _valid; }; } // namespace Gnome diff --git a/gtk2_ardour/editor_drag.cc b/gtk2_ardour/editor_drag.cc index 0469e45e36..e54f4877bd 100644 --- a/gtk2_ardour/editor_drag.cc +++ b/gtk2_ardour/editor_drag.cc @@ -1457,6 +1457,7 @@ NoteResizeDrag::finished (GdkEvent* event, bool movement_occurred) MidiRegionSelection::iterator next; next = r; ++next; + cerr << "Working on MRV " << (*r)->midi_region()->name() << endl; (*r)->commit_resizing (at_front, _current_pointer_x - _grab_x, relative); r = next; } diff --git a/gtk2_ardour/midi_region_view.cc b/gtk2_ardour/midi_region_view.cc index 4ef0ead38e..a033148cb8 100644 --- a/gtk2_ardour/midi_region_view.cc +++ b/gtk2_ardour/midi_region_view.cc @@ -724,6 +724,20 @@ MidiRegionView::abort_command() clear_selection(); } +CanvasNoteEvent* +MidiRegionView::find_canvas_note (boost::shared_ptr note) +{ + /* XXX optimize the crap out of this SOON */ + + for (Events::iterator i = _events.begin(); i != _events.end(); ++i) { + if ((*i)->note() == note) { + return *i; + } + } + + return 0; +} + void MidiRegionView::redisplay_model() { @@ -738,17 +752,51 @@ MidiRegionView::redisplay_model() for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) { _marked_for_selection.insert((*i)->note()); } + + for (Events::iterator i = _events.begin(); i != _events.end(); ++i) { + (*i)->invalidate (); + } - clear_events(); _model->read_lock(); MidiModel::Notes notes = _model->notes(); for (size_t i = 0; i < _model->n_notes(); ++i) { boost::shared_ptr note (_model->note_at (i)); + if (note_in_visible_range (note)) { - add_note (note); + CanvasNoteEvent* cne; + + if ((cne = find_canvas_note (note)) != 0) { + + cne->validate (); + + CanvasNote* cn; + CanvasHit* ch; + + if ((cn = dynamic_cast(cne)) != 0) { + update_note (cn); + } else if ((ch = dynamic_cast(cne)) != 0) { + update_hit (ch); + } + + } else { + + add_note (note); + } + } + } + + /* remove note items that are no longer valid */ + + for (Events::iterator i = _events.begin(); i != _events.end(); ) { + if (!(*i)->valid ()) { + cerr << "Canvas note " << *i << " is invalid, deleting\n"; + delete *i; + i = _events.erase (i); + } else { + ++i; } } @@ -1100,6 +1148,64 @@ MidiRegionView::note_in_visible_range(const boost::shared_ptr note) co return !outside; } +void +MidiRegionView::update_note (CanvasNote* ev) +{ + boost::shared_ptr note = ev->note(); + + const nframes64_t note_start_frames = beats_to_frames(note->time()); + const nframes64_t note_end_frames = beats_to_frames(note->end_time()); + + const double x = trackview.editor().frame_to_pixel(note_start_frames - _region->start()); + + + const double y1 = midi_stream_view()->note_to_y(note->note()); + const double note_endpixel = + trackview.editor().frame_to_pixel(note_end_frames - _region->start()); + + ev->property_x1() = x; + ev->property_y1() = y1; + if (note->length() > 0) { + ev->property_x2() = note_endpixel; + } else { + ev->property_x2() = trackview.editor().frame_to_pixel(_region->length()); + } + ev->property_y2() = y1 + floor(midi_stream_view()->note_height()); + + if (note->length() == 0) { + if (_active_notes) { + assert(note->note() < 128); + // If this note is already active there's a stuck note, + // finish the old note rectangle + if (_active_notes[note->note()]) { + CanvasNote* const old_rect = _active_notes[note->note()]; + boost::shared_ptr old_note = old_rect->note(); + old_rect->property_x2() = x; + old_rect->property_outline_what() = (guint32) 0xF; + } + _active_notes[note->note()] = ev; + } + /* outline all but right edge */ + ev->property_outline_what() = (guint32) (0x1 & 0x4 & 0x8); + } else { + /* outline all edges */ + ev->property_outline_what() = (guint32) 0xF; + } +} + +void +MidiRegionView::update_hit (CanvasHit* ev) +{ + boost::shared_ptr note = ev->note(); + + const nframes64_t note_start_frames = beats_to_frames(note->time()); + const double x = trackview.editor().frame_to_pixel(note_start_frames - _region->start()); + const double diamond_size = midi_stream_view()->note_height() / 2.0; + const double y = midi_stream_view()->note_to_y(note->note()) + ((diamond_size-2) / 4.0); + + ev->move(x, y); +} + /** Add a MIDI note to the view (with length). * * If in sustained mode, notes with length 0 will be considered active @@ -1109,56 +1215,23 @@ MidiRegionView::note_in_visible_range(const boost::shared_ptr note) co void MidiRegionView::add_note(const boost::shared_ptr note) { + CanvasNoteEvent* event = 0; + assert(note->time() >= 0); assert(midi_view()->note_mode() == Sustained || midi_view()->note_mode() == Percussive); - const nframes64_t note_start_frames = beats_to_frames(note->time()); - const nframes64_t note_end_frames = beats_to_frames(note->end_time()); - ArdourCanvas::Group* const group = (ArdourCanvas::Group*)get_canvas_group(); - CanvasNoteEvent* event = 0; - - const double x = trackview.editor().frame_to_pixel(note_start_frames - _region->start()); - if (midi_view()->note_mode() == Sustained) { - const double y1 = midi_stream_view()->note_to_y(note->note()); - const double note_endpixel = - trackview.editor().frame_to_pixel(note_end_frames - _region->start()); CanvasNote* ev_rect = new CanvasNote(*this, *group, note); - ev_rect->property_x1() = x; - ev_rect->property_y1() = y1; - if (note->length() > 0) { - ev_rect->property_x2() = note_endpixel; - } else { - ev_rect->property_x2() = trackview.editor().frame_to_pixel(_region->length()); - } - ev_rect->property_y2() = y1 + floor(midi_stream_view()->note_height()); - if (note->length() == 0) { - if (_active_notes) { - assert(note->note() < 128); - // If this note is already active there's a stuck note, - // finish the old note rectangle - if (_active_notes[note->note()]) { - CanvasNote* const old_rect = _active_notes[note->note()]; - boost::shared_ptr old_note = old_rect->note(); - old_rect->property_x2() = x; - old_rect->property_outline_what() = (guint32) 0xF; - } - _active_notes[note->note()] = ev_rect; - } - /* outline all but right edge */ - ev_rect->property_outline_what() = (guint32) (0x1 & 0x4 & 0x8); - } else { - /* outline all edges */ - ev_rect->property_outline_what() = (guint32) 0xF; - } + update_note (ev_rect); event = ev_rect; MidiGhostRegion* gr; + for (std::vector::iterator g = ghosts.begin(); g != ghosts.end(); ++g) { if ((gr = dynamic_cast(*g)) != 0) { gr->add_note(ev_rect); @@ -1166,12 +1239,15 @@ MidiRegionView::add_note(const boost::shared_ptr note) } } else if (midi_view()->note_mode() == Percussive) { + const double diamond_size = midi_stream_view()->note_height() / 2.0; - const double y = midi_stream_view()->note_to_y(note->note()) + ((diamond_size-2) / 4.0); CanvasHit* ev_diamond = new CanvasHit(*this, *group, diamond_size, note); - ev_diamond->move(x, y); + + update_hit (ev_diamond); + event = ev_diamond; + } else { event = 0; } @@ -1860,8 +1936,6 @@ MidiRegionView::trim_note (CanvasNoteEvent* event, Evoral::MusicalTime front_del if negative - move the end of the note earlier in time (shortening it) */ - cerr << "Trim front by " << front_delta << " end by " << end_delta << endl; - if (front_delta) { if (front_delta < 0) { diff --git a/gtk2_ardour/midi_region_view.h b/gtk2_ardour/midi_region_view.h index 8bbd535a48..63c6a481de 100644 --- a/gtk2_ardour/midi_region_view.h +++ b/gtk2_ardour/midi_region_view.h @@ -37,6 +37,7 @@ #include "automation_line.h" #include "enums.h" #include "canvas.h" +#include "canvas-hit.h" #include "canvas-note.h" #include "canvas-note-event.h" #include "canvas-program-change.h" @@ -381,6 +382,11 @@ class MidiRegionView : public RegionView /* connection used to connect to model's ContentChanged signal */ sigc::connection content_connection; + + ArdourCanvas::CanvasNoteEvent* find_canvas_note (boost::shared_ptr); + void update_note (ArdourCanvas::CanvasNote*); + void update_hit (ArdourCanvas::CanvasHit*); + };