2 Copyright (C) 2001-2007 Paul Davis
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
27 #include <gtkmm2ext/gtk_ui.h>
29 #include <sigc++/signal.h>
31 #include "pbd/memento_command.h"
32 #include "pbd/stateful_diff_command.h"
34 #include "ardour/playlist.h"
35 #include "ardour/tempo.h"
36 #include "ardour/midi_region.h"
37 #include "ardour/midi_source.h"
38 #include "ardour/midi_model.h"
39 #include "ardour/midi_patch_manager.h"
40 #include "ardour/session.h"
42 #include "evoral/Parameter.hpp"
43 #include "evoral/MIDIParameters.hpp"
44 #include "evoral/Control.hpp"
45 #include "evoral/midi_util.h"
47 #include "automation_region_view.h"
48 #include "automation_time_axis.h"
49 #include "canvas-hit.h"
50 #include "canvas-note.h"
51 #include "canvas-program-change.h"
52 #include "ghostregion.h"
53 #include "gui_thread.h"
55 #include "midi_cut_buffer.h"
56 #include "midi_list_editor.h"
57 #include "midi_region_view.h"
58 #include "midi_streamview.h"
59 #include "midi_time_axis.h"
60 #include "midi_time_axis.h"
61 #include "midi_util.h"
62 #include "public_editor.h"
63 #include "rgb_macros.h"
64 #include "selection.h"
65 #include "simpleline.h"
66 #include "streamview.h"
71 using namespace ARDOUR;
73 using namespace Editing;
74 using namespace ArdourCanvas;
75 using Gtkmm2ext::Keyboard;
77 MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &tv,
78 boost::shared_ptr<MidiRegion> r, double spu, Gdk::Color const & basic_color)
79 : RegionView (parent, tv, r, spu, basic_color)
81 , _last_channel_selection(0xFFFF)
82 , _current_range_min(0)
83 , _current_range_max(0)
84 , _model_name(string())
85 , _custom_device_mode(string())
87 , _note_group(new ArdourCanvas::Group(*parent))
91 , _step_edit_cursor (0)
92 , _step_edit_cursor_width (1.0)
93 , _step_edit_cursor_position (0.0)
97 , _optimization_iterator (_events.end())
99 , no_sound_notes (false)
101 _note_group->raise_to_top();
102 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
105 MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &tv,
106 boost::shared_ptr<MidiRegion> r, double spu, Gdk::Color& basic_color,
107 TimeAxisViewItem::Visibility visibility)
108 : RegionView (parent, tv, r, spu, basic_color, false, visibility)
110 , _last_channel_selection(0xFFFF)
111 , _model_name(string())
112 , _custom_device_mode(string())
114 , _note_group(new ArdourCanvas::Group(*parent))
120 , _sort_needed (true)
121 , _optimization_iterator (_events.end())
123 , no_sound_notes (false)
125 _note_group->raise_to_top();
126 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
129 MidiRegionView::MidiRegionView (const MidiRegionView& other)
130 : sigc::trackable(other)
133 , _last_channel_selection(0xFFFF)
134 , _model_name(string())
135 , _custom_device_mode(string())
137 , _note_group(new ArdourCanvas::Group(*get_canvas_group()))
143 , _sort_needed (true)
144 , _optimization_iterator (_events.end())
146 , no_sound_notes (false)
151 UINT_TO_RGBA (other.fill_color, &r, &g, &b, &a);
152 c.set_rgb_p (r/255.0, g/255.0, b/255.0);
157 MidiRegionView::MidiRegionView (const MidiRegionView& other, boost::shared_ptr<MidiRegion> region)
158 : RegionView (other, boost::shared_ptr<Region> (region))
160 , _last_channel_selection(0xFFFF)
161 , _model_name(string())
162 , _custom_device_mode(string())
164 , _note_group(new ArdourCanvas::Group(*get_canvas_group()))
170 , _sort_needed (true)
171 , _optimization_iterator (_events.end())
173 , no_sound_notes (false)
178 UINT_TO_RGBA (other.fill_color, &r, &g, &b, &a);
179 c.set_rgb_p (r/255.0, g/255.0, b/255.0);
185 MidiRegionView::init (Gdk::Color const & basic_color, bool wfd)
187 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
189 CanvasNoteEvent::CanvasNoteEventDeleted.connect (note_delete_connection, MISSING_INVALIDATOR,
190 ui_bind (&MidiRegionView::maybe_remove_deleted_note_from_selection, this, _1),
194 midi_region()->midi_source(0)->load_model();
197 _model = midi_region()->midi_source(0)->model();
198 _enable_display = false;
200 RegionView::init (basic_color, false);
202 compute_colors (basic_color);
204 set_height (trackview.current_height());
207 region_sync_changed ();
208 region_resized (ARDOUR::bounds_change);
211 reset_width_dependent_items (_pixel_width);
215 _enable_display = true;
218 display_model (_model);
222 group->raise_to_top();
223 group->signal_event().connect (sigc::mem_fun (this, &MidiRegionView::canvas_event), false);
225 midi_view()->signal_channel_mode_changed().connect(
226 sigc::mem_fun(this, &MidiRegionView::midi_channel_mode_changed));
228 midi_view()->signal_midi_patch_settings_changed().connect(
229 sigc::mem_fun(this, &MidiRegionView::midi_patch_settings_changed));
231 trackview.editor().SnapChanged.connect (snap_changed_connection, invalidator (*this), ui_bind (&MidiRegionView::snap_changed, this), gui_context ());
235 MidiRegionView::canvas_event(GdkEvent* ev)
237 if (!trackview.editor().internal_editing()) {
241 /* XXX: note that until version 2.30, the GnomeCanvas did not propagate scroll events
242 to its items, which means that ev->type == GDK_SCROLL will never be seen
247 return scroll (&ev->scroll);
250 return key_press (&ev->key);
252 case GDK_KEY_RELEASE:
253 return key_release (&ev->key);
255 case GDK_BUTTON_PRESS:
256 return button_press (&ev->button);
258 case GDK_2BUTTON_PRESS:
261 case GDK_BUTTON_RELEASE:
262 return button_release (&ev->button);
264 case GDK_ENTER_NOTIFY:
265 return enter_notify (&ev->crossing);
267 case GDK_LEAVE_NOTIFY:
268 return leave_notify (&ev->crossing);
270 case GDK_MOTION_NOTIFY:
271 return motion (&ev->motion);
281 MidiRegionView::enter_notify (GdkEventCrossing* ev)
283 /* FIXME: do this on switch to note tool, too, if the pointer is already in */
285 Keyboard::magic_widget_grab_focus();
288 if (trackview.editor().current_mouse_mode() == MouseRange) {
289 create_ghost_note (ev->x, ev->y);
296 MidiRegionView::leave_notify (GdkEventCrossing* ev)
298 trackview.editor().hide_verbose_canvas_cursor ();
305 MidiRegionView::button_press (GdkEventButton* ev)
309 group->w2i (_last_x, _last_y);
311 if (_mouse_state != SelectTouchDragging && ev->button == 1) {
312 _pressed_button = ev->button;
313 _mouse_state = Pressed;
317 _pressed_button = ev->button;
323 MidiRegionView::button_release (GdkEventButton* ev)
325 double event_x, event_y;
326 nframes64_t event_frame = 0;
330 group->w2i(event_x, event_y);
331 group->ungrab(ev->time);
332 event_frame = trackview.editor().pixel_to_frame(event_x);
334 if (ev->button == 3) {
336 } else if (_pressed_button != 1) {
340 switch (_mouse_state) {
341 case Pressed: // Clicked
342 switch (trackview.editor().current_mouse_mode()) {
346 maybe_select_by_position (ev, event_x, event_y);
352 Evoral::MusicalTime beats = trackview.editor().get_grid_type_as_beats (success, trackview.editor().pixel_to_frame (event_x));
356 create_note_at (event_x, event_y, beats, true);
364 case SelectRectDragging: // Select drag done
370 case AddDragging: // Add drag done
372 if (_drag_rect->property_x2() > _drag_rect->property_x1() + 2) {
373 const double x = _drag_rect->property_x1();
374 const double length = trackview.editor().pixel_to_frame
375 (_drag_rect->property_x2() - _drag_rect->property_x1());
377 create_note_at (x, _drag_rect->property_y1(), frames_to_beats(length), false);
383 create_ghost_note (ev->x, ev->y);
393 MidiRegionView::motion (GdkEventMotion* ev)
395 double event_x, event_y;
396 nframes64_t event_frame = 0;
400 group->w2i(event_x, event_y);
402 // convert event_x to global frame
403 event_frame = trackview.editor().pixel_to_frame(event_x) + _region->position();
404 trackview.editor().snap_to(event_frame);
405 // convert event_frame back to local coordinates relative to position
406 event_frame -= _region->position();
409 update_ghost_note (ev->x, ev->y);
412 /* any motion immediately hides velocity text that may have been visible */
414 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
415 (*i)->hide_velocity ();
418 switch (_mouse_state) {
419 case Pressed: // Maybe start a drag, if we've moved a bit
421 if (fabs (event_x - _last_x) < 1 && fabs (event_y - _last_y) < 1) {
422 /* no appreciable movement since the button was pressed */
427 if (_pressed_button == 1 && trackview.editor().current_mouse_mode() == MouseObject) {
428 group->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
429 Gdk::Cursor(Gdk::FLEUR), ev->time);
432 _drag_start_x = event_x;
433 _drag_start_y = event_y;
435 _drag_rect = new ArdourCanvas::SimpleRect(*group);
436 _drag_rect->property_x1() = event_x;
437 _drag_rect->property_y1() = event_y;
438 _drag_rect->property_x2() = event_x;
439 _drag_rect->property_y2() = event_y;
440 _drag_rect->property_outline_what() = 0xFF;
441 _drag_rect->property_outline_color_rgba()
442 = ARDOUR_UI::config()->canvasvar_MidiSelectRectOutline.get();
443 _drag_rect->property_fill_color_rgba()
444 = ARDOUR_UI::config()->canvasvar_MidiSelectRectFill.get();
446 _mouse_state = SelectRectDragging;
449 // Add note drag start
450 } else if (trackview.editor().internal_editing()) {
455 group->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
456 Gdk::Cursor(Gdk::FLEUR), ev->time);
459 _drag_start_x = event_x;
460 _drag_start_y = event_y;
462 _drag_rect = new ArdourCanvas::SimpleRect(*group);
463 _drag_rect->property_x1() = trackview.editor().frame_to_pixel(event_frame);
465 _drag_rect->property_y1() = midi_stream_view()->note_to_y(
466 midi_stream_view()->y_to_note(event_y));
467 _drag_rect->property_x2() = trackview.editor().frame_to_pixel(event_frame);
468 _drag_rect->property_y2() = _drag_rect->property_y1()
469 + floor(midi_stream_view()->note_height());
470 _drag_rect->property_outline_what() = 0xFF;
471 _drag_rect->property_outline_color_rgba() = 0xFFFFFF99;
472 _drag_rect->property_fill_color_rgba() = 0xFFFFFF66;
474 _mouse_state = AddDragging;
480 case SelectRectDragging: // Select drag motion
481 case AddDragging: // Add note drag motion
485 GdkModifierType state;
486 gdk_window_get_pointer(ev->window, &t_x, &t_y, &state);
491 if (_mouse_state == AddDragging)
492 event_x = trackview.editor().frame_to_pixel(event_frame);
495 if (event_x > _drag_start_x)
496 _drag_rect->property_x2() = event_x;
498 _drag_rect->property_x1() = event_x;
501 if (_drag_rect && _mouse_state == SelectRectDragging) {
502 if (event_y > _drag_start_y)
503 _drag_rect->property_y2() = event_y;
505 _drag_rect->property_y1() = event_y;
507 update_drag_selection(_drag_start_x, event_x, _drag_start_y, event_y);
513 case SelectTouchDragging:
525 MidiRegionView::scroll (GdkEventScroll* ev)
527 if (_selection.empty()) {
531 trackview.editor().hide_verbose_canvas_cursor ();
533 bool fine = !Keyboard::modifier_state_equals (ev->state, Keyboard::SecondaryModifier);
535 if (ev->direction == GDK_SCROLL_UP) {
536 change_velocities (true, fine, false);
537 } else if (ev->direction == GDK_SCROLL_DOWN) {
538 change_velocities (false, fine, false);
544 MidiRegionView::key_press (GdkEventKey* ev)
546 /* since GTK bindings are generally activated on press, and since
547 detectable auto-repeat is the name of the game and only sends
548 repeated presses, carry out key actions at key press, not release.
551 if (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R){
552 _mouse_state = SelectTouchDragging;
555 } else if (ev->keyval == GDK_Escape) {
559 } else if (ev->keyval == GDK_comma || ev->keyval == GDK_period) {
561 bool start = (ev->keyval == GDK_comma);
562 bool end = (ev->keyval == GDK_period);
563 bool shorter = Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier);
564 bool fine = Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
566 change_note_lengths (fine, shorter, 0.0, start, end);
570 } else if (ev->keyval == GDK_Delete) {
575 } else if (ev->keyval == GDK_Tab) {
577 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
578 goto_previous_note ();
584 } else if (ev->keyval == GDK_Up) {
586 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
587 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
589 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
590 change_velocities (true, fine, allow_smush);
592 transpose (true, fine, allow_smush);
596 } else if (ev->keyval == GDK_Down) {
598 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
599 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
601 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
602 change_velocities (false, fine, allow_smush);
604 transpose (false, fine, allow_smush);
608 } else if (ev->keyval == GDK_Left) {
613 } else if (ev->keyval == GDK_Right) {
618 } else if (ev->keyval == GDK_Control_L) {
627 MidiRegionView::key_release (GdkEventKey* ev)
629 if (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R) {
637 MidiRegionView::show_list_editor ()
640 _list_editor = new MidiListEditor (trackview.session(), midi_region());
642 _list_editor->present ();
645 /** Add a note to the model, and the view, at a canvas (click) coordinate.
646 * \param x horizontal position in pixels
647 * \param y vertical position in pixels
648 * \param length duration of the note in beats, which will be snapped to the grid
649 * \param sh true to make the note 1 frame shorter than the snapped version of \a length.
652 MidiRegionView::create_note_at(double x, double y, double length, bool sh)
654 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
655 MidiStreamView* const view = mtv->midi_view();
657 double note = midi_stream_view()->y_to_note(y);
660 assert(note <= 127.0);
662 // Start of note in frames relative to region start
663 nframes64_t const start_frames = snap_frame_to_frame(trackview.editor().pixel_to_frame(x));
664 assert(start_frames >= 0);
667 length = frames_to_beats(
668 snap_frame_to_frame(start_frames + beats_to_frames(length)) - start_frames);
670 assert (length != 0);
673 length = frames_to_beats (beats_to_frames (length) - 1);
676 uint16_t chn_mask = mtv->channel_selector().get_selected_channels();
680 /* pick the highest selected channel, unless all channels are selected,
681 which is interpreted to mean channel 1 (zero)
684 for (uint16_t i = 0; i < 16; ++i) {
685 if (chn_mask & (1<<i)) {
695 const boost::shared_ptr<NoteType> new_note (new NoteType (channel,
696 frames_to_beats(start_frames + _region->start()), length,
697 (uint8_t)note, 0x40));
699 if (_model->contains (new_note)) {
703 view->update_note_range(new_note->note());
705 MidiModel::DiffCommand* cmd = _model->new_diff_command("add note");
707 _model->apply_command(*trackview.session(), cmd);
709 play_midi_note (new_note);
713 MidiRegionView::clear_events()
718 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
719 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
724 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
729 _pgm_changes.clear();
731 _optimization_iterator = _events.end();
736 MidiRegionView::display_model(boost::shared_ptr<MidiModel> model)
739 content_connection.disconnect ();
740 _model->ContentsChanged.connect (content_connection, invalidator (*this), boost::bind (&MidiRegionView::redisplay_model, this), gui_context());
744 if (_enable_display) {
750 MidiRegionView::start_diff_command(string name)
752 if (!_diff_command) {
753 _diff_command = _model->new_diff_command(name);
758 MidiRegionView::diff_add_note(const boost::shared_ptr<NoteType> note, bool selected, bool show_velocity)
761 _diff_command->add(note);
764 _marked_for_selection.insert(note);
767 _marked_for_velocity.insert(note);
772 MidiRegionView::diff_remove_note(ArdourCanvas::CanvasNoteEvent* ev)
774 if (_diff_command && ev->note()) {
775 _diff_command->remove(ev->note());
780 MidiRegionView::diff_add_change (ArdourCanvas::CanvasNoteEvent* ev,
781 MidiModel::DiffCommand::Property property,
785 _diff_command->change (ev->note(), property, val);
790 MidiRegionView::diff_add_change (ArdourCanvas::CanvasNoteEvent* ev,
791 MidiModel::DiffCommand::Property property,
792 Evoral::MusicalTime val)
795 _diff_command->change (ev->note(), property, val);
800 MidiRegionView::apply_diff ()
804 if (!_diff_command) {
808 if ((add_or_remove = _diff_command->adds_or_removes())) {
809 // Mark all selected notes for selection when model reloads
810 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
811 _marked_for_selection.insert((*i)->note());
815 _model->apply_command(*trackview.session(), _diff_command);
817 midi_view()->midi_track()->playlist_modified();
820 _marked_for_selection.clear();
823 _marked_for_velocity.clear();
827 MidiRegionView::apply_diff_as_subcommand()
831 if (!_diff_command) {
835 if ((add_or_remove = _diff_command->adds_or_removes())) {
836 // Mark all selected notes for selection when model reloads
837 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
838 _marked_for_selection.insert((*i)->note());
842 _model->apply_command_as_subcommand(*trackview.session(), _diff_command);
844 midi_view()->midi_track()->playlist_modified();
847 _marked_for_selection.clear();
849 _marked_for_velocity.clear();
854 MidiRegionView::abort_command()
856 delete _diff_command;
862 MidiRegionView::find_canvas_note (boost::shared_ptr<NoteType> note)
864 if (_optimization_iterator != _events.end()) {
865 ++_optimization_iterator;
868 if (_optimization_iterator != _events.end() && (*_optimization_iterator)->note() == note) {
869 return *_optimization_iterator;
872 for (_optimization_iterator = _events.begin(); _optimization_iterator != _events.end(); ++_optimization_iterator) {
873 if ((*_optimization_iterator)->note() == note) {
874 return *_optimization_iterator;
882 MidiRegionView::get_events (Events& e, Evoral::Sequence<Evoral::MusicalTime>::NoteOperator op, uint8_t val, int chan_mask)
884 MidiModel::Notes notes;
885 _model->get_notes (notes, op, val, chan_mask);
887 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
888 CanvasNoteEvent* cne = find_canvas_note (*n);
896 MidiRegionView::redisplay_model()
898 // Don't redisplay the model if we're currently recording and displaying that
904 cerr << "MidiRegionView::redisplay_model called without a model" << endmsg;
908 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
912 MidiModel::ReadLock lock(_model->read_lock());
914 MidiModel::Notes& notes (_model->notes());
915 _optimization_iterator = _events.begin();
917 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
919 boost::shared_ptr<NoteType> note (*n);
920 CanvasNoteEvent* cne;
923 if (note_in_region_range (note, visible)) {
925 if ((cne = find_canvas_note (note)) != 0) {
932 if ((cn = dynamic_cast<CanvasNote*>(cne)) != 0) {
934 } else if ((ch = dynamic_cast<CanvasHit*>(cne)) != 0) {
946 add_note (note, visible);
951 if ((cne = find_canvas_note (note)) != 0) {
959 /* remove note items that are no longer valid */
961 for (Events::iterator i = _events.begin(); i != _events.end(); ) {
962 if (!(*i)->valid ()) {
964 i = _events.erase (i);
970 _pgm_changes.clear();
974 display_program_changes();
976 _marked_for_selection.clear ();
977 _marked_for_velocity.clear ();
979 /* we may have caused _events to contain things out of order (e.g. if a note
980 moved earlier or later). we don't generally need them in time order, but
981 make a note that a sort is required for those cases that require it.
988 MidiRegionView::display_program_changes()
990 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
991 uint16_t chn_mask = mtv->channel_selector().get_selected_channels();
993 for (uint8_t i = 0; i < 16; ++i) {
994 if (chn_mask & (1<<i)) {
995 display_program_changes_on_channel (i);
1001 MidiRegionView::display_program_changes_on_channel(uint8_t channel)
1003 boost::shared_ptr<Evoral::Control> control =
1004 _model->control(Evoral::MIDI::ProgramChange (MidiPgmChangeAutomation, channel));
1010 Glib::Mutex::Lock lock (control->list()->lock());
1012 for (AutomationList::const_iterator event = control->list()->begin();
1013 event != control->list()->end(); ++event) {
1014 double event_time = (*event)->when;
1015 double program_number = floor((*event)->value + 0.5);
1017 // Get current value of bank select MSB at time of the program change
1018 Evoral::Parameter bank_select_msb(MidiCCAutomation, channel, MIDI_CTL_MSB_BANK);
1019 boost::shared_ptr<Evoral::Control> msb_control = _model->control(bank_select_msb);
1021 if (msb_control != 0) {
1022 msb = uint8_t(floor(msb_control->get_double(true, event_time) + 0.5));
1025 // Get current value of bank select LSB at time of the program change
1026 Evoral::Parameter bank_select_lsb(MidiCCAutomation, channel, MIDI_CTL_LSB_BANK);
1027 boost::shared_ptr<Evoral::Control> lsb_control = _model->control(bank_select_lsb);
1029 if (lsb_control != 0) {
1030 lsb = uint8_t(floor(lsb_control->get_double(true, event_time) + 0.5));
1033 MIDI::Name::PatchPrimaryKey patch_key(msb, lsb, program_number);
1035 boost::shared_ptr<MIDI::Name::Patch> patch =
1036 MIDI::Name::MidiPatchManager::instance().find_patch(
1037 _model_name, _custom_device_mode, channel, patch_key);
1039 PCEvent program_change(event_time, uint8_t(program_number), channel);
1042 add_pgm_change(program_change, patch->name());
1045 // program_number is zero-based: convert to one-based
1046 snprintf(buf, 4, "%d", int(program_number+1));
1047 add_pgm_change(program_change, buf);
1053 MidiRegionView::display_sysexes()
1055 for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
1056 Evoral::MusicalTime time = (*i)->time();
1061 for (uint32_t b = 0; b < (*i)->size(); ++b) {
1062 str << int((*i)->buffer()[b]);
1063 if (b != (*i)->size() -1) {
1067 string text = str.str();
1069 ArdourCanvas::Group* const group = (ArdourCanvas::Group*)get_canvas_group();
1071 const double x = trackview.editor().frame_to_pixel(beats_to_frames(time));
1073 double height = midi_stream_view()->contents_height();
1075 boost::shared_ptr<CanvasSysEx> sysex = boost::shared_ptr<CanvasSysEx>(
1076 new CanvasSysEx(*this, *group, text, height, x, 1.0));
1078 // Show unless program change is beyond the region bounds
1079 if (time - _region->start() >= _region->length() || time < _region->start()) {
1085 _sys_exes.push_back(sysex);
1090 MidiRegionView::~MidiRegionView ()
1092 in_destructor = true;
1094 trackview.editor().hide_verbose_canvas_cursor ();
1096 note_delete_connection.disconnect ();
1098 delete _list_editor;
1100 RegionViewGoingAway (this); /* EMIT_SIGNAL */
1102 if (_active_notes) {
1110 delete _diff_command;
1111 delete _step_edit_cursor;
1115 MidiRegionView::region_resized (const PropertyChange& what_changed)
1117 RegionView::region_resized(what_changed);
1119 if (what_changed.contains (ARDOUR::Properties::position)) {
1120 set_duration(_region->length(), 0);
1121 if (_enable_display) {
1128 MidiRegionView::reset_width_dependent_items (double pixel_width)
1130 RegionView::reset_width_dependent_items(pixel_width);
1131 assert(_pixel_width == pixel_width);
1133 if (_enable_display) {
1137 move_step_edit_cursor (_step_edit_cursor_position);
1138 set_step_edit_cursor_width (_step_edit_cursor_width);
1142 MidiRegionView::set_height (double height)
1144 static const double FUDGE = 2.0;
1145 const double old_height = _height;
1146 RegionView::set_height(height);
1147 _height = height - FUDGE;
1149 apply_note_range(midi_stream_view()->lowest_note(),
1150 midi_stream_view()->highest_note(),
1151 height != old_height + FUDGE);
1154 name_pixbuf->raise_to_top();
1157 for (PgmChanges::iterator x = _pgm_changes.begin(); x != _pgm_changes.end(); ++x) {
1158 (*x)->set_height (midi_stream_view()->contents_height());
1161 if (_step_edit_cursor) {
1162 _step_edit_cursor->property_y2() = midi_stream_view()->contents_height();
1167 /** Apply the current note range from the stream view
1168 * by repositioning/hiding notes as necessary
1171 MidiRegionView::apply_note_range (uint8_t min, uint8_t max, bool force)
1173 if (!_enable_display) {
1177 if (!force && _current_range_min == min && _current_range_max == max) {
1181 _current_range_min = min;
1182 _current_range_max = max;
1184 for (Events::const_iterator i = _events.begin(); i != _events.end(); ++i) {
1185 CanvasNoteEvent* event = *i;
1186 boost::shared_ptr<NoteType> note (event->note());
1188 if (note->note() < _current_range_min ||
1189 note->note() > _current_range_max) {
1195 if (CanvasNote* cnote = dynamic_cast<CanvasNote*>(event)) {
1197 const double y1 = midi_stream_view()->note_to_y(note->note());
1198 const double y2 = y1 + floor(midi_stream_view()->note_height());
1200 cnote->property_y1() = y1;
1201 cnote->property_y2() = y2;
1203 } else if (CanvasHit* chit = dynamic_cast<CanvasHit*>(event)) {
1205 double x = trackview.editor().frame_to_pixel(
1206 beats_to_frames(note->time()) - _region->start());
1207 const double diamond_size = midi_stream_view()->note_height() / 2.0;
1208 double y = midi_stream_view()->note_to_y(event->note()->note())
1209 + ((diamond_size-2.0) / 4.0);
1211 chit->set_height (diamond_size);
1212 chit->move (x - chit->x1(), y - chit->y1());
1219 MidiRegionView::add_ghost (TimeAxisView& tv)
1223 double unit_position = _region->position () / samples_per_unit;
1224 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(&tv);
1225 MidiGhostRegion* ghost;
1227 if (mtv && mtv->midi_view()) {
1228 /* if ghost is inserted into midi track, use a dedicated midi ghost canvas group
1229 to allow having midi notes on top of note lines and waveforms.
1231 ghost = new MidiGhostRegion (*mtv->midi_view(), trackview, unit_position);
1233 ghost = new MidiGhostRegion (tv, trackview, unit_position);
1236 ghost->set_height ();
1237 ghost->set_duration (_region->length() / samples_per_unit);
1238 ghosts.push_back (ghost);
1240 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1241 if ((note = dynamic_cast<CanvasNote*>(*i)) != 0) {
1242 ghost->add_note(note);
1246 GhostRegion::CatchDeletion.connect (*this, invalidator (*this), ui_bind (&RegionView::remove_ghost, this, _1), gui_context());
1252 /** Begin tracking note state for successive calls to add_event
1255 MidiRegionView::begin_write()
1257 assert(!_active_notes);
1258 _active_notes = new CanvasNote*[128];
1259 for (unsigned i=0; i < 128; ++i) {
1260 _active_notes[i] = 0;
1265 /** Destroy note state for add_event
1268 MidiRegionView::end_write()
1270 delete[] _active_notes;
1272 _marked_for_selection.clear();
1273 _marked_for_velocity.clear();
1277 /** Resolve an active MIDI note (while recording).
1280 MidiRegionView::resolve_note(uint8_t note, double end_time)
1282 if (midi_view()->note_mode() != Sustained) {
1286 if (_active_notes && _active_notes[note]) {
1287 const nframes64_t end_time_frames = beats_to_frames(end_time);
1288 _active_notes[note]->property_x2() = trackview.editor().frame_to_pixel(end_time_frames);
1289 _active_notes[note]->property_outline_what() = (guint32) 0xF; // all edges
1290 _active_notes[note] = 0;
1295 /** Extend active notes to rightmost edge of region (if length is changed)
1298 MidiRegionView::extend_active_notes()
1300 if (!_active_notes) {
1304 for (unsigned i=0; i < 128; ++i) {
1305 if (_active_notes[i]) {
1306 _active_notes[i]->property_x2() = trackview.editor().frame_to_pixel(_region->length());
1312 MidiRegionView::play_midi_note(boost::shared_ptr<NoteType> note)
1314 if (no_sound_notes || !trackview.editor().sound_notes()) {
1318 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1321 route_ui->midi_track()->write_immediate_event(
1322 note->on_event().size(), note->on_event().buffer());
1324 const double note_length_beats = (note->off_event().time() - note->on_event().time());
1325 nframes_t note_length_ms = beats_to_frames(note_length_beats)
1326 * (1000 / (double)route_ui->session()->nominal_frame_rate());
1327 Glib::signal_timeout().connect(sigc::bind(sigc::mem_fun(this, &MidiRegionView::play_midi_note_off), note),
1328 note_length_ms, G_PRIORITY_DEFAULT);
1332 MidiRegionView::play_midi_note_off(boost::shared_ptr<NoteType> note)
1334 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1337 route_ui->midi_track()->write_immediate_event(
1338 note->off_event().size(), note->off_event().buffer());
1344 MidiRegionView::note_in_region_range(const boost::shared_ptr<NoteType> note, bool& visible) const
1346 const nframes64_t note_start_frames = beats_to_frames(note->time());
1348 bool outside = (note_start_frames - _region->start() >= _region->length()) ||
1349 (note_start_frames < _region->start());
1351 visible = (note->note() >= midi_stream_view()->lowest_note()) &&
1352 (note->note() <= midi_stream_view()->highest_note());
1358 MidiRegionView::update_note (CanvasNote* ev)
1360 boost::shared_ptr<NoteType> note = ev->note();
1362 const nframes64_t note_start_frames = beats_to_frames(note->time());
1364 /* trim note display to not overlap the end of its region */
1365 const nframes64_t note_end_frames = min (beats_to_frames (note->end_time()), _region->start() + _region->length());
1367 const double x = trackview.editor().frame_to_pixel(note_start_frames - _region->start());
1368 const double y1 = midi_stream_view()->note_to_y(note->note());
1369 const double note_endpixel = trackview.editor().frame_to_pixel(note_end_frames - _region->start());
1371 ev->property_x1() = x;
1372 ev->property_y1() = y1;
1373 if (note->length() > 0) {
1374 ev->property_x2() = note_endpixel;
1376 ev->property_x2() = trackview.editor().frame_to_pixel(_region->length());
1378 ev->property_y2() = y1 + floor(midi_stream_view()->note_height());
1380 if (note->length() == 0) {
1381 if (_active_notes) {
1382 assert(note->note() < 128);
1383 // If this note is already active there's a stuck note,
1384 // finish the old note rectangle
1385 if (_active_notes[note->note()]) {
1386 CanvasNote* const old_rect = _active_notes[note->note()];
1387 boost::shared_ptr<NoteType> old_note = old_rect->note();
1388 old_rect->property_x2() = x;
1389 old_rect->property_outline_what() = (guint32) 0xF;
1391 _active_notes[note->note()] = ev;
1393 /* outline all but right edge */
1394 ev->property_outline_what() = (guint32) (0x1 & 0x4 & 0x8);
1396 /* outline all edges */
1397 ev->property_outline_what() = (guint32) 0xF;
1402 MidiRegionView::update_hit (CanvasHit* ev)
1404 boost::shared_ptr<NoteType> note = ev->note();
1406 const nframes64_t note_start_frames = beats_to_frames(note->time());
1407 const double x = trackview.editor().frame_to_pixel(note_start_frames - _region->start());
1408 const double diamond_size = midi_stream_view()->note_height() / 2.0;
1409 const double y = midi_stream_view()->note_to_y(note->note()) + ((diamond_size-2) / 4.0);
1414 /** Add a MIDI note to the view (with length).
1416 * If in sustained mode, notes with length 0 will be considered active
1417 * notes, and resolve_note should be called when the corresponding note off
1418 * event arrives, to properly display the note.
1421 MidiRegionView::add_note(const boost::shared_ptr<NoteType> note, bool visible)
1423 CanvasNoteEvent* event = 0;
1425 assert(note->time() >= 0);
1426 assert(midi_view()->note_mode() == Sustained || midi_view()->note_mode() == Percussive);
1428 ArdourCanvas::Group* const group = (ArdourCanvas::Group*)get_canvas_group();
1430 if (midi_view()->note_mode() == Sustained) {
1432 CanvasNote* ev_rect = new CanvasNote(*this, *group, note);
1434 update_note (ev_rect);
1438 MidiGhostRegion* gr;
1440 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
1441 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
1442 gr->add_note(ev_rect);
1446 } else if (midi_view()->note_mode() == Percussive) {
1448 const double diamond_size = midi_stream_view()->note_height() / 2.0;
1450 CanvasHit* ev_diamond = new CanvasHit(*this, *group, diamond_size, note);
1452 update_hit (ev_diamond);
1461 if (_marked_for_selection.find(note) != _marked_for_selection.end()) {
1462 note_selected(event, true);
1465 if (_marked_for_velocity.find(note) != _marked_for_velocity.end()) {
1466 event->show_velocity();
1468 event->on_channel_selection_change(_last_channel_selection);
1469 _events.push_back(event);
1480 MidiRegionView::step_add_note (uint8_t channel, uint8_t number, uint8_t velocity,
1481 Evoral::MusicalTime pos, Evoral::MusicalTime len)
1483 boost::shared_ptr<NoteType> new_note (new NoteType (channel, pos, len, number, velocity));
1485 /* potentially extend region to hold new note */
1487 nframes64_t end_frame = _region->position() + beats_to_frames (new_note->end_time());
1488 nframes64_t region_end = _region->position() + _region->length() - 1;
1490 if (end_frame > region_end) {
1491 _region->set_length (end_frame - _region->position(), this);
1494 _marked_for_selection.clear ();
1497 start_diff_command (_("step add"));
1498 diff_add_note (new_note, true, false);
1501 // last_step_edit_note = new_note;
1505 MidiRegionView::step_sustain (Evoral::MusicalTime beats)
1507 change_note_lengths (false, false, beats, false, true);
1511 MidiRegionView::add_pgm_change(PCEvent& program, const string& displaytext)
1513 assert(program.time >= 0);
1515 ArdourCanvas::Group* const group = (ArdourCanvas::Group*)get_canvas_group();
1516 const double x = trackview.editor().frame_to_pixel(beats_to_frames(program.time));
1518 double height = midi_stream_view()->contents_height();
1520 boost::shared_ptr<CanvasProgramChange> pgm_change = boost::shared_ptr<CanvasProgramChange>(
1521 new CanvasProgramChange(*this, *group,
1526 _custom_device_mode,
1527 program.time, program.channel, program.value));
1529 // Show unless program change is beyond the region bounds
1530 if (program.time - _region->start() >= _region->length() || program.time < _region->start()) {
1536 _pgm_changes.push_back(pgm_change);
1540 MidiRegionView::get_patch_key_at(double time, uint8_t channel, MIDI::Name::PatchPrimaryKey& key)
1542 Evoral::Parameter bank_select_msb(MidiCCAutomation, channel, MIDI_CTL_MSB_BANK);
1543 boost::shared_ptr<Evoral::Control> msb_control = _model->control(bank_select_msb);
1545 if (msb_control != 0) {
1546 msb = int(msb_control->get_double(true, time));
1549 Evoral::Parameter bank_select_lsb(MidiCCAutomation, channel, MIDI_CTL_LSB_BANK);
1550 boost::shared_ptr<Evoral::Control> lsb_control = _model->control(bank_select_lsb);
1552 if (lsb_control != 0) {
1553 lsb = lsb_control->get_double(true, time);
1556 Evoral::Parameter program_change(MidiPgmChangeAutomation, channel, 0);
1557 boost::shared_ptr<Evoral::Control> program_control = _model->control(program_change);
1558 double program_number = -1.0;
1559 if (program_control != 0) {
1560 program_number = program_control->get_double(true, time);
1563 key.msb = (int) floor(msb + 0.5);
1564 key.lsb = (int) floor(lsb + 0.5);
1565 key.program_number = (int) floor(program_number + 0.5);
1566 assert(key.is_sane());
1571 MidiRegionView::alter_program_change(PCEvent& old_program, const MIDI::Name::PatchPrimaryKey& new_patch)
1573 // TODO: Get the real event here and alter them at the original times
1574 Evoral::Parameter bank_select_msb(MidiCCAutomation, old_program.channel, MIDI_CTL_MSB_BANK);
1575 boost::shared_ptr<Evoral::Control> msb_control = _model->control(bank_select_msb);
1576 if (msb_control != 0) {
1577 msb_control->set_double(double(new_patch.msb), true, old_program.time);
1580 // TODO: Get the real event here and alter them at the original times
1581 Evoral::Parameter bank_select_lsb(MidiCCAutomation, old_program.channel, MIDI_CTL_LSB_BANK);
1582 boost::shared_ptr<Evoral::Control> lsb_control = _model->control(bank_select_lsb);
1583 if (lsb_control != 0) {
1584 lsb_control->set_double(double(new_patch.lsb), true, old_program.time);
1587 Evoral::Parameter program_change(MidiPgmChangeAutomation, old_program.channel, 0);
1588 boost::shared_ptr<Evoral::Control> program_control = _model->control(program_change);
1590 assert(program_control != 0);
1591 program_control->set_double(float(new_patch.program_number), true, old_program.time);
1593 _pgm_changes.clear ();
1594 display_program_changes (); // XXX would be nice to limit to just old_program.channel
1598 MidiRegionView::program_selected(CanvasProgramChange& program, const MIDI::Name::PatchPrimaryKey& new_patch)
1600 PCEvent program_change_event(program.event_time(), program.program(), program.channel());
1601 alter_program_change(program_change_event, new_patch);
1605 MidiRegionView::previous_program(CanvasProgramChange& program)
1607 if (program.program() < 127) {
1608 MIDI::Name::PatchPrimaryKey key;
1609 get_patch_key_at(program.event_time(), program.channel(), key);
1610 PCEvent program_change_event(program.event_time(), program.program(), program.channel());
1612 key.program_number++;
1613 alter_program_change(program_change_event, key);
1618 MidiRegionView::next_program(CanvasProgramChange& program)
1620 if (program.program() > 0) {
1621 MIDI::Name::PatchPrimaryKey key;
1622 get_patch_key_at(program.event_time(), program.channel(), key);
1623 PCEvent program_change_event(program.event_time(), program.program(), program.channel());
1625 key.program_number--;
1626 alter_program_change(program_change_event, key);
1631 MidiRegionView::maybe_remove_deleted_note_from_selection (CanvasNoteEvent* cne)
1633 if (_selection.empty()) {
1637 if (_selection.erase (cne) > 0) {
1638 cerr << "Erased a CNE from selection\n";
1643 MidiRegionView::delete_selection()
1645 if (_selection.empty()) {
1649 start_diff_command (_("delete selection"));
1651 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1652 if ((*i)->selected()) {
1653 _diff_command->remove((*i)->note());
1663 MidiRegionView::delete_note (boost::shared_ptr<NoteType> n)
1665 start_diff_command (_("delete note"));
1666 _diff_command->remove (n);
1669 trackview.editor().hide_verbose_canvas_cursor ();
1673 MidiRegionView::clear_selection_except(ArdourCanvas::CanvasNoteEvent* ev)
1675 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1676 if ((*i)->selected() && (*i) != ev) {
1677 (*i)->set_selected(false);
1678 (*i)->hide_velocity();
1686 MidiRegionView::unique_select(ArdourCanvas::CanvasNoteEvent* ev)
1688 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
1691 Selection::iterator tmp = i;
1694 (*i)->set_selected (false);
1695 _selection.erase (i);
1704 /* don't bother with removing this regionview from the editor selection,
1705 since we're about to add another note, and thus put/keep this
1706 regionview in the editor selection.
1709 if (!ev->selected()) {
1710 add_to_selection (ev);
1715 MidiRegionView::select_matching_notes (uint8_t notenum, uint16_t channel_mask, bool add, bool extend)
1717 uint8_t low_note = 127;
1718 uint8_t high_note = 0;
1719 MidiModel::Notes& notes (_model->notes());
1720 _optimization_iterator = _events.begin();
1726 if (extend && _selection.empty()) {
1732 /* scan existing selection to get note range */
1734 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1735 if ((*i)->note()->note() < low_note) {
1736 low_note = (*i)->note()->note();
1738 if ((*i)->note()->note() > high_note) {
1739 high_note = (*i)->note()->note();
1743 low_note = min (low_note, notenum);
1744 high_note = max (high_note, notenum);
1747 no_sound_notes = true;
1749 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1751 boost::shared_ptr<NoteType> note (*n);
1752 CanvasNoteEvent* cne;
1753 bool select = false;
1755 if (((1 << note->channel()) & channel_mask) != 0) {
1757 if ((note->note() >= low_note && note->note() <= high_note)) {
1760 } else if (note->note() == notenum) {
1766 if ((cne = find_canvas_note (note)) != 0) {
1767 // extend is false because we've taken care of it,
1768 // since it extends by time range, not pitch.
1769 note_selected (cne, add, false);
1773 add = true; // we need to add all remaining matching notes, even if the passed in value was false (for "set")
1777 no_sound_notes = false;
1781 MidiRegionView::toggle_matching_notes (uint8_t notenum, uint16_t channel_mask)
1783 MidiModel::Notes& notes (_model->notes());
1784 _optimization_iterator = _events.begin();
1786 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1788 boost::shared_ptr<NoteType> note (*n);
1789 CanvasNoteEvent* cne;
1791 if (note->note() == notenum && (((0x0001 << note->channel()) & channel_mask) != 0)) {
1792 if ((cne = find_canvas_note (note)) != 0) {
1793 if (cne->selected()) {
1794 note_deselected (cne);
1796 note_selected (cne, true, false);
1804 MidiRegionView::note_selected(ArdourCanvas::CanvasNoteEvent* ev, bool add, bool extend)
1807 clear_selection_except(ev);
1812 if (!ev->selected()) {
1813 add_to_selection (ev);
1817 /* find end of latest note selected, select all between that and the start of "ev" */
1819 Evoral::MusicalTime earliest = DBL_MAX;
1820 Evoral::MusicalTime latest = 0;
1822 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1823 if ((*i)->note()->end_time() > latest) {
1824 latest = (*i)->note()->end_time();
1826 if ((*i)->note()->time() < earliest) {
1827 earliest = (*i)->note()->time();
1831 if (ev->note()->end_time() > latest) {
1832 latest = ev->note()->end_time();
1835 if (ev->note()->time() < earliest) {
1836 earliest = ev->note()->time();
1839 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1841 /* find notes entirely within OR spanning the earliest..latest range */
1843 if (((*i)->note()->time() >= earliest && (*i)->note()->end_time() <= latest) ||
1844 ((*i)->note()->time() <= earliest && (*i)->note()->end_time() >= latest)) {
1845 add_to_selection (*i);
1849 /* if events were guaranteed to be time sorted, we could do this.
1850 but as of sept 10th 2009, they no longer are.
1853 if ((*i)->note()->time() > latest) {
1862 MidiRegionView::note_deselected(ArdourCanvas::CanvasNoteEvent* ev)
1864 remove_from_selection (ev);
1868 MidiRegionView::update_drag_selection(double x1, double x2, double y1, double y2)
1878 // TODO: Make this faster by storing the last updated selection rect, and only
1879 // adjusting things that are in the area that appears/disappeared.
1880 // We probably need a tree to be able to find events in O(log(n)) time.
1882 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1884 /* check if any corner of the note is inside the rect
1887 1) this is computing "touched by", not "contained by" the rect.
1888 2) this does not require that events be sorted in time.
1891 const double ix1 = (*i)->x1();
1892 const double ix2 = (*i)->x2();
1893 const double iy1 = (*i)->y1();
1894 const double iy2 = (*i)->y2();
1896 if ((ix1 >= x1 && ix1 <= x2 && iy1 >= y1 && iy1 <= y2) ||
1897 (ix1 >= x1 && ix1 <= x2 && iy2 >= y1 && iy2 <= y2) ||
1898 (ix2 >= x1 && ix2 <= x2 && iy1 >= y1 && iy1 <= y2) ||
1899 (ix2 >= x1 && ix2 <= x2 && iy2 >= y1 && iy2 <= y2)) {
1902 if (!(*i)->selected()) {
1903 add_to_selection (*i);
1905 } else if ((*i)->selected()) {
1906 // Not inside rectangle
1907 remove_from_selection (*i);
1913 MidiRegionView::remove_from_selection (CanvasNoteEvent* ev)
1915 Selection::iterator i = _selection.find (ev);
1917 if (i != _selection.end()) {
1918 _selection.erase (i);
1921 ev->set_selected (false);
1922 ev->hide_velocity ();
1924 if (_selection.empty()) {
1925 PublicEditor& editor (trackview.editor());
1926 editor.get_selection().remove (this);
1931 MidiRegionView::add_to_selection (CanvasNoteEvent* ev)
1933 bool add_mrv_selection = false;
1935 if (_selection.empty()) {
1936 add_mrv_selection = true;
1939 if (_selection.insert (ev).second) {
1940 ev->set_selected (true);
1941 play_midi_note ((ev)->note());
1944 if (add_mrv_selection) {
1945 PublicEditor& editor (trackview.editor());
1946 editor.get_selection().add (this);
1951 MidiRegionView::move_selection(double dx, double dy)
1953 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1954 (*i)->move_event(dx, dy);
1959 MidiRegionView::note_dropped(CanvasNoteEvent *, frameoffset_t dt, int8_t dnote)
1961 assert (!_selection.empty());
1963 uint8_t lowest_note_in_selection = 127;
1964 uint8_t highest_note_in_selection = 0;
1965 uint8_t highest_note_difference = 0;
1967 // find highest and lowest notes first
1969 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1970 uint8_t pitch = (*i)->note()->note();
1971 lowest_note_in_selection = std::min(lowest_note_in_selection, pitch);
1972 highest_note_in_selection = std::max(highest_note_in_selection, pitch);
1976 cerr << "dnote: " << (int) dnote << endl;
1977 cerr << "lowest note (streamview): " << int(midi_stream_view()->lowest_note())
1978 << " highest note (streamview): " << int(midi_stream_view()->highest_note()) << endl;
1979 cerr << "lowest note (selection): " << int(lowest_note_in_selection) << " highest note(selection): "
1980 << int(highest_note_in_selection) << endl;
1981 cerr << "selection size: " << _selection.size() << endl;
1982 cerr << "Highest note in selection: " << (int) highest_note_in_selection << endl;
1985 // Make sure the note pitch does not exceed the MIDI standard range
1986 if (highest_note_in_selection + dnote > 127) {
1987 highest_note_difference = highest_note_in_selection - 127;
1990 start_diff_command(_("move notes"));
1992 for (Selection::iterator i = _selection.begin(); i != _selection.end() ; ++i) {
1994 Evoral::MusicalTime new_time = frames_to_beats (beats_to_frames ((*i)->note()->time()) + dt);
2000 diff_add_change (*i, MidiModel::DiffCommand::StartTime, new_time);
2002 uint8_t original_pitch = (*i)->note()->note();
2003 uint8_t new_pitch = original_pitch + dnote - highest_note_difference;
2005 // keep notes in standard midi range
2006 clamp_to_0_127(new_pitch);
2008 // keep original pitch if note is dragged outside valid midi range
2009 if ((original_pitch != 0 && new_pitch == 0)
2010 || (original_pitch != 127 && new_pitch == 127)) {
2011 new_pitch = original_pitch;
2014 lowest_note_in_selection = std::min(lowest_note_in_selection, new_pitch);
2015 highest_note_in_selection = std::max(highest_note_in_selection, new_pitch);
2017 diff_add_change (*i, MidiModel::DiffCommand::NoteNumber, new_pitch);
2022 // care about notes being moved beyond the upper/lower bounds on the canvas
2023 if (lowest_note_in_selection < midi_stream_view()->lowest_note() ||
2024 highest_note_in_selection > midi_stream_view()->highest_note()) {
2025 midi_stream_view()->set_note_range(MidiStreamView::ContentsRange);
2030 MidiRegionView::snap_pixel_to_frame(double x)
2032 PublicEditor& editor = trackview.editor();
2033 // x is region relative, convert it to global absolute frames
2034 nframes64_t frame = editor.pixel_to_frame(x) + _region->position();
2035 editor.snap_to(frame);
2036 return frame - _region->position(); // convert back to region relative
2040 MidiRegionView::snap_frame_to_frame(nframes64_t x)
2042 PublicEditor& editor = trackview.editor();
2043 // x is region relative, convert it to global absolute frames
2044 nframes64_t frame = x + _region->position();
2045 editor.snap_to(frame);
2046 return frame - _region->position(); // convert back to region relative
2050 MidiRegionView::snap_to_pixel(double x)
2052 return (double) trackview.editor().frame_to_pixel(snap_pixel_to_frame(x));
2056 MidiRegionView::get_position_pixels()
2058 nframes64_t region_frame = get_position();
2059 return trackview.editor().frame_to_pixel(region_frame);
2063 MidiRegionView::get_end_position_pixels()
2065 nframes64_t frame = get_position() + get_duration ();
2066 return trackview.editor().frame_to_pixel(frame);
2070 MidiRegionView::beats_to_frames(double beats) const
2072 return _time_converter.to(beats);
2076 MidiRegionView::frames_to_beats(nframes64_t frames) const
2078 return _time_converter.from(frames);
2082 MidiRegionView::begin_resizing (bool /*at_front*/)
2084 _resize_data.clear();
2086 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2087 CanvasNote *note = dynamic_cast<CanvasNote *> (*i);
2089 // only insert CanvasNotes into the map
2091 NoteResizeData *resize_data = new NoteResizeData();
2092 resize_data->canvas_note = note;
2094 // create a new SimpleRect from the note which will be the resize preview
2095 SimpleRect *resize_rect = new SimpleRect(
2096 *group, note->x1(), note->y1(), note->x2(), note->y2());
2098 // calculate the colors: get the color settings
2099 uint32_t fill_color = UINT_RGBA_CHANGE_A(
2100 ARDOUR_UI::config()->canvasvar_MidiNoteSelected.get(),
2103 // make the resize preview notes more transparent and bright
2104 fill_color = UINT_INTERPOLATE(fill_color, 0xFFFFFF40, 0.5);
2106 // calculate color based on note velocity
2107 resize_rect->property_fill_color_rgba() = UINT_INTERPOLATE(
2108 CanvasNoteEvent::meter_style_fill_color(note->note()->velocity(), note->selected()),
2112 resize_rect->property_outline_color_rgba() = CanvasNoteEvent::calculate_outline(
2113 ARDOUR_UI::config()->canvasvar_MidiNoteSelected.get());
2115 resize_data->resize_rect = resize_rect;
2116 _resize_data.push_back(resize_data);
2121 /** Update resizing notes while user drags.
2122 * @param primary `primary' note for the drag; ie the one that is used as the reference in non-relative mode.
2123 * @param at_front which end of the note (true == note on, false == note off)
2124 * @param delta_x change in mouse position since the start of the drag
2125 * @param relative true if relative resizing is taking place, false if absolute resizing. This only makes
2126 * a difference when multiple notes are being resized; in relative mode, each note's length is changed by the
2127 * amount of the drag. In non-relative mode, all selected notes are set to have the same start or end point
2128 * as the \a primary note.
2131 MidiRegionView::update_resizing (ArdourCanvas::CanvasNote* primary, bool at_front, double delta_x, bool relative)
2133 bool cursor_set = false;
2135 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2136 SimpleRect* resize_rect = (*i)->resize_rect;
2137 CanvasNote* canvas_note = (*i)->canvas_note;
2142 current_x = canvas_note->x1() + delta_x;
2144 current_x = primary->x1() + delta_x;
2148 current_x = canvas_note->x2() + delta_x;
2150 current_x = primary->x2() + delta_x;
2155 resize_rect->property_x1() = snap_to_pixel(current_x);
2156 resize_rect->property_x2() = canvas_note->x2();
2158 resize_rect->property_x2() = snap_to_pixel(current_x);
2159 resize_rect->property_x1() = canvas_note->x1();
2165 beats = snap_pixel_to_frame (current_x);
2166 beats = frames_to_beats (beats);
2171 if (beats < canvas_note->note()->end_time()) {
2172 len = canvas_note->note()->time() - beats;
2173 len += canvas_note->note()->length();
2178 if (beats >= canvas_note->note()->end_time()) {
2179 len = beats - canvas_note->note()->time();
2186 snprintf (buf, sizeof (buf), "%.3g beats", len);
2187 trackview.editor().show_verbose_canvas_cursor_with (buf);
2196 /** Finish resizing notes when the user releases the mouse button.
2197 * Parameters the same as for \a update_resizing().
2200 MidiRegionView::commit_resizing (ArdourCanvas::CanvasNote* primary, bool at_front, double delta_x, bool relative)
2202 start_diff_command(_("resize notes"));
2204 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2205 CanvasNote* canvas_note = (*i)->canvas_note;
2206 SimpleRect* resize_rect = (*i)->resize_rect;
2211 current_x = canvas_note->x1() + delta_x;
2213 current_x = primary->x1() + delta_x;
2217 current_x = canvas_note->x2() + delta_x;
2219 current_x = primary->x2() + delta_x;
2223 current_x = snap_pixel_to_frame (current_x);
2224 current_x = frames_to_beats (current_x);
2226 if (at_front && current_x < canvas_note->note()->end_time()) {
2227 diff_add_change (canvas_note, MidiModel::DiffCommand::StartTime, current_x);
2229 double len = canvas_note->note()->time() - current_x;
2230 len += canvas_note->note()->length();
2233 /* XXX convert to beats */
2234 diff_add_change (canvas_note, MidiModel::DiffCommand::Length, len);
2239 double len = current_x - canvas_note->note()->time();
2242 /* XXX convert to beats */
2243 diff_add_change (canvas_note, MidiModel::DiffCommand::Length, len);
2251 _resize_data.clear();
2256 MidiRegionView::change_note_channel (CanvasNoteEvent* event, int8_t channel)
2258 diff_add_change (event, MidiModel::DiffCommand::Channel, (uint8_t) channel);
2262 MidiRegionView::change_note_velocity(CanvasNoteEvent* event, int8_t velocity, bool relative)
2264 uint8_t new_velocity;
2267 new_velocity = event->note()->velocity() + velocity;
2268 clamp_to_0_127(new_velocity);
2270 new_velocity = velocity;
2273 event->set_selected (event->selected()); // change color
2275 diff_add_change (event, MidiModel::DiffCommand::Velocity, new_velocity);
2279 MidiRegionView::change_note_note (CanvasNoteEvent* event, int8_t note, bool relative)
2284 new_note = event->note()->note() + note;
2289 clamp_to_0_127 (new_note);
2290 diff_add_change (event, MidiModel::DiffCommand::NoteNumber, new_note);
2294 MidiRegionView::trim_note (CanvasNoteEvent* event, Evoral::MusicalTime front_delta, Evoral::MusicalTime end_delta)
2296 bool change_start = false;
2297 bool change_length = false;
2298 Evoral::MusicalTime new_start = 0;
2299 Evoral::MusicalTime new_length = 0;
2301 /* NOTE: the semantics of the two delta arguments are slightly subtle:
2303 front_delta: if positive - move the start of the note later in time (shortening it)
2304 if negative - move the start of the note earlier in time (lengthening it)
2306 end_delta: if positive - move the end of the note later in time (lengthening it)
2307 if negative - move the end of the note earlier in time (shortening it)
2311 if (front_delta < 0) {
2313 if (event->note()->time() < -front_delta) {
2316 new_start = event->note()->time() + front_delta; // moves earlier
2319 /* start moved toward zero, so move the end point out to where it used to be.
2320 Note that front_delta is negative, so this increases the length.
2323 new_length = event->note()->length() - front_delta;
2324 change_start = true;
2325 change_length = true;
2329 Evoral::MusicalTime new_pos = event->note()->time() + front_delta;
2331 if (new_pos < event->note()->end_time()) {
2332 new_start = event->note()->time() + front_delta;
2333 /* start moved toward the end, so move the end point back to where it used to be */
2334 new_length = event->note()->length() - front_delta;
2335 change_start = true;
2336 change_length = true;
2343 bool can_change = true;
2344 if (end_delta < 0) {
2345 if (event->note()->length() < -end_delta) {
2351 new_length = event->note()->length() + end_delta;
2352 change_length = true;
2357 diff_add_change (event, MidiModel::DiffCommand::StartTime, new_start);
2360 if (change_length) {
2361 diff_add_change (event, MidiModel::DiffCommand::Length, new_length);
2366 MidiRegionView::change_note_time (CanvasNoteEvent* event, Evoral::MusicalTime delta, bool relative)
2368 Evoral::MusicalTime new_time;
2372 if (event->note()->time() < -delta) {
2375 new_time = event->note()->time() + delta;
2378 new_time = event->note()->time() + delta;
2384 diff_add_change (event, MidiModel::DiffCommand::StartTime, new_time);
2388 MidiRegionView::change_note_length (CanvasNoteEvent* event, Evoral::MusicalTime t)
2390 diff_add_change (event, MidiModel::DiffCommand::Length, t);
2394 MidiRegionView::change_velocities (bool up, bool fine, bool allow_smush)
2398 if (_selection.empty()) {
2413 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2414 if ((*i)->note()->velocity() + delta == 0 || (*i)->note()->velocity() + delta == 127) {
2420 start_diff_command(_("change velocities"));
2422 for (Selection::iterator i = _selection.begin(); i != _selection.end();) {
2423 Selection::iterator next = i;
2425 change_note_velocity (*i, delta, true);
2431 if (!_selection.empty()) {
2433 snprintf (buf, sizeof (buf), "Vel %d",
2434 (int) (*_selection.begin())->note()->velocity());
2435 trackview.editor().show_verbose_canvas_cursor_with (buf);
2441 MidiRegionView::transpose (bool up, bool fine, bool allow_smush)
2443 if (_selection.empty()) {
2460 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2462 if ((int8_t) (*i)->note()->note() + delta <= 0) {
2466 if ((int8_t) (*i)->note()->note() + delta > 127) {
2473 start_diff_command (_("transpose"));
2475 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2476 Selection::iterator next = i;
2478 change_note_note (*i, delta, true);
2486 MidiRegionView::change_note_lengths (bool fine, bool shorter, Evoral::MusicalTime delta, bool start, bool end)
2492 /* grab the current grid distance */
2494 delta = trackview.editor().get_grid_type_as_beats (success, _region->position());
2496 /* XXX cannot get grid type as beats ... should always be possible ... FIX ME */
2497 cerr << "Grid type not available as beats - TO BE FIXED\n";
2507 start_diff_command (_("change note lengths"));
2509 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2510 Selection::iterator next = i;
2513 /* note the negation of the delta for start */
2515 trim_note (*i, (start ? -delta : 0), (end ? delta : 0));
2524 MidiRegionView::nudge_notes (bool forward)
2526 if (_selection.empty()) {
2530 /* pick a note as the point along the timeline to get the nudge distance.
2531 its not necessarily the earliest note, so we may want to pull the notes out
2532 into a vector and sort before using the first one.
2535 nframes64_t ref_point = _region->position() + beats_to_frames ((*(_selection.begin()))->note()->time());
2537 nframes64_t distance;
2539 if (trackview.editor().snap_mode() == Editing::SnapOff) {
2541 /* grid is off - use nudge distance */
2543 distance = trackview.editor().get_nudge_distance (ref_point, unused);
2549 nframes64_t next_pos = ref_point;
2552 /* XXX need check on max_frames, but that needs max_frames64 or something */
2555 if (next_pos == 0) {
2561 trackview.editor().snap_to (next_pos, (forward ? 1 : -1), false);
2562 distance = ref_point - next_pos;
2565 if (distance == 0) {
2569 Evoral::MusicalTime delta = frames_to_beats (fabs (distance));
2575 start_diff_command (_("nudge"));
2577 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2578 Selection::iterator next = i;
2580 change_note_time (*i, delta, true);
2588 MidiRegionView::change_channel(uint8_t channel)
2590 start_diff_command(_("change channel"));
2591 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2592 diff_add_change (*i, MidiModel::DiffCommand::Channel, channel);
2600 MidiRegionView::note_entered(ArdourCanvas::CanvasNoteEvent* ev)
2602 if (_mouse_state == SelectTouchDragging) {
2603 note_selected (ev, true);
2606 show_verbose_canvas_cursor (ev->note ());
2610 MidiRegionView::note_left (ArdourCanvas::CanvasNoteEvent* note)
2612 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2613 (*i)->hide_velocity ();
2616 trackview.editor().hide_verbose_canvas_cursor ();
2620 MidiRegionView::switch_source(boost::shared_ptr<Source> src)
2622 boost::shared_ptr<MidiSource> msrc = boost::dynamic_pointer_cast<MidiSource>(src);
2624 display_model(msrc->model());
2628 MidiRegionView::set_frame_color()
2631 if (_selected && should_show_selection) {
2632 frame->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_SelectedFrameBase.get();
2634 frame->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_MidiFrameBase.get();
2640 MidiRegionView::midi_channel_mode_changed(ChannelMode mode, uint16_t mask)
2644 case FilterChannels:
2645 _force_channel = -1;
2648 _force_channel = mask;
2649 mask = 0xFFFF; // Show all notes as active (below)
2652 // Update notes for selection
2653 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2654 (*i)->on_channel_selection_change(mask);
2657 _last_channel_selection = mask;
2661 MidiRegionView::midi_patch_settings_changed(std::string model, std::string custom_device_mode)
2663 _model_name = model;
2664 _custom_device_mode = custom_device_mode;
2669 MidiRegionView::cut_copy_clear (Editing::CutCopyOp op)
2671 if (_selection.empty()) {
2675 PublicEditor& editor (trackview.editor());
2680 editor.get_cut_buffer().add (selection_as_cut_buffer());
2688 start_diff_command();
2690 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2696 diff_remove_note (*i);
2706 MidiRegionView::selection_as_cut_buffer () const
2710 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2711 NoteType* n = (*i)->note().get();
2712 notes.insert (boost::shared_ptr<NoteType> (new NoteType (*n)));
2715 MidiCutBuffer* cb = new MidiCutBuffer (trackview.session());
2722 MidiRegionView::paste (nframes64_t pos, float times, const MidiCutBuffer& mcb)
2728 start_diff_command (_("paste"));
2730 Evoral::MusicalTime beat_delta;
2731 Evoral::MusicalTime paste_pos_beats;
2732 Evoral::MusicalTime duration;
2733 Evoral::MusicalTime end_point = 0;
2735 duration = (*mcb.notes().rbegin())->end_time() - (*mcb.notes().begin())->time();
2736 paste_pos_beats = frames_to_beats (pos - _region->position());
2737 beat_delta = (*mcb.notes().begin())->time() - paste_pos_beats;
2738 paste_pos_beats = 0;
2740 _selection.clear ();
2742 for (int n = 0; n < (int) times; ++n) {
2744 for (Notes::const_iterator i = mcb.notes().begin(); i != mcb.notes().end(); ++i) {
2746 boost::shared_ptr<NoteType> copied_note (new NoteType (*((*i).get())));
2747 copied_note->set_time (paste_pos_beats + copied_note->time() - beat_delta);
2749 /* make all newly added notes selected */
2751 diff_add_note (copied_note, true);
2752 end_point = copied_note->end_time();
2755 paste_pos_beats += duration;
2758 /* if we pasted past the current end of the region, extend the region */
2760 nframes64_t end_frame = _region->position() + beats_to_frames (end_point);
2761 nframes64_t region_end = _region->position() + _region->length() - 1;
2763 if (end_frame > region_end) {
2765 trackview.session()->begin_reversible_command (_("paste"));
2767 _region->clear_history ();
2768 _region->set_length (end_frame, this);
2769 trackview.session()->add_command (new StatefulDiffCommand (_region));
2775 struct EventNoteTimeEarlyFirstComparator {
2776 bool operator() (CanvasNoteEvent* a, CanvasNoteEvent* b) {
2777 return a->note()->time() < b->note()->time();
2782 MidiRegionView::time_sort_events ()
2784 if (!_sort_needed) {
2788 EventNoteTimeEarlyFirstComparator cmp;
2791 _sort_needed = false;
2795 MidiRegionView::goto_next_note ()
2797 // nframes64_t pos = -1;
2798 bool use_next = false;
2800 if (_events.back()->selected()) {
2804 time_sort_events ();
2806 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2807 if ((*i)->selected()) {
2810 } else if (use_next) {
2812 // pos = _region->position() + beats_to_frames ((*i)->note()->time());
2817 /* use the first one */
2819 unique_select (_events.front());
2824 MidiRegionView::goto_previous_note ()
2826 // nframes64_t pos = -1;
2827 bool use_next = false;
2829 if (_events.front()->selected()) {
2833 time_sort_events ();
2835 for (Events::reverse_iterator i = _events.rbegin(); i != _events.rend(); ++i) {
2836 if ((*i)->selected()) {
2839 } else if (use_next) {
2841 // pos = _region->position() + beats_to_frames ((*i)->note()->time());
2846 /* use the last one */
2848 unique_select (*(_events.rbegin()));
2852 MidiRegionView::selection_as_notelist (Notes& selected, bool allow_all_if_none_selected)
2854 bool had_selected = false;
2856 time_sort_events ();
2858 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2859 if ((*i)->selected()) {
2860 selected.insert ((*i)->note());
2861 had_selected = true;
2865 if (allow_all_if_none_selected && !had_selected) {
2866 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2867 selected.insert ((*i)->note());
2873 MidiRegionView::update_ghost_note (double x, double y)
2879 nframes64_t f = trackview.editor().pixel_to_frame (x) + _region->position ();
2880 trackview.editor().snap_to (f);
2881 f -= _region->position ();
2884 Evoral::MusicalTime beats = trackview.editor().get_grid_type_as_beats (success, f);
2889 double length = frames_to_beats (snap_frame_to_frame (f + beats_to_frames (beats)) - f);
2891 _ghost_note->note()->set_time (frames_to_beats (f + _region->start()));
2892 _ghost_note->note()->set_length (length);
2893 _ghost_note->note()->set_note (midi_stream_view()->y_to_note (y));
2895 update_note (_ghost_note);
2897 show_verbose_canvas_cursor (_ghost_note->note ());
2901 MidiRegionView::create_ghost_note (double x, double y)
2906 boost::shared_ptr<NoteType> g (new NoteType);
2907 _ghost_note = new NoEventCanvasNote (*this, *group, g);
2908 update_ghost_note (x, y);
2909 _ghost_note->show ();
2914 show_verbose_canvas_cursor (_ghost_note->note ());
2918 MidiRegionView::snap_changed ()
2924 create_ghost_note (_last_ghost_x, _last_ghost_y);
2928 MidiRegionView::show_verbose_canvas_cursor (boost::shared_ptr<NoteType> n) const
2931 snprintf (buf, sizeof (buf), "%s (%d)\nVel %d",
2932 Evoral::midi_note_name (n->note()).c_str(),
2934 (int) n->velocity());
2935 trackview.editor().show_verbose_canvas_cursor_with (buf);
2939 MidiRegionView::drop_down_keys ()
2941 _mouse_state = None;
2945 MidiRegionView::maybe_select_by_position (GdkEventButton* ev, double x, double y)
2947 double note = midi_stream_view()->y_to_note(y);
2949 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
2951 cerr << "Selecting by position\n";
2953 uint16_t chn_mask = mtv->channel_selector().get_selected_channels();
2955 if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
2956 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchGreaterThanOrEqual, (uint8_t) floor (note), chn_mask);
2957 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
2958 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchLessThanOrEqual, (uint8_t) floor (note), chn_mask);
2963 bool add_mrv_selection = false;
2965 if (_selection.empty()) {
2966 add_mrv_selection = true;
2969 for (Events::iterator i = e.begin(); i != e.end(); ++i) {
2970 if (_selection.insert (*i).second) {
2971 (*i)->set_selected (true);
2975 if (add_mrv_selection) {
2976 PublicEditor& editor (trackview.editor());
2977 editor.get_selection().add (this);
2982 MidiRegionView::color_handler ()
2984 RegionView::color_handler ();
2986 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2987 (*i)->set_selected ((*i)->selected()); // will change color
2990 /* XXX probably more to do here */
2994 MidiRegionView::enable_display (bool yn)
2996 RegionView::enable_display (yn);
3003 MidiRegionView::show_step_edit_cursor (Evoral::MusicalTime pos)
3005 if (_step_edit_cursor == 0) {
3006 ArdourCanvas::Group* const group = (ArdourCanvas::Group*)get_canvas_group();
3008 _step_edit_cursor = new ArdourCanvas::SimpleRect (*group);
3009 _step_edit_cursor->property_y1() = 0;
3010 _step_edit_cursor->property_y2() = midi_stream_view()->contents_height();
3011 _step_edit_cursor->property_fill_color_rgba() = RGBA_TO_UINT (45,0,0,90);
3012 _step_edit_cursor->property_outline_color_rgba() = RGBA_TO_UINT (85,0,0,90);
3015 move_step_edit_cursor (pos);
3016 _step_edit_cursor->show ();
3020 MidiRegionView::move_step_edit_cursor (Evoral::MusicalTime pos)
3022 _step_edit_cursor_position = pos;
3024 if (_step_edit_cursor) {
3025 double pixel = trackview.editor().frame_to_pixel (beats_to_frames (pos));
3026 _step_edit_cursor->property_x1() = pixel;
3027 set_step_edit_cursor_width (_step_edit_cursor_width);
3032 MidiRegionView::hide_step_edit_cursor ()
3034 if (_step_edit_cursor) {
3035 _step_edit_cursor->hide ();
3040 MidiRegionView::set_step_edit_cursor_width (Evoral::MusicalTime beats)
3042 _step_edit_cursor_width = beats;
3044 if (_step_edit_cursor) {
3045 _step_edit_cursor->property_x2() = _step_edit_cursor->property_x1() + trackview.editor().frame_to_pixel (beats_to_frames (beats));