X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=gtk2_ardour%2Fmidi_region_view.cc;h=195cc6f828356801defcde4b56389fcfb4a224f0;hb=b04f89be519b6c3257cbe5707992b514771bce63;hp=376ae0197f32f9b730278ae2b5b40b22abe9f38d;hpb=a99331a4e946390609d7c158c843f2a14aee0bd2;p=ardour.git diff --git a/gtk2_ardour/midi_region_view.cc b/gtk2_ardour/midi_region_view.cc index 376ae0197f..195cc6f828 100644 --- a/gtk2_ardour/midi_region_view.cc +++ b/gtk2_ardour/midi_region_view.cc @@ -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) @@ -191,6 +189,8 @@ MidiRegionView::parameter_changed (std::string const & p) if (_enable_display) { redisplay_model(); } + } else if (p == "color-regions-using-track-color") { + set_colors (); } } @@ -212,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) @@ -244,7 +243,6 @@ MidiRegionView::MidiRegionView (const MidiRegionView& other, boost::shared_ptrParameterChanged.connect (*this, invalidator (*this), boost::bind (&MidiRegionView::parameter_changed, this, _1), gui_context()); + UIConfiguration::instance().ParameterChanged.connect (sigc::mem_fun (*this, &MidiRegionView::parameter_changed)); connect_to_diskstream (); } @@ -978,10 +977,8 @@ MidiRegionView::clear_events () } } - for (Events::iterator i = _events.begin(); i != _events.end(); ++i) { - delete *i; - } + _note_group->clear (true); _events.clear(); _patch_changes.clear(); _sys_exes.clear(); @@ -996,7 +993,7 @@ MidiRegionView::display_model(boost::shared_ptr model) content_connection.disconnect (); _model->ContentsChanged.connect (content_connection, invalidator (*this), boost::bind (&MidiRegionView::redisplay_model, this), gui_context()); /* Don't signal as nobody else needs to know until selection has been altered. */ - clear_events (); + clear_events(); if (_enable_display) { redisplay_model(); @@ -1055,7 +1052,7 @@ MidiRegionView::note_diff_add_change (NoteBase* ev, } void -MidiRegionView::apply_diff (bool as_subcommand) +MidiRegionView::apply_diff (bool as_subcommand, bool was_copy) { bool add_or_remove; bool commit = false; @@ -1064,15 +1061,14 @@ MidiRegionView::apply_diff (bool as_subcommand) return; } - if ((add_or_remove = _note_diff_command->adds_or_removes())) { + if (!was_copy && (add_or_remove = _note_diff_command->adds_or_removes())) { // Mark all selected notes for selection when model reloads for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) { _marked_for_selection.insert((*i)->note()); } } - midi_view()->midi_track()->midi_playlist()->region_edited( - _region, _note_diff_command); + midi_view()->midi_track()->midi_playlist()->region_edited (_region, _note_diff_command); if (as_subcommand) { _model->apply_command_as_subcommand (*trackview.session(), _note_diff_command); @@ -1104,18 +1100,18 @@ MidiRegionView::abort_command() NoteBase* MidiRegionView::find_canvas_note (boost::shared_ptr 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; @@ -1128,8 +1124,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; } } @@ -1139,10 +1135,10 @@ MidiRegionView::find_canvas_note (Evoral::event_id_t id) boost::shared_ptr MidiRegionView::find_canvas_patch_change (MidiModel::PatchChangePtr p) { - for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) { - if ((*x)->patch() == p) { - return (*x); - } + PatchChanges::const_iterator f = _patch_changes.find (p); + + if (f != _patch_changes.end()) { + return f->second; } return boost::shared_ptr(); @@ -1157,7 +1153,7 @@ MidiRegionView::get_events (Events& e, Evoral::Sequence::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)); } } } @@ -1173,8 +1169,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; @@ -1186,78 +1182,108 @@ MidiRegionView::redisplay_model() return; } - for (Events::iterator i = _events.begin(); i != _events.end(); ++i) { - (*i)->invalidate (); + for (_optimization_iterator = _events.begin(); _optimization_iterator != _events.end(); ++_optimization_iterator) { + _optimization_iterator->second->invalidate(); } - MidiModel::ReadLock lock(_model->read_lock()); + bool empty_when_starting = _events.empty(); + _optimization_iterator = _events.begin(); + MidiModel::Notes missing_notes; + Note* sus = NULL; + Hit* hit = NULL; + MidiModel::ReadLock lock(_model->read_lock()); MidiModel::Notes& notes (_model->notes()); - _optimization_iterator = _events.begin(); - bool empty_when_starting = _events.empty(); NoteBase* cne; - for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) { boost::shared_ptr note (*n); bool visible; if (note_in_region_range (note, visible)) { - if (!empty_when_starting && (cne = find_canvas_note (note)) != 0) { - + cne->validate (); if (visible) { - cne->validate (); - update_note (cne); cne->show (); } else { cne->hide (); } - } else { - - cne = add_note (note, visible); - } - - set::iterator it; - for (it = _pending_note_selection.begin(); it != _pending_note_selection.end(); ++it) { - if ((*it) == note->id()) { - add_to_selection (cne); - } - } - - } else { - - if (!empty_when_starting && (cne = find_canvas_note (note)) != 0) { - cne->validate (); - cne->hide (); + missing_notes.insert (note); } } } - /* remove note items that are no longer valid */ - if (!empty_when_starting) { + MidiModel::Notes::iterator f; for (Events::iterator i = _events.begin(); i != _events.end(); ) { - if (!(*i)->valid ()) { + + NoteBase* cne = i->second; + + /* remove note items that are no longer valid */ + if (!cne->valid()) { for (vector::iterator j = ghosts.begin(); j != ghosts.end(); ++j) { MidiGhostRegion* gr = dynamic_cast (*j); if (gr) { - gr->remove_note (*i); + gr->remove_note (cne); } } - delete *i; + delete cne; i = _events.erase (i); } else { + bool visible = cne->item()->visible(); + + if ((sus = dynamic_cast(cne))) { + + if (visible) { + update_sustained (sus); + } + + } else if ((hit = dynamic_cast(cne))) { + + if (visible) { + update_hit (hit); + } + + } ++i; } } } + for (MidiModel::Notes::iterator n = missing_notes.begin(); n != missing_notes.end(); ++n) { + boost::shared_ptr note (*n); + NoteBase* cne; + bool visible; + + if (note_in_region_range (note, visible)) { + if (visible) { + cne = add_note (note, true); + } else { + cne = add_note (note, false); + } + } else { + cne = add_note (note, false); + } + + for (set::iterator it = _pending_note_selection.begin(); it != _pending_note_selection.end(); ++it) { + if ((*it) == note->id()) { + add_to_selection (cne); + } + } + } + + for (vector::iterator j = ghosts.begin(); j != ghosts.end(); ++j) { + MidiGhostRegion* gr = dynamic_cast (*j); + if (gr && !gr->trackview.hidden()) { + gr->redisplay_model (); + } + } + _sys_exes.clear(); display_sysexes(); @@ -1267,12 +1293,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 @@ -1300,11 +1320,20 @@ MidiRegionView::display_patch_changes_on_channel (uint8_t channel, bool active_c } if ((p = find_canvas_patch_change (*i)) != 0) { - framecnt_t region_frames = source_beats_to_region_frames ((*i)->time()); - const double x = trackview.editor().sample_to_pixel (region_frames); - const string patch_name = instrument_info().get_patch_name ((*i)->bank(), (*i)->program(), channel); - p->canvas_item()->set_position (ArdourCanvas::Duple (x, 1.0)); - p->flag()->set_text (patch_name); + + const framecnt_t region_frames = source_beats_to_region_frames ((*i)->time()); + + if (region_frames < 0 || region_frames >= _region->length()) { + p->hide(); + } else { + const double x = trackview.editor().sample_to_pixel (region_frames); + const string patch_name = instrument_info().get_patch_name ((*i)->bank(), (*i)->program(), channel); + p->canvas_item()->set_position (ArdourCanvas::Duple (x, 1.0)); + p->flag()->set_text (patch_name); + + p->show(); + } + } else { const string patch_name = instrument_info().get_patch_name ((*i)->bank(), (*i)->program(), channel); add_canvas_patch_change (*i, patch_name, active_channel); @@ -1443,11 +1472,15 @@ MidiRegionView::reset_width_dependent_items (double pixel_width) redisplay_model(); } - for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) { - if ((*x)->canvas_item()->width() >= _pixel_width) { - (*x)->hide(); - } else { - (*x)->show(); + bool hide_all = false; + PatchChanges::iterator x = _patch_changes.begin(); + if (x != _patch_changes.end()) { + hide_all = x->second->flag()->width() >= _pixel_width; + } + + if (hide_all) { + for (; x != _patch_changes.end(); ++x) { + x->second->hide(); } } @@ -1470,7 +1503,7 @@ MidiRegionView::set_height (double height) } for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) { - (*x)->set_height (midi_stream_view()->contents_height()); + (*x).second->set_height (midi_stream_view()->contents_height()); } if (_step_edit_cursor) { @@ -1515,15 +1548,16 @@ MidiRegionView::add_ghost (TimeAxisView& tv) ghost = new MidiGhostRegion (*this, tv, trackview, unit_position); } - for (Events::iterator i = _events.begin(); i != _events.end(); ++i) { - ghost->add_note(*i); - } - ghost->set_colors (); ghost->set_height (); ghost->set_duration (_region->length() / samples_per_pixel); - ghosts.push_back (ghost); + for (Events::iterator i = _events.begin(); i != _events.end(); ++i) { + ghost->add_note(i->second); + } + + ghosts.push_back (ghost); + enable_display (true); return ghost; } @@ -1738,14 +1772,6 @@ MidiRegionView::update_sustained (Note* ev, bool update_ghost_regions) ev->set_fill_color(base_col); ev->set_outline_color(ev->calculate_outline(base_col, ev->selected())); - if (update_ghost_regions) { - for (std::vector::iterator i = ghosts.begin(); i != ghosts.end(); ++i) { - MidiGhostRegion* gr = dynamic_cast (*i); - if (gr) { - gr->update_note (ev); - } - } - } } void @@ -1775,14 +1801,6 @@ MidiRegionView::update_hit (Hit* ev, bool update_ghost_regions) ev->set_fill_color(base_col); ev->set_outline_color(ev->calculate_outline(base_col, ev->selected())); - if (update_ghost_regions) { - for (std::vector::iterator i = ghosts.begin(); i != ghosts.end(); ++i) { - MidiGhostRegion* gr = dynamic_cast (*i); - if (gr) { - gr->update_hit (ev); - } - } - } } /** Add a MIDI note to the view (with length). @@ -1798,7 +1816,7 @@ MidiRegionView::add_note(const boost::shared_ptr note, bool visible) if (midi_view()->note_mode() == Sustained) { - Note* ev_rect = new Note (*this, _note_group, note); + Note* ev_rect = new Note (*this, _note_group, note); // XXX may leak update_sustained (ev_rect); @@ -1808,7 +1826,7 @@ MidiRegionView::add_note(const boost::shared_ptr note, bool visible) const double diamond_size = std::max(1., floor(note_height()) - 2.); - Hit* ev_diamond = new Hit (*this, _note_group, diamond_size, note); + Hit* ev_diamond = new Hit (*this, _note_group, diamond_size, note); // XXX may leak update_hit (ev_diamond); @@ -1836,7 +1854,7 @@ MidiRegionView::add_note(const boost::shared_ptr 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(); @@ -1930,7 +1948,7 @@ MidiRegionView::add_canvas_patch_change (MidiModel::PatchChangePtr patch, const patch_change->hide (); } - _patch_changes.push_back (patch_change); + _patch_changes.insert (make_pair (patch, patch_change)); } void @@ -1938,7 +1956,7 @@ MidiRegionView::remove_canvas_patch_change (PatchChange* pc) { /* remove the canvas item */ for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) { - if ((*x)->patch() == pc->patch()) { + if (x->second->patch() == pc->patch()) { _patch_changes.erase (x); break; } @@ -2031,7 +2049,7 @@ MidiRegionView::change_patch_change (MidiModel::PatchChangePtr old_change, const trackview.editor().commit_reversible_command (); for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) { - if ((*x)->patch() == old_change) { + if (x->second->patch() == old_change) { _patch_changes.erase (x); break; } @@ -2202,7 +2220,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); } } @@ -2212,9 +2230,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); } } } @@ -2223,10 +2241,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); } } } @@ -2386,11 +2404,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); } - } } } @@ -2419,14 +2436,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); } } @@ -2461,13 +2478,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); } } } @@ -2559,68 +2576,192 @@ MidiRegionView::move_selection(double dx, double dy, double cumulative_dy) } } +NoteBase* +MidiRegionView::copy_selection () +{ + NoteBase* note; + _copy_drag_events.clear (); + + if (_selection.empty()) { + return 0; + } + + for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) { + boost::shared_ptr g (new NoteType (*((*i)->note()))); + if (midi_view()->note_mode() == Sustained) { + Note* n = new Note (*this, _note_group, g); + update_sustained (n, false); + note = n; + } else { + Hit* h = new Hit (*this, _note_group, 10, g); + update_hit (h, false); + note = h; + } + + _copy_drag_events.push_back (note); + } + + return _copy_drag_events.front (); +} + void -MidiRegionView::note_dropped(NoteBase *, frameoffset_t dt, int8_t dnote) +MidiRegionView::move_copies (double dx, double dy, double cumulative_dy) +{ + typedef vector > PossibleChord; + PossibleChord to_play; + Evoral::Beats earliest = Evoral::MaxBeats; + + for (CopyDragEvents::iterator i = _copy_drag_events.begin(); i != _copy_drag_events.end(); ++i) { + if ((*i)->note()->time() < earliest) { + earliest = (*i)->note()->time(); + } + } + + for (CopyDragEvents::iterator i = _copy_drag_events.begin(); i != _copy_drag_events.end(); ++i) { + if ((*i)->note()->time() == earliest) { + to_play.push_back ((*i)->note()); + } + (*i)->move_event(dx, dy); + } + + if (dy && !_copy_drag_events.empty() && !_no_sound_notes && UIConfiguration::instance().get_sound_midi_notes()) { + + if (to_play.size() > 1) { + + PossibleChord shifted; + + for (PossibleChord::iterator n = to_play.begin(); n != to_play.end(); ++n) { + boost::shared_ptr moved_note (new NoteType (**n)); + moved_note->set_note (moved_note->note() + cumulative_dy); + shifted.push_back (moved_note); + } + + start_playing_midi_chord (shifted); + + } else if (!to_play.empty()) { + + boost::shared_ptr moved_note (new NoteType (*to_play.front())); + moved_note->set_note (moved_note->note() + cumulative_dy); + start_playing_midi_note (moved_note); + } + } +} + +void +MidiRegionView::note_dropped(NoteBase *, frameoffset_t dt, int8_t dnote, bool copy) { uint8_t lowest_note_in_selection = 127; uint8_t highest_note_in_selection = 0; uint8_t highest_note_difference = 0; - // find highest and lowest notes first + if (!copy) { + // find highest and lowest notes first - for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) { - uint8_t pitch = (*i)->note()->note(); - lowest_note_in_selection = std::min(lowest_note_in_selection, pitch); - highest_note_in_selection = std::max(highest_note_in_selection, pitch); - } - - /* - cerr << "dnote: " << (int) dnote << endl; - cerr << "lowest note (streamview): " << int(midi_stream_view()->lowest_note()) - << " highest note (streamview): " << int(midi_stream_view()->highest_note()) << endl; - cerr << "lowest note (selection): " << int(lowest_note_in_selection) << " highest note(selection): " - << int(highest_note_in_selection) << endl; - cerr << "selection size: " << _selection.size() << endl; - cerr << "Highest note in selection: " << (int) highest_note_in_selection << endl; - */ + for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) { + uint8_t pitch = (*i)->note()->note(); + lowest_note_in_selection = std::min(lowest_note_in_selection, pitch); + highest_note_in_selection = std::max(highest_note_in_selection, pitch); + } - // Make sure the note pitch does not exceed the MIDI standard range - if (highest_note_in_selection + dnote > 127) { - highest_note_difference = highest_note_in_selection - 127; - } - TempoMap& map (trackview.session()->tempo_map()); + /* + cerr << "dnote: " << (int) dnote << endl; + cerr << "lowest note (streamview): " << int(midi_stream_view()->lowest_note()) + << " highest note (streamview): " << int(midi_stream_view()->highest_note()) << endl; + cerr << "lowest note (selection): " << int(lowest_note_in_selection) << " highest note(selection): " + << int(highest_note_in_selection) << endl; + cerr << "selection size: " << _selection.size() << endl; + cerr << "Highest note in selection: " << (int) highest_note_in_selection << endl; + */ - start_note_diff_command (_("move notes")); + // Make sure the note pitch does not exceed the MIDI standard range + if (highest_note_in_selection + dnote > 127) { + highest_note_difference = highest_note_in_selection - 127; + } + TempoMap& map (trackview.session()->tempo_map()); - for (Selection::iterator i = _selection.begin(); i != _selection.end() ; ++i) { + start_note_diff_command (_("move notes")); - double const start_qn = _region->quarter_note() - midi_region()->start_beats(); - framepos_t new_frames = map.frame_at_quarter_note (start_qn + (*i)->note()->time().to_double()) + dt; - Evoral::Beats new_time = Evoral::Beats (map.quarter_note_at_frame (new_frames) - start_qn); - if (new_time < 0) { - continue; + for (Selection::iterator i = _selection.begin(); i != _selection.end() ; ++i) { + + double const start_qn = _region->quarter_note() - midi_region()->start_beats(); + framepos_t new_frames = map.frame_at_quarter_note (start_qn + (*i)->note()->time().to_double()) + dt; + Evoral::Beats new_time = Evoral::Beats (map.quarter_note_at_frame (new_frames) - start_qn); + if (new_time < 0) { + continue; + } + + note_diff_add_change (*i, MidiModel::NoteDiffCommand::StartTime, new_time); + + uint8_t original_pitch = (*i)->note()->note(); + uint8_t new_pitch = original_pitch + dnote - highest_note_difference; + + // keep notes in standard midi range + clamp_to_0_127(new_pitch); + + lowest_note_in_selection = std::min(lowest_note_in_selection, new_pitch); + highest_note_in_selection = std::max(highest_note_in_selection, new_pitch); + + note_diff_add_change (*i, MidiModel::NoteDiffCommand::NoteNumber, new_pitch); + } + } else { + + clear_editor_note_selection (); + + for (CopyDragEvents::iterator i = _copy_drag_events.begin(); i != _copy_drag_events.end(); ++i) { + uint8_t pitch = (*i)->note()->note(); + lowest_note_in_selection = std::min(lowest_note_in_selection, pitch); + highest_note_in_selection = std::max(highest_note_in_selection, pitch); + } + + // Make sure the note pitch does not exceed the MIDI standard range + if (highest_note_in_selection + dnote > 127) { + highest_note_difference = highest_note_in_selection - 127; } - note_diff_add_change (*i, MidiModel::NoteDiffCommand::StartTime, new_time); + TempoMap& map (trackview.session()->tempo_map()); + start_note_diff_command (_("copy notes")); + + for (CopyDragEvents::iterator i = _copy_drag_events.begin(); i != _copy_drag_events.end() ; ++i) { + + /* update time */ + + double const start_qn = _region->quarter_note() - midi_region()->start_beats(); + framepos_t new_frames = map.frame_at_quarter_note (start_qn + (*i)->note()->time().to_double()) + dt; + Evoral::Beats new_time = Evoral::Beats (map.quarter_note_at_frame (new_frames) - start_qn); + + if (new_time < 0) { + continue; + } + + (*i)->note()->set_time (new_time); + + /* update pitch */ - uint8_t original_pitch = (*i)->note()->note(); - uint8_t new_pitch = original_pitch + dnote - highest_note_difference; + uint8_t original_pitch = (*i)->note()->note(); + uint8_t new_pitch = original_pitch + dnote - highest_note_difference; - // keep notes in standard midi range - clamp_to_0_127(new_pitch); + (*i)->note()->set_note (new_pitch); - lowest_note_in_selection = std::min(lowest_note_in_selection, new_pitch); - highest_note_in_selection = std::max(highest_note_in_selection, new_pitch); + // keep notes in standard midi range + clamp_to_0_127(new_pitch); + + lowest_note_in_selection = std::min(lowest_note_in_selection, new_pitch); + highest_note_in_selection = std::max(highest_note_in_selection, new_pitch); + + note_diff_add_note ((*i)->note(), true); + + delete *i; + } - note_diff_add_change (*i, MidiModel::NoteDiffCommand::NoteNumber, new_pitch); + _copy_drag_events.clear (); } - apply_diff(); + apply_diff (false, copy); // 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()) { - midi_stream_view()->set_note_range(MidiStreamView::ContentsRange); + midi_stream_view()->set_note_range (MidiStreamView::ContentsRange); } } @@ -3285,21 +3426,20 @@ MidiRegionView::nudge_notes (bool forward, bool fine) /* use grid */ - framepos_t next_pos = ref_point; - + MusicFrame next_pos (ref_point, 0); if (forward) { - if (max_framepos - 1 < next_pos) { - next_pos += 1; + if (max_framepos - 1 < next_pos.frame) { + next_pos.frame += 1; } } else { - if (next_pos == 0) { + if (next_pos.frame == 0) { return; } - next_pos -= 1; + next_pos.frame -= 1; } trackview.editor().snap_to (next_pos, (forward ? RoundUpAlways : RoundDownAlways), false); - const framecnt_t distance = ref_point - next_pos; + const framecnt_t distance = ref_point - next_pos.frame; delta = region_frames_to_region_beats (fabs ((double)distance)); } @@ -3457,7 +3597,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 (); @@ -3626,53 +3766,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(&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); } } @@ -3681,35 +3815,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(&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); } } @@ -3718,18 +3860,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); } } } @@ -3862,8 +4004,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); } } @@ -3882,7 +4024,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 */