2 Copyright (C) 2001-2011 Paul Davis
3 Author: David Robillard
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/midi_region.h"
35 #include "ardour/midi_source.h"
36 #include "ardour/midi_model.h"
37 #include "ardour/midi_patch_manager.h"
38 #include "ardour/session.h"
40 #include "evoral/Parameter.hpp"
41 #include "evoral/MIDIParameters.hpp"
42 #include "evoral/MIDIEvent.hpp"
43 #include "evoral/Control.hpp"
44 #include "evoral/midi_util.h"
46 #include "automation_region_view.h"
47 #include "automation_time_axis.h"
48 #include "canvas-hit.h"
49 #include "canvas-note.h"
50 #include "canvas_patch_change.h"
53 #include "editor_drag.h"
54 #include "ghostregion.h"
55 #include "gui_thread.h"
57 #include "midi_channel_dialog.h"
58 #include "midi_cut_buffer.h"
59 #include "midi_list_editor.h"
60 #include "midi_region_view.h"
61 #include "midi_streamview.h"
62 #include "midi_time_axis.h"
63 #include "midi_util.h"
64 #include "midi_velocity_dialog.h"
65 #include "mouse_cursors.h"
66 #include "note_player.h"
67 #include "public_editor.h"
68 #include "rgb_macros.h"
69 #include "selection.h"
70 #include "simpleline.h"
71 #include "streamview.h"
73 #include "patch_change_dialog.h"
74 #include "verbose_cursor.h"
78 using namespace ARDOUR;
80 using namespace Editing;
81 using namespace ArdourCanvas;
82 using Gtkmm2ext::Keyboard;
84 PBD::Signal1<void, MidiRegionView *> MidiRegionView::SelectionCleared;
86 #define MIDI_BP_ZERO ((Config->get_first_midi_bank_is_zero())?0:1)
88 MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &tv,
89 boost::shared_ptr<MidiRegion> r, double spu, Gdk::Color const & basic_color)
90 : RegionView (parent, tv, r, spu, basic_color)
91 , _last_channel_selection(0xFFFF)
92 , _current_range_min(0)
93 , _current_range_max(0)
95 , _note_group(new ArdourCanvas::Group(*group))
96 , _note_diff_command (0)
98 , _step_edit_cursor (0)
99 , _step_edit_cursor_width (1.0)
100 , _step_edit_cursor_position (0.0)
101 , _channel_selection_scoped_note (0)
102 , _temporary_note_group (0)
105 , _sort_needed (true)
106 , _optimization_iterator (_events.end())
108 , _no_sound_notes (false)
111 , pre_enter_cursor (0)
112 , pre_press_cursor (0)
115 _note_group->raise_to_top();
116 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
118 Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&MidiRegionView::parameter_changed, this, _1), gui_context());
119 connect_to_diskstream ();
121 SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), boost::bind (&MidiRegionView::selection_cleared, this, _1), gui_context ());
124 MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &tv,
125 boost::shared_ptr<MidiRegion> r, double spu, Gdk::Color& basic_color,
126 TimeAxisViewItem::Visibility visibility)
127 : RegionView (parent, tv, r, spu, basic_color, false, visibility)
128 , _last_channel_selection(0xFFFF)
129 , _current_range_min(0)
130 , _current_range_max(0)
132 , _note_group(new ArdourCanvas::Group(*parent))
133 , _note_diff_command (0)
135 , _step_edit_cursor (0)
136 , _step_edit_cursor_width (1.0)
137 , _step_edit_cursor_position (0.0)
138 , _channel_selection_scoped_note (0)
139 , _temporary_note_group (0)
142 , _sort_needed (true)
143 , _optimization_iterator (_events.end())
145 , _no_sound_notes (false)
148 , pre_enter_cursor (0)
149 , pre_press_cursor (0)
152 _note_group->raise_to_top();
153 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
155 connect_to_diskstream ();
157 SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), boost::bind (&MidiRegionView::selection_cleared, this, _1), gui_context ());
161 MidiRegionView::parameter_changed (std::string const & p)
163 if (p == "diplay-first-midi-bank-as-zero") {
164 if (_enable_display) {
170 MidiRegionView::MidiRegionView (const MidiRegionView& other)
171 : sigc::trackable(other)
173 , _last_channel_selection(0xFFFF)
174 , _current_range_min(0)
175 , _current_range_max(0)
177 , _note_group(new ArdourCanvas::Group(*get_canvas_group()))
178 , _note_diff_command (0)
180 , _step_edit_cursor (0)
181 , _step_edit_cursor_width (1.0)
182 , _step_edit_cursor_position (0.0)
183 , _channel_selection_scoped_note (0)
184 , _temporary_note_group (0)
187 , _sort_needed (true)
188 , _optimization_iterator (_events.end())
190 , _no_sound_notes (false)
193 , pre_enter_cursor (0)
194 , pre_press_cursor (0)
200 UINT_TO_RGBA (other.fill_color, &r, &g, &b, &a);
201 c.set_rgb_p (r/255.0, g/255.0, b/255.0);
206 MidiRegionView::MidiRegionView (const MidiRegionView& other, boost::shared_ptr<MidiRegion> region)
207 : RegionView (other, boost::shared_ptr<Region> (region))
208 , _last_channel_selection(0xFFFF)
209 , _current_range_min(0)
210 , _current_range_max(0)
212 , _note_group(new ArdourCanvas::Group(*get_canvas_group()))
213 , _note_diff_command (0)
215 , _step_edit_cursor (0)
216 , _step_edit_cursor_width (1.0)
217 , _step_edit_cursor_position (0.0)
218 , _channel_selection_scoped_note (0)
219 , _temporary_note_group (0)
222 , _sort_needed (true)
223 , _optimization_iterator (_events.end())
225 , _no_sound_notes (false)
228 , pre_enter_cursor (0)
229 , pre_press_cursor (0)
235 UINT_TO_RGBA (other.fill_color, &r, &g, &b, &a);
236 c.set_rgb_p (r/255.0, g/255.0, b/255.0);
242 MidiRegionView::init (Gdk::Color const & basic_color, bool wfd)
244 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
246 CanvasNoteEvent::CanvasNoteEventDeleted.connect (note_delete_connection, MISSING_INVALIDATOR,
247 boost::bind (&MidiRegionView::maybe_remove_deleted_note_from_selection, this, _1),
251 midi_region()->midi_source(0)->load_model();
254 _model = midi_region()->midi_source(0)->model();
255 _enable_display = false;
257 RegionView::init (basic_color, false);
259 compute_colors (basic_color);
261 set_height (trackview.current_height());
264 region_sync_changed ();
265 region_resized (ARDOUR::bounds_change);
270 _enable_display = true;
273 display_model (_model);
277 reset_width_dependent_items (_pixel_width);
279 group->raise_to_top();
280 group->signal_event().connect(
281 sigc::mem_fun(this, &MidiRegionView::canvas_event), false);
283 midi_view()->signal_channel_mode_changed().connect(
284 sigc::mem_fun(this, &MidiRegionView::midi_channel_mode_changed));
286 instrument_info().Changed.connect (_instrument_changed_connection, invalidator (*this),
287 boost::bind (&MidiRegionView::instrument_settings_changed, this), gui_context());
289 trackview.editor().SnapChanged.connect(snap_changed_connection, invalidator(*this),
290 boost::bind (&MidiRegionView::snap_changed, this),
293 Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&MidiRegionView::parameter_changed, this, _1), gui_context());
294 connect_to_diskstream ();
296 SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), boost::bind (&MidiRegionView::selection_cleared, this, _1), gui_context ());
300 MidiRegionView::instrument_info () const
302 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
303 return route_ui->route()->instrument_info();
306 const boost::shared_ptr<ARDOUR::MidiRegion>
307 MidiRegionView::midi_region() const
309 return boost::dynamic_pointer_cast<ARDOUR::MidiRegion>(_region);
313 MidiRegionView::connect_to_diskstream ()
315 midi_view()->midi_track()->DataRecorded.connect(
316 *this, invalidator(*this),
317 boost::bind (&MidiRegionView::data_recorded, this, _1),
322 MidiRegionView::canvas_event(GdkEvent* ev)
327 case GDK_ENTER_NOTIFY:
328 case GDK_LEAVE_NOTIFY:
329 _last_event_x = ev->crossing.x;
330 _last_event_y = ev->crossing.y;
332 case GDK_MOTION_NOTIFY:
333 _last_event_x = ev->motion.x;
334 _last_event_y = ev->motion.y;
340 if (ev->type == GDK_2BUTTON_PRESS) {
341 return trackview.editor().toggle_internal_editing_from_double_click (ev);
344 if ((!trackview.editor().internal_editing() && trackview.editor().current_mouse_mode() != MouseGain) ||
345 (trackview.editor().current_mouse_mode() == MouseTimeFX) ||
346 (trackview.editor().current_mouse_mode() == MouseZoom)) {
347 // handle non-draw modes elsewhere
353 return scroll (&ev->scroll);
356 return key_press (&ev->key);
358 case GDK_KEY_RELEASE:
359 return key_release (&ev->key);
361 case GDK_BUTTON_PRESS:
362 return button_press (&ev->button);
364 case GDK_BUTTON_RELEASE:
365 r = button_release (&ev->button);
370 case GDK_ENTER_NOTIFY:
371 return enter_notify (&ev->crossing);
373 case GDK_LEAVE_NOTIFY:
374 return leave_notify (&ev->crossing);
376 case GDK_MOTION_NOTIFY:
377 return motion (&ev->motion);
387 MidiRegionView::remove_ghost_note ()
394 MidiRegionView::enter_notify (GdkEventCrossing* ev)
396 trackview.editor().MouseModeChanged.connect (
397 _mouse_mode_connection, invalidator (*this), boost::bind (&MidiRegionView::mouse_mode_changed, this), gui_context ()
400 if (trackview.editor().current_mouse_mode() == MouseDraw && _mouse_state != AddDragging) {
401 create_ghost_note (ev->x, ev->y);
404 if (!trackview.editor().internal_editing()) {
405 Keyboard::magic_widget_drop_focus();
407 Keyboard::magic_widget_grab_focus();
411 // if current operation is non-operational in a midi region, change the cursor to so indicate
412 if (trackview.editor().current_mouse_mode() == MouseGain) {
413 Editor* editor = dynamic_cast<Editor *> (&trackview.editor());
414 pre_enter_cursor = editor->get_canvas_cursor();
415 editor->set_canvas_cursor(editor->cursors()->timebar);
422 MidiRegionView::leave_notify (GdkEventCrossing*)
424 _mouse_mode_connection.disconnect ();
426 trackview.editor().verbose_cursor()->hide ();
427 remove_ghost_note ();
429 if (pre_enter_cursor) {
430 Editor* editor = dynamic_cast<Editor *> (&trackview.editor());
431 editor->set_canvas_cursor(pre_enter_cursor);
438 MidiRegionView::mouse_mode_changed ()
440 if (trackview.editor().current_mouse_mode() == MouseDraw && trackview.editor().internal_editing()) {
441 create_ghost_note (_last_event_x, _last_event_y);
443 remove_ghost_note ();
444 trackview.editor().verbose_cursor()->hide ();
447 if (!trackview.editor().internal_editing()) {
448 Keyboard::magic_widget_drop_focus();
450 Keyboard::magic_widget_grab_focus();
456 MidiRegionView::button_press (GdkEventButton* ev)
458 if (ev->button != 1) {
462 Editor* editor = dynamic_cast<Editor *> (&trackview.editor());
463 MouseMode m = editor->current_mouse_mode();
465 if (m == MouseObject && Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())) {
466 pre_press_cursor = editor->get_canvas_cursor ();
467 editor->set_canvas_cursor (editor->cursors()->midi_pencil);
470 if (_mouse_state != SelectTouchDragging) {
472 _pressed_button = ev->button;
473 _mouse_state = Pressed;
478 _pressed_button = ev->button;
484 MidiRegionView::button_release (GdkEventButton* ev)
486 double event_x, event_y;
488 if (ev->button != 1) {
495 group->w2i(event_x, event_y);
496 group->ungrab(ev->time);
498 PublicEditor& editor = trackview.editor ();
500 if (pre_press_cursor) {
501 dynamic_cast<Editor*>(&editor)->set_canvas_cursor (pre_press_cursor, false);
502 pre_press_cursor = 0;
505 switch (_mouse_state) {
506 case Pressed: // Clicked
508 switch (editor.current_mouse_mode()) {
510 /* no motion occured - simple click */
519 if (Keyboard::is_insert_note_event(ev)) {
521 double event_x, event_y;
525 group->w2i(event_x, event_y);
528 Evoral::MusicalTime beats = editor.get_grid_type_as_beats (success, editor.pixel_to_frame (event_x));
534 /* Shorten the length by 1 tick so that we can add a new note at the next
535 grid snap without it overlapping this one.
537 beats -= 1.0 / Timecode::BBT_Time::ticks_per_beat;
539 create_note_at (editor.pixel_to_frame (event_x), event_y, beats, true);
547 Evoral::MusicalTime beats = editor.get_grid_type_as_beats (success, editor.pixel_to_frame (event_x));
553 /* Shorten the length by 1 tick so that we can add a new note at the next
554 grid snap without it overlapping this one.
556 beats -= 1.0 / Timecode::BBT_Time::ticks_per_beat;
558 create_note_at (editor.pixel_to_frame (event_x), event_y, beats, true);
569 case SelectRectDragging:
571 editor.drags()->end_grab ((GdkEvent *) ev);
573 create_ghost_note (ev->x, ev->y);
585 MidiRegionView::motion (GdkEventMotion* ev)
587 PublicEditor& editor = trackview.editor ();
589 if (!_ghost_note && editor.current_mouse_mode() == MouseObject &&
590 Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier()) &&
591 _mouse_state != AddDragging) {
593 create_ghost_note (ev->x, ev->y);
595 } else if (_ghost_note && editor.current_mouse_mode() == MouseObject &&
596 Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())) {
598 update_ghost_note (ev->x, ev->y);
600 } else if (_ghost_note && editor.current_mouse_mode() == MouseObject) {
602 remove_ghost_note ();
603 editor.verbose_cursor()->hide ();
605 } else if (_ghost_note && editor.current_mouse_mode() == MouseDraw) {
607 update_ghost_note (ev->x, ev->y);
610 /* any motion immediately hides velocity text that may have been visible */
612 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
613 (*i)->hide_velocity ();
616 switch (_mouse_state) {
619 if (_pressed_button == 1) {
621 MouseMode m = editor.current_mouse_mode();
623 if (m == MouseDraw || (m == MouseObject && Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier()))) {
625 editor.drags()->set (new NoteCreateDrag (dynamic_cast<Editor *> (&editor), group, this), (GdkEvent *) ev);
626 _mouse_state = AddDragging;
627 remove_ghost_note ();
628 editor.verbose_cursor()->hide ();
630 } else if (m == MouseObject) {
631 editor.drags()->set (new MidiRubberbandSelectDrag (dynamic_cast<Editor *> (&editor), this), (GdkEvent *) ev);
633 _mouse_state = SelectRectDragging;
635 } else if (m == MouseRange) {
636 editor.drags()->set (new MidiVerticalSelectDrag (dynamic_cast<Editor *> (&editor), this), (GdkEvent *) ev);
637 _mouse_state = SelectVerticalDragging;
644 case SelectRectDragging:
645 case SelectVerticalDragging:
647 editor.drags()->motion_handler ((GdkEvent *) ev, false);
650 case SelectTouchDragging:
662 MidiRegionView::scroll (GdkEventScroll* ev)
664 if (_selection.empty()) {
668 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
669 /* XXX: bit of a hack; allow PrimaryModifier scroll through so that
670 it still works for zoom.
675 trackview.editor().verbose_cursor()->hide ();
677 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
678 bool together = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
680 if (ev->direction == GDK_SCROLL_UP) {
681 change_velocities (true, fine, false, together);
682 } else if (ev->direction == GDK_SCROLL_DOWN) {
683 change_velocities (false, fine, false, together);
689 MidiRegionView::key_press (GdkEventKey* ev)
691 /* since GTK bindings are generally activated on press, and since
692 detectable auto-repeat is the name of the game and only sends
693 repeated presses, carry out key actions at key press, not release.
696 bool unmodified = Keyboard::no_modifier_keys_pressed (ev);
698 if (unmodified && (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R)) {
699 _mouse_state = SelectTouchDragging;
702 } else if (ev->keyval == GDK_Escape && unmodified) {
706 } else if (unmodified && (ev->keyval == GDK_comma || ev->keyval == GDK_period)) {
708 bool start = (ev->keyval == GDK_comma);
709 bool end = (ev->keyval == GDK_period);
710 bool shorter = Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier);
711 bool fine = Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
713 change_note_lengths (fine, shorter, 0.0, start, end);
717 } else if (ev->keyval == GDK_Delete && unmodified) {
722 } else if (ev->keyval == GDK_Tab) {
724 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
725 goto_previous_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
727 goto_next_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
731 } else if (ev->keyval == GDK_ISO_Left_Tab) {
733 /* Shift-TAB generates ISO Left Tab, for some reason */
735 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
736 goto_previous_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
738 goto_next_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
744 } else if (ev->keyval == GDK_Up) {
746 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
747 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
748 bool together = Keyboard::modifier_state_contains (ev->state, Keyboard::Level4Modifier);
750 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
751 change_velocities (true, fine, allow_smush, together);
753 transpose (true, fine, allow_smush);
757 } else if (ev->keyval == GDK_Down) {
759 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
760 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
761 bool together = Keyboard::modifier_state_contains (ev->state, Keyboard::Level4Modifier);
763 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
764 change_velocities (false, fine, allow_smush, together);
766 transpose (false, fine, allow_smush);
770 } else if (ev->keyval == GDK_Left && unmodified) {
775 } else if (ev->keyval == GDK_Right && unmodified) {
780 } else if (ev->keyval == GDK_c && unmodified) {
784 } else if (ev->keyval == GDK_v && unmodified) {
793 MidiRegionView::key_release (GdkEventKey* ev)
795 if ((_mouse_state == SelectTouchDragging) && (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R)) {
803 MidiRegionView::channel_edit ()
805 if (_selection.empty()) {
809 /* pick a note somewhat at random (since Selection is a set<>) to
810 * provide the "current" channel for the dialog.
813 uint8_t current_channel = (*_selection.begin())->note()->channel ();
814 MidiChannelDialog channel_dialog (current_channel);
815 int ret = channel_dialog.run ();
818 case Gtk::RESPONSE_OK:
824 uint8_t new_channel = channel_dialog.active_channel ();
826 start_note_diff_command (_("channel edit"));
828 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
829 Selection::iterator next = i;
831 change_note_channel (*i, new_channel);
839 MidiRegionView::velocity_edit ()
841 if (_selection.empty()) {
845 /* pick a note somewhat at random (since Selection is a set<>) to
846 * provide the "current" velocity for the dialog.
849 uint8_t current_velocity = (*_selection.begin())->note()->velocity ();
850 MidiVelocityDialog velocity_dialog (current_velocity);
851 int ret = velocity_dialog.run ();
854 case Gtk::RESPONSE_OK:
860 uint8_t new_velocity = velocity_dialog.velocity ();
862 start_note_diff_command (_("velocity edit"));
864 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
865 Selection::iterator next = i;
867 change_note_velocity (*i, new_velocity, false);
875 MidiRegionView::show_list_editor ()
878 _list_editor = new MidiListEditor (trackview.session(), midi_region(), midi_view()->midi_track());
880 _list_editor->present ();
883 /** Add a note to the model, and the view, at a canvas (click) coordinate.
884 * \param t time in frames relative to the position of the region
885 * \param y vertical position in pixels
886 * \param length duration of the note in beats
887 * \param snap_t true to snap t to the grid, otherwise false.
890 MidiRegionView::create_note_at (framepos_t t, double y, double length, bool snap_t)
892 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
893 MidiStreamView* const view = mtv->midi_view();
895 double note = view->y_to_note(y);
898 assert(note <= 127.0);
900 // Start of note in frames relative to region start
902 framecnt_t grid_frames;
903 t = snap_frame_to_grid_underneath (t, grid_frames);
907 assert (length != 0);
909 const boost::shared_ptr<NoteType> new_note (new NoteType (mtv->get_channel_for_add (),
910 region_frames_to_region_beats(t + _region->start()),
912 (uint8_t)note, 0x40));
914 if (_model->contains (new_note)) {
918 view->update_note_range(new_note->note());
920 MidiModel::NoteDiffCommand* cmd = _model->new_note_diff_command(_("add note"));
922 _model->apply_command(*trackview.session(), cmd);
924 play_midi_note (new_note);
928 MidiRegionView::clear_events()
933 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
934 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
939 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
944 _patch_changes.clear();
946 _optimization_iterator = _events.end();
950 MidiRegionView::display_model(boost::shared_ptr<MidiModel> model)
954 content_connection.disconnect ();
955 _model->ContentsChanged.connect (content_connection, invalidator (*this), boost::bind (&MidiRegionView::redisplay_model, this), gui_context());
959 if (_enable_display) {
965 MidiRegionView::start_note_diff_command (string name)
967 if (!_note_diff_command) {
968 _note_diff_command = _model->new_note_diff_command (name);
973 MidiRegionView::note_diff_add_note (const boost::shared_ptr<NoteType> note, bool selected, bool show_velocity)
975 if (_note_diff_command) {
976 _note_diff_command->add (note);
979 _marked_for_selection.insert(note);
982 _marked_for_velocity.insert(note);
987 MidiRegionView::note_diff_remove_note (ArdourCanvas::CanvasNoteEvent* ev)
989 if (_note_diff_command && ev->note()) {
990 _note_diff_command->remove(ev->note());
995 MidiRegionView::note_diff_add_change (ArdourCanvas::CanvasNoteEvent* ev,
996 MidiModel::NoteDiffCommand::Property property,
999 if (_note_diff_command) {
1000 _note_diff_command->change (ev->note(), property, val);
1005 MidiRegionView::note_diff_add_change (ArdourCanvas::CanvasNoteEvent* ev,
1006 MidiModel::NoteDiffCommand::Property property,
1007 Evoral::MusicalTime val)
1009 if (_note_diff_command) {
1010 _note_diff_command->change (ev->note(), property, val);
1015 MidiRegionView::apply_diff (bool as_subcommand)
1019 if (!_note_diff_command) {
1023 if ((add_or_remove = _note_diff_command->adds_or_removes())) {
1024 // Mark all selected notes for selection when model reloads
1025 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1026 _marked_for_selection.insert((*i)->note());
1030 if (as_subcommand) {
1031 _model->apply_command_as_subcommand (*trackview.session(), _note_diff_command);
1033 _model->apply_command (*trackview.session(), _note_diff_command);
1036 _note_diff_command = 0;
1037 midi_view()->midi_track()->playlist_modified();
1039 if (add_or_remove) {
1040 _marked_for_selection.clear();
1043 _marked_for_velocity.clear();
1047 MidiRegionView::abort_command()
1049 delete _note_diff_command;
1050 _note_diff_command = 0;
1055 MidiRegionView::find_canvas_note (boost::shared_ptr<NoteType> note)
1057 if (_optimization_iterator != _events.end()) {
1058 ++_optimization_iterator;
1061 if (_optimization_iterator != _events.end() && (*_optimization_iterator)->note() == note) {
1062 return *_optimization_iterator;
1065 for (_optimization_iterator = _events.begin(); _optimization_iterator != _events.end(); ++_optimization_iterator) {
1066 if ((*_optimization_iterator)->note() == note) {
1067 return *_optimization_iterator;
1075 MidiRegionView::get_events (Events& e, Evoral::Sequence<Evoral::MusicalTime>::NoteOperator op, uint8_t val, int chan_mask)
1077 MidiModel::Notes notes;
1078 _model->get_notes (notes, op, val, chan_mask);
1080 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1081 CanvasNoteEvent* cne = find_canvas_note (*n);
1089 MidiRegionView::redisplay_model()
1091 // Don't redisplay the model if we're currently recording and displaying that
1092 if (_active_notes) {
1100 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1101 (*i)->invalidate ();
1104 MidiModel::ReadLock lock(_model->read_lock());
1106 MidiModel::Notes& notes (_model->notes());
1107 _optimization_iterator = _events.begin();
1109 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1111 boost::shared_ptr<NoteType> note (*n);
1112 CanvasNoteEvent* cne;
1115 if (note_in_region_range (note, visible)) {
1117 if ((cne = find_canvas_note (note)) != 0) {
1124 if ((cn = dynamic_cast<CanvasNote*>(cne)) != 0) {
1126 } else if ((ch = dynamic_cast<CanvasHit*>(cne)) != 0) {
1138 add_note (note, visible);
1143 if ((cne = find_canvas_note (note)) != 0) {
1151 /* remove note items that are no longer valid */
1153 for (Events::iterator i = _events.begin(); i != _events.end(); ) {
1154 if (!(*i)->valid ()) {
1156 for (vector<GhostRegion*>::iterator j = ghosts.begin(); j != ghosts.end(); ++j) {
1157 MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*j);
1159 gr->remove_note (*i);
1164 i = _events.erase (i);
1171 _patch_changes.clear();
1175 display_patch_changes ();
1177 _marked_for_selection.clear ();
1178 _marked_for_velocity.clear ();
1180 /* we may have caused _events to contain things out of order (e.g. if a note
1181 moved earlier or later). we don't generally need them in time order, but
1182 make a note that a sort is required for those cases that require it.
1185 _sort_needed = true;
1189 MidiRegionView::display_patch_changes ()
1191 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1192 uint16_t chn_mask = mtv->channel_selector().get_selected_channels();
1194 for (uint8_t i = 0; i < 16; ++i) {
1195 display_patch_changes_on_channel (i, chn_mask & (1 << i));
1199 /** @param active_channel true to display patch changes fully, false to display
1200 * them `greyed-out' (as on an inactive channel)
1203 MidiRegionView::display_patch_changes_on_channel (uint8_t channel, bool active_channel)
1205 for (MidiModel::PatchChanges::const_iterator i = _model->patch_changes().begin(); i != _model->patch_changes().end(); ++i) {
1207 if ((*i)->channel() != channel) {
1211 string patch_name = instrument_info().get_patch_name ((*i)->bank(), (*i)->program(), channel);
1212 add_canvas_patch_change (*i, patch_name, active_channel);
1217 MidiRegionView::display_sysexes()
1219 bool have_periodic_system_messages = false;
1220 bool display_periodic_messages = true;
1222 if (!Config->get_never_display_periodic_midi()) {
1224 for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
1225 const boost::shared_ptr<const Evoral::MIDIEvent<Evoral::MusicalTime> > mev =
1226 boost::static_pointer_cast<const Evoral::MIDIEvent<Evoral::MusicalTime> > (*i);
1229 if (mev->is_spp() || mev->is_mtc_quarter() || mev->is_mtc_full()) {
1230 have_periodic_system_messages = true;
1236 if (have_periodic_system_messages) {
1237 double zoom = trackview.editor().get_current_zoom (); // frames per pixel
1239 /* get an approximate value for the number of samples per video frame */
1241 double video_frame = trackview.session()->frame_rate() * (1.0/30);
1243 /* if we are zoomed out beyond than the cutoff (i.e. more
1244 * frames per pixel than frames per 4 video frames), don't
1245 * show periodic sysex messages.
1248 if (zoom > (video_frame*4)) {
1249 display_periodic_messages = false;
1253 display_periodic_messages = false;
1256 for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
1258 const boost::shared_ptr<const Evoral::MIDIEvent<Evoral::MusicalTime> > mev =
1259 boost::static_pointer_cast<const Evoral::MIDIEvent<Evoral::MusicalTime> > (*i);
1261 Evoral::MusicalTime time = (*i)->time();
1265 if (mev->is_spp() || mev->is_mtc_quarter() || mev->is_mtc_full()) {
1266 if (!display_periodic_messages) {
1274 for (uint32_t b = 0; b < (*i)->size(); ++b) {
1275 str << int((*i)->buffer()[b]);
1276 if (b != (*i)->size() -1) {
1280 string text = str.str();
1282 const double x = trackview.editor().frame_to_pixel(source_beats_to_absolute_frames(time));
1284 double height = midi_stream_view()->contents_height();
1286 boost::shared_ptr<CanvasSysEx> sysex = boost::shared_ptr<CanvasSysEx>(
1287 new CanvasSysEx(*this, *_note_group, text, height, x, 1.0));
1289 // Show unless message is beyond the region bounds
1290 if (time - _region->start() >= _region->length() || time < _region->start()) {
1296 _sys_exes.push_back(sysex);
1300 MidiRegionView::~MidiRegionView ()
1302 in_destructor = true;
1304 trackview.editor().verbose_cursor()->hide ();
1306 note_delete_connection.disconnect ();
1308 delete _list_editor;
1310 RegionViewGoingAway (this); /* EMIT_SIGNAL */
1312 if (_active_notes) {
1316 _selection_cleared_connection.disconnect ();
1322 delete _note_diff_command;
1323 delete _step_edit_cursor;
1324 delete _temporary_note_group;
1328 MidiRegionView::region_resized (const PropertyChange& what_changed)
1330 RegionView::region_resized(what_changed);
1332 if (what_changed.contains (ARDOUR::Properties::position)) {
1333 set_duration(_region->length(), 0);
1334 if (_enable_display) {
1341 MidiRegionView::reset_width_dependent_items (double pixel_width)
1343 RegionView::reset_width_dependent_items(pixel_width);
1344 assert(_pixel_width == pixel_width);
1346 if (_enable_display) {
1350 for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
1351 if ((*x)->width() >= _pixel_width) {
1358 move_step_edit_cursor (_step_edit_cursor_position);
1359 set_step_edit_cursor_width (_step_edit_cursor_width);
1363 MidiRegionView::set_height (double height)
1365 static const double FUDGE = 2.0;
1366 const double old_height = _height;
1367 RegionView::set_height(height);
1368 _height = height - FUDGE;
1370 apply_note_range(midi_stream_view()->lowest_note(),
1371 midi_stream_view()->highest_note(),
1372 height != old_height + FUDGE);
1375 name_pixbuf->raise_to_top();
1378 for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
1379 (*x)->set_height (midi_stream_view()->contents_height());
1382 if (_step_edit_cursor) {
1383 _step_edit_cursor->property_y2() = midi_stream_view()->contents_height();
1388 /** Apply the current note range from the stream view
1389 * by repositioning/hiding notes as necessary
1392 MidiRegionView::apply_note_range (uint8_t min, uint8_t max, bool force)
1394 if (!_enable_display) {
1398 if (!force && _current_range_min == min && _current_range_max == max) {
1402 _current_range_min = min;
1403 _current_range_max = max;
1405 for (Events::const_iterator i = _events.begin(); i != _events.end(); ++i) {
1406 CanvasNoteEvent* event = *i;
1407 boost::shared_ptr<NoteType> note (event->note());
1409 if (note->note() < _current_range_min ||
1410 note->note() > _current_range_max) {
1416 if (CanvasNote* cnote = dynamic_cast<CanvasNote*>(event)) {
1418 const double y1 = midi_stream_view()->note_to_y(note->note());
1419 const double y2 = y1 + floor(midi_stream_view()->note_height());
1421 cnote->property_y1() = y1;
1422 cnote->property_y2() = y2;
1424 } else if (CanvasHit* chit = dynamic_cast<CanvasHit*>(event)) {
1426 const double diamond_size = update_hit (chit);
1428 chit->set_height (diamond_size);
1434 MidiRegionView::add_ghost (TimeAxisView& tv)
1438 double unit_position = _region->position () / samples_per_unit;
1439 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(&tv);
1440 MidiGhostRegion* ghost;
1442 if (mtv && mtv->midi_view()) {
1443 /* if ghost is inserted into midi track, use a dedicated midi ghost canvas group
1444 to allow having midi notes on top of note lines and waveforms.
1446 ghost = new MidiGhostRegion (*mtv->midi_view(), trackview, unit_position);
1448 ghost = new MidiGhostRegion (tv, trackview, unit_position);
1451 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1452 if ((note = dynamic_cast<CanvasNote*>(*i)) != 0) {
1453 ghost->add_note(note);
1457 ghost->set_height ();
1458 ghost->set_duration (_region->length() / samples_per_unit);
1459 ghosts.push_back (ghost);
1461 GhostRegion::CatchDeletion.connect (*this, invalidator (*this), boost::bind (&RegionView::remove_ghost, this, _1), gui_context());
1467 /** Begin tracking note state for successive calls to add_event
1470 MidiRegionView::begin_write()
1472 assert(!_active_notes);
1473 _active_notes = new CanvasNote*[128];
1474 for (unsigned i=0; i < 128; ++i) {
1475 _active_notes[i] = 0;
1480 /** Destroy note state for add_event
1483 MidiRegionView::end_write()
1485 delete[] _active_notes;
1487 _marked_for_selection.clear();
1488 _marked_for_velocity.clear();
1492 /** Resolve an active MIDI note (while recording).
1495 MidiRegionView::resolve_note(uint8_t note, double end_time)
1497 if (midi_view()->note_mode() != Sustained) {
1501 if (_active_notes && _active_notes[note]) {
1503 /* XXX is end_time really region-centric? I think so, because
1504 this is a new region that we're recording, so source zero is
1505 the same as region zero
1507 const framepos_t end_time_frames = region_beats_to_region_frames(end_time);
1509 _active_notes[note]->property_x2() = trackview.editor().frame_to_pixel(end_time_frames);
1510 _active_notes[note]->property_outline_what() = (guint32) 0xF; // all edges
1511 _active_notes[note] = 0;
1516 /** Extend active notes to rightmost edge of region (if length is changed)
1519 MidiRegionView::extend_active_notes()
1521 if (!_active_notes) {
1525 for (unsigned i=0; i < 128; ++i) {
1526 if (_active_notes[i]) {
1527 _active_notes[i]->property_x2() = trackview.editor().frame_to_pixel(_region->length());
1534 MidiRegionView::play_midi_note(boost::shared_ptr<NoteType> note)
1536 if (_no_sound_notes || !Config->get_sound_midi_notes()) {
1540 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1542 if (!route_ui || !route_ui->midi_track()) {
1546 NotePlayer* np = new NotePlayer (route_ui->midi_track ());
1550 /* NotePlayer deletes itself */
1554 MidiRegionView::start_playing_midi_note(boost::shared_ptr<NoteType> note)
1556 if (_no_sound_notes || !Config->get_sound_midi_notes()) {
1560 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1562 if (!route_ui || !route_ui->midi_track()) {
1566 delete _note_player;
1567 _note_player = new NotePlayer (route_ui->midi_track ());
1568 _note_player->add (note);
1569 _note_player->on ();
1573 MidiRegionView::start_playing_midi_chord (vector<boost::shared_ptr<NoteType> > notes)
1575 if (_no_sound_notes || !Config->get_sound_midi_notes()) {
1579 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1581 if (!route_ui || !route_ui->midi_track()) {
1585 delete _note_player;
1586 _note_player = new NotePlayer (route_ui->midi_track());
1588 for (vector<boost::shared_ptr<NoteType> >::iterator n = notes.begin(); n != notes.end(); ++n) {
1589 _note_player->add (*n);
1592 _note_player->on ();
1597 MidiRegionView::note_in_region_range (const boost::shared_ptr<NoteType> note, bool& visible) const
1599 const framepos_t note_start_frames = source_beats_to_region_frames (note->time());
1600 bool outside = (note_start_frames < 0) || (note_start_frames > _region->last_frame());
1602 visible = (note->note() >= midi_stream_view()->lowest_note()) &&
1603 (note->note() <= midi_stream_view()->highest_note());
1608 /** Update a canvas note's size from its model note.
1609 * @param ev Canvas note to update.
1610 * @param update_ghost_regions true to update the note in any ghost regions that we have, otherwise false.
1613 MidiRegionView::update_note (CanvasNote* ev, bool update_ghost_regions)
1615 boost::shared_ptr<NoteType> note = ev->note();
1616 const double x = trackview.editor().frame_to_pixel (source_beats_to_region_frames (note->time()));
1617 const double y1 = midi_stream_view()->note_to_y(note->note());
1619 ev->property_x1() = x;
1620 ev->property_y1() = y1;
1622 /* trim note display to not overlap the end of its region */
1624 if (note->length() > 0) {
1625 const framepos_t note_end_frames = min (source_beats_to_region_frames (note->end_time()), _region->length());
1626 ev->property_x2() = trackview.editor().frame_to_pixel (note_end_frames);
1628 ev->property_x2() = trackview.editor().frame_to_pixel (_region->length());
1631 ev->property_y2() = y1 + floor(midi_stream_view()->note_height());
1633 if (note->length() == 0) {
1634 if (_active_notes) {
1635 assert(note->note() < 128);
1636 // If this note is already active there's a stuck note,
1637 // finish the old note rectangle
1638 if (_active_notes[note->note()]) {
1639 CanvasNote* const old_rect = _active_notes[note->note()];
1640 boost::shared_ptr<NoteType> old_note = old_rect->note();
1641 old_rect->property_x2() = x;
1642 old_rect->property_outline_what() = (guint32) 0xF;
1644 _active_notes[note->note()] = ev;
1646 /* outline all but right edge */
1647 ev->property_outline_what() = (guint32) (0x1 & 0x4 & 0x8);
1649 /* outline all edges */
1650 ev->property_outline_what() = (guint32) 0xF;
1653 if (update_ghost_regions) {
1654 for (std::vector<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
1655 MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*i);
1657 gr->update_note (ev);
1664 MidiRegionView::update_hit (CanvasHit* ev)
1666 boost::shared_ptr<NoteType> note = ev->note();
1668 const framepos_t note_start_frames = source_beats_to_region_frames(note->time());
1669 const double x = trackview.editor().frame_to_pixel(note_start_frames);
1670 const double diamond_size = midi_stream_view()->note_height() / 2.0;
1671 const double y = midi_stream_view()->note_to_y(note->note()) + ((diamond_size-2) / 4.0);
1675 return diamond_size;
1678 /** Add a MIDI note to the view (with length).
1680 * If in sustained mode, notes with length 0 will be considered active
1681 * notes, and resolve_note should be called when the corresponding note off
1682 * event arrives, to properly display the note.
1685 MidiRegionView::add_note(const boost::shared_ptr<NoteType> note, bool visible)
1687 CanvasNoteEvent* event = 0;
1689 assert(note->time() >= 0);
1690 assert(midi_view()->note_mode() == Sustained || midi_view()->note_mode() == Percussive);
1692 //ArdourCanvas::Group* const group = (ArdourCanvas::Group*) get_canvas_group();
1694 if (midi_view()->note_mode() == Sustained) {
1696 CanvasNote* ev_rect = new CanvasNote(*this, *_note_group, note);
1698 update_note (ev_rect);
1702 MidiGhostRegion* gr;
1704 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
1705 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
1706 gr->add_note(ev_rect);
1710 } else if (midi_view()->note_mode() == Percussive) {
1712 const double diamond_size = midi_stream_view()->note_height() / 2.0;
1714 CanvasHit* ev_diamond = new CanvasHit (*this, *_note_group, diamond_size, note);
1716 update_hit (ev_diamond);
1725 if (_marked_for_selection.find(note) != _marked_for_selection.end()) {
1726 note_selected(event, true);
1729 if (_marked_for_velocity.find(note) != _marked_for_velocity.end()) {
1730 event->show_velocity();
1733 event->on_channel_selection_change(_last_channel_selection);
1734 _events.push_back(event);
1743 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1744 MidiStreamView* const view = mtv->midi_view();
1746 view->update_note_range (note->note());
1750 MidiRegionView::step_add_note (uint8_t channel, uint8_t number, uint8_t velocity,
1751 Evoral::MusicalTime pos, Evoral::MusicalTime len)
1753 boost::shared_ptr<NoteType> new_note (new NoteType (channel, pos, len, number, velocity));
1755 /* potentially extend region to hold new note */
1757 framepos_t end_frame = source_beats_to_absolute_frames (new_note->end_time());
1758 framepos_t region_end = _region->last_frame();
1760 if (end_frame > region_end) {
1761 _region->set_length (end_frame - _region->position());
1764 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1765 MidiStreamView* const view = mtv->midi_view();
1767 view->update_note_range(new_note->note());
1769 _marked_for_selection.clear ();
1772 start_note_diff_command (_("step add"));
1773 note_diff_add_note (new_note, true, false);
1776 // last_step_edit_note = new_note;
1780 MidiRegionView::step_sustain (Evoral::MusicalTime beats)
1782 change_note_lengths (false, false, beats, false, true);
1785 /** Add a new patch change flag to the canvas.
1786 * @param patch the patch change to add
1787 * @param the text to display in the flag
1788 * @param active_channel true to display the flag as on an active channel, false to grey it out for an inactive channel.
1791 MidiRegionView::add_canvas_patch_change (MidiModel::PatchChangePtr patch, const string& displaytext, bool active_channel)
1793 assert (patch->time() >= 0);
1795 framecnt_t region_frames = source_beats_to_region_frames (patch->time());
1796 const double x = trackview.editor().frame_to_pixel (region_frames);
1798 double const height = midi_stream_view()->contents_height();
1800 boost::shared_ptr<CanvasPatchChange> patch_change = boost::shared_ptr<CanvasPatchChange>(
1801 new CanvasPatchChange(*this, *_note_group,
1810 if (patch_change->width() < _pixel_width) {
1811 // Show unless patch change is beyond the region bounds
1812 if (region_frames < 0 || region_frames >= _region->length()) {
1813 patch_change->hide();
1815 patch_change->show();
1818 patch_change->hide ();
1821 _patch_changes.push_back (patch_change);
1824 MIDI::Name::PatchPrimaryKey
1825 MidiRegionView::patch_change_to_patch_key (MidiModel::PatchChangePtr p)
1827 return MIDI::Name::PatchPrimaryKey (p->program(), p->bank());
1831 MidiRegionView::get_patch_key_at (double time, uint8_t channel, MIDI::Name::PatchPrimaryKey& key)
1833 MidiModel::PatchChanges::iterator i = _model->patch_change_lower_bound (time);
1834 while (i != _model->patch_changes().end() && (*i)->channel() != channel) {
1838 if (i != _model->patch_changes().end()) {
1839 key.bank_number = (*i)->bank();
1840 key.program_number = (*i)->program ();
1842 key.bank_number = key.program_number = 0;
1845 assert (key.is_sane());
1849 MidiRegionView::change_patch_change (CanvasPatchChange& pc, const MIDI::Name::PatchPrimaryKey& new_patch)
1851 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("alter patch change"));
1853 if (pc.patch()->program() != new_patch.program_number) {
1854 c->change_program (pc.patch (), new_patch.program_number);
1857 int const new_bank = new_patch.bank_number;
1858 if (pc.patch()->bank() != new_bank) {
1859 c->change_bank (pc.patch (), new_bank);
1862 _model->apply_command (*trackview.session(), c);
1864 _patch_changes.clear ();
1865 display_patch_changes ();
1869 MidiRegionView::change_patch_change (MidiModel::PatchChangePtr old_change, const Evoral::PatchChange<Evoral::MusicalTime> & new_change)
1871 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("alter patch change"));
1873 if (old_change->time() != new_change.time()) {
1874 c->change_time (old_change, new_change.time());
1877 if (old_change->channel() != new_change.channel()) {
1878 c->change_channel (old_change, new_change.channel());
1881 if (old_change->program() != new_change.program()) {
1882 c->change_program (old_change, new_change.program());
1885 if (old_change->bank() != new_change.bank()) {
1886 c->change_bank (old_change, new_change.bank());
1889 _model->apply_command (*trackview.session(), c);
1891 _patch_changes.clear ();
1892 display_patch_changes ();
1895 /** Add a patch change to the region.
1896 * @param t Time in frames relative to region position
1897 * @param patch Patch to add; time and channel are ignored (time is converted from t, and channel comes from
1898 * MidiTimeAxisView::get_channel_for_add())
1901 MidiRegionView::add_patch_change (framecnt_t t, Evoral::PatchChange<Evoral::MusicalTime> const & patch)
1903 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1905 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("add patch change"));
1906 c->add (MidiModel::PatchChangePtr (
1907 new Evoral::PatchChange<Evoral::MusicalTime> (
1908 absolute_frames_to_source_beats (_region->position() + t),
1909 mtv->get_channel_for_add(), patch.program(), patch.bank()
1914 _model->apply_command (*trackview.session(), c);
1916 _patch_changes.clear ();
1917 display_patch_changes ();
1921 MidiRegionView::move_patch_change (CanvasPatchChange& pc, Evoral::MusicalTime t)
1923 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("move patch change"));
1924 c->change_time (pc.patch (), t);
1925 _model->apply_command (*trackview.session(), c);
1927 _patch_changes.clear ();
1928 display_patch_changes ();
1932 MidiRegionView::delete_patch_change (CanvasPatchChange* pc)
1934 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("delete patch change"));
1935 c->remove (pc->patch ());
1936 _model->apply_command (*trackview.session(), c);
1938 _patch_changes.clear ();
1939 display_patch_changes ();
1943 MidiRegionView::previous_patch (CanvasPatchChange& patch)
1945 if (patch.patch()->program() < 127) {
1946 MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch());
1947 key.program_number++;
1948 change_patch_change (patch, key);
1953 MidiRegionView::next_patch (CanvasPatchChange& patch)
1955 if (patch.patch()->program() > 0) {
1956 MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch());
1957 key.program_number--;
1958 change_patch_change (patch, key);
1963 MidiRegionView::previous_bank (CanvasPatchChange& patch)
1965 if (patch.patch()->program() < 127) {
1966 MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch());
1967 if (key.bank_number > 0) {
1969 change_patch_change (patch, key);
1975 MidiRegionView::next_bank (CanvasPatchChange& patch)
1977 if (patch.patch()->program() > 0) {
1978 MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch());
1979 if (key.bank_number < 127) {
1981 change_patch_change (patch, key);
1987 MidiRegionView::maybe_remove_deleted_note_from_selection (CanvasNoteEvent* cne)
1989 if (_selection.empty()) {
1993 _selection.erase (cne);
1997 MidiRegionView::delete_selection()
1999 if (_selection.empty()) {
2003 start_note_diff_command (_("delete selection"));
2005 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2006 if ((*i)->selected()) {
2007 _note_diff_command->remove((*i)->note());
2017 MidiRegionView::delete_note (boost::shared_ptr<NoteType> n)
2019 start_note_diff_command (_("delete note"));
2020 _note_diff_command->remove (n);
2023 trackview.editor().verbose_cursor()->hide ();
2027 MidiRegionView::clear_selection_except (ArdourCanvas::CanvasNoteEvent* ev, bool signal)
2029 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2031 Selection::iterator tmp = i;
2034 (*i)->set_selected (false);
2035 (*i)->hide_velocity ();
2036 _selection.erase (i);
2044 /* this does not change the status of this regionview w.r.t the editor
2049 SelectionCleared (this); /* EMIT SIGNAL */
2054 MidiRegionView::unique_select(ArdourCanvas::CanvasNoteEvent* ev)
2056 clear_selection_except (ev);
2058 /* don't bother with checking to see if we should remove this
2059 regionview from the editor selection, since we're about to add
2060 another note, and thus put/keep this regionview in the editor
2064 if (!ev->selected()) {
2065 add_to_selection (ev);
2070 MidiRegionView::select_all_notes ()
2074 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2075 add_to_selection (*i);
2080 MidiRegionView::select_range (framepos_t start, framepos_t end)
2084 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2085 framepos_t t = source_beats_to_absolute_frames((*i)->note()->time());
2086 if (t >= start && t <= end) {
2087 add_to_selection (*i);
2093 MidiRegionView::invert_selection ()
2095 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2096 if ((*i)->selected()) {
2097 remove_from_selection(*i);
2099 add_to_selection (*i);
2105 MidiRegionView::select_matching_notes (uint8_t notenum, uint16_t channel_mask, bool add, bool extend)
2107 uint8_t low_note = 127;
2108 uint8_t high_note = 0;
2109 MidiModel::Notes& notes (_model->notes());
2110 _optimization_iterator = _events.begin();
2116 if (extend && _selection.empty()) {
2122 /* scan existing selection to get note range */
2124 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2125 if ((*i)->note()->note() < low_note) {
2126 low_note = (*i)->note()->note();
2128 if ((*i)->note()->note() > high_note) {
2129 high_note = (*i)->note()->note();
2133 low_note = min (low_note, notenum);
2134 high_note = max (high_note, notenum);
2137 _no_sound_notes = true;
2139 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
2141 boost::shared_ptr<NoteType> note (*n);
2142 CanvasNoteEvent* cne;
2143 bool select = false;
2145 if (((1 << note->channel()) & channel_mask) != 0) {
2147 if ((note->note() >= low_note && note->note() <= high_note)) {
2150 } else if (note->note() == notenum) {
2156 if ((cne = find_canvas_note (note)) != 0) {
2157 // extend is false because we've taken care of it,
2158 // since it extends by time range, not pitch.
2159 note_selected (cne, add, false);
2163 add = true; // we need to add all remaining matching notes, even if the passed in value was false (for "set")
2167 _no_sound_notes = false;
2171 MidiRegionView::toggle_matching_notes (uint8_t notenum, uint16_t channel_mask)
2173 MidiModel::Notes& notes (_model->notes());
2174 _optimization_iterator = _events.begin();
2176 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
2178 boost::shared_ptr<NoteType> note (*n);
2179 CanvasNoteEvent* cne;
2181 if (note->note() == notenum && (((0x0001 << note->channel()) & channel_mask) != 0)) {
2182 if ((cne = find_canvas_note (note)) != 0) {
2183 if (cne->selected()) {
2184 note_deselected (cne);
2186 note_selected (cne, true, false);
2194 MidiRegionView::note_selected (ArdourCanvas::CanvasNoteEvent* ev, bool add, bool extend)
2197 clear_selection_except (ev);
2198 if (!_selection.empty()) {
2199 PublicEditor& editor (trackview.editor());
2200 editor.get_selection().add (this);
2206 if (!ev->selected()) {
2207 add_to_selection (ev);
2211 /* find end of latest note selected, select all between that and the start of "ev" */
2213 Evoral::MusicalTime earliest = Evoral::MaxMusicalTime;
2214 Evoral::MusicalTime latest = 0;
2216 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2217 if ((*i)->note()->end_time() > latest) {
2218 latest = (*i)->note()->end_time();
2220 if ((*i)->note()->time() < earliest) {
2221 earliest = (*i)->note()->time();
2225 if (ev->note()->end_time() > latest) {
2226 latest = ev->note()->end_time();
2229 if (ev->note()->time() < earliest) {
2230 earliest = ev->note()->time();
2233 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2235 /* find notes entirely within OR spanning the earliest..latest range */
2237 if (((*i)->note()->time() >= earliest && (*i)->note()->end_time() <= latest) ||
2238 ((*i)->note()->time() <= earliest && (*i)->note()->end_time() >= latest)) {
2239 add_to_selection (*i);
2247 MidiRegionView::note_deselected(ArdourCanvas::CanvasNoteEvent* ev)
2249 remove_from_selection (ev);
2253 MidiRegionView::update_drag_selection(double x1, double x2, double y1, double y2, bool extend)
2263 // TODO: Make this faster by storing the last updated selection rect, and only
2264 // adjusting things that are in the area that appears/disappeared.
2265 // We probably need a tree to be able to find events in O(log(n)) time.
2267 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2269 /* check if any corner of the note is inside the rect
2272 1) this is computing "touched by", not "contained by" the rect.
2273 2) this does not require that events be sorted in time.
2276 const double ix1 = (*i)->x1();
2277 const double ix2 = (*i)->x2();
2278 const double iy1 = (*i)->y1();
2279 const double iy2 = (*i)->y2();
2281 if ((ix1 >= x1 && ix1 <= x2 && iy1 >= y1 && iy1 <= y2) ||
2282 (ix1 >= x1 && ix1 <= x2 && iy2 >= y1 && iy2 <= y2) ||
2283 (ix2 >= x1 && ix2 <= x2 && iy1 >= y1 && iy1 <= y2) ||
2284 (ix2 >= x1 && ix2 <= x2 && iy2 >= y1 && iy2 <= y2)) {
2287 if (!(*i)->selected()) {
2288 add_to_selection (*i);
2290 } else if ((*i)->selected() && !extend) {
2291 // Not inside rectangle
2292 remove_from_selection (*i);
2298 MidiRegionView::update_vertical_drag_selection (double y1, double y2, bool extend)
2304 // TODO: Make this faster by storing the last updated selection rect, and only
2305 // adjusting things that are in the area that appears/disappeared.
2306 // We probably need a tree to be able to find events in O(log(n)) time.
2308 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2310 /* check if any corner of the note is inside the rect
2313 1) this is computing "touched by", not "contained by" the rect.
2314 2) this does not require that events be sorted in time.
2317 if (((*i)->y1() >= y1 && (*i)->y1() <= y2)) {
2318 // within y- (note-) range
2319 if (!(*i)->selected()) {
2320 add_to_selection (*i);
2322 } else if ((*i)->selected() && !extend) {
2323 // Not inside rectangle
2324 remove_from_selection (*i);
2330 MidiRegionView::remove_from_selection (CanvasNoteEvent* ev)
2332 Selection::iterator i = _selection.find (ev);
2334 if (i != _selection.end()) {
2335 _selection.erase (i);
2338 ev->set_selected (false);
2339 ev->hide_velocity ();
2341 if (_selection.empty()) {
2342 PublicEditor& editor (trackview.editor());
2343 editor.get_selection().remove (this);
2348 MidiRegionView::add_to_selection (CanvasNoteEvent* ev)
2350 bool add_mrv_selection = false;
2352 if (_selection.empty()) {
2353 add_mrv_selection = true;
2356 if (_selection.insert (ev).second) {
2357 ev->set_selected (true);
2358 start_playing_midi_note ((ev)->note());
2361 if (add_mrv_selection) {
2362 PublicEditor& editor (trackview.editor());
2363 editor.get_selection().add (this);
2368 MidiRegionView::move_selection(double dx, double dy, double cumulative_dy)
2370 typedef vector<boost::shared_ptr<NoteType> > PossibleChord;
2371 PossibleChord to_play;
2372 Evoral::MusicalTime earliest = Evoral::MaxMusicalTime;
2374 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2375 if ((*i)->note()->time() < earliest) {
2376 earliest = (*i)->note()->time();
2380 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2381 if (Evoral::musical_time_equal ((*i)->note()->time(), earliest)) {
2382 to_play.push_back ((*i)->note());
2384 (*i)->move_event(dx, dy);
2387 if (dy && !_selection.empty() && !_no_sound_notes && Config->get_sound_midi_notes()) {
2389 if (to_play.size() > 1) {
2391 PossibleChord shifted;
2393 for (PossibleChord::iterator n = to_play.begin(); n != to_play.end(); ++n) {
2394 boost::shared_ptr<NoteType> moved_note (new NoteType (**n));
2395 moved_note->set_note (moved_note->note() + cumulative_dy);
2396 shifted.push_back (moved_note);
2399 start_playing_midi_chord (shifted);
2401 } else if (!to_play.empty()) {
2403 boost::shared_ptr<NoteType> moved_note (new NoteType (*to_play.front()));
2404 moved_note->set_note (moved_note->note() + cumulative_dy);
2405 start_playing_midi_note (moved_note);
2411 MidiRegionView::note_dropped(CanvasNoteEvent *, frameoffset_t dt, int8_t dnote)
2413 assert (!_selection.empty());
2415 uint8_t lowest_note_in_selection = 127;
2416 uint8_t highest_note_in_selection = 0;
2417 uint8_t highest_note_difference = 0;
2419 // find highest and lowest notes first
2421 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2422 uint8_t pitch = (*i)->note()->note();
2423 lowest_note_in_selection = std::min(lowest_note_in_selection, pitch);
2424 highest_note_in_selection = std::max(highest_note_in_selection, pitch);
2428 cerr << "dnote: " << (int) dnote << endl;
2429 cerr << "lowest note (streamview): " << int(midi_stream_view()->lowest_note())
2430 << " highest note (streamview): " << int(midi_stream_view()->highest_note()) << endl;
2431 cerr << "lowest note (selection): " << int(lowest_note_in_selection) << " highest note(selection): "
2432 << int(highest_note_in_selection) << endl;
2433 cerr << "selection size: " << _selection.size() << endl;
2434 cerr << "Highest note in selection: " << (int) highest_note_in_selection << endl;
2437 // Make sure the note pitch does not exceed the MIDI standard range
2438 if (highest_note_in_selection + dnote > 127) {
2439 highest_note_difference = highest_note_in_selection - 127;
2442 start_note_diff_command (_("move notes"));
2444 for (Selection::iterator i = _selection.begin(); i != _selection.end() ; ++i) {
2446 framepos_t new_frames = source_beats_to_absolute_frames ((*i)->note()->time()) + dt;
2447 Evoral::MusicalTime new_time = absolute_frames_to_source_beats (new_frames);
2453 note_diff_add_change (*i, MidiModel::NoteDiffCommand::StartTime, new_time);
2455 uint8_t original_pitch = (*i)->note()->note();
2456 uint8_t new_pitch = original_pitch + dnote - highest_note_difference;
2458 // keep notes in standard midi range
2459 clamp_to_0_127(new_pitch);
2461 lowest_note_in_selection = std::min(lowest_note_in_selection, new_pitch);
2462 highest_note_in_selection = std::max(highest_note_in_selection, new_pitch);
2464 note_diff_add_change (*i, MidiModel::NoteDiffCommand::NoteNumber, new_pitch);
2469 // care about notes being moved beyond the upper/lower bounds on the canvas
2470 if (lowest_note_in_selection < midi_stream_view()->lowest_note() ||
2471 highest_note_in_selection > midi_stream_view()->highest_note()) {
2472 midi_stream_view()->set_note_range(MidiStreamView::ContentsRange);
2476 /** @param x Pixel relative to the region position.
2477 * @return Snapped frame relative to the region position.
2480 MidiRegionView::snap_pixel_to_frame(double x)
2482 PublicEditor& editor (trackview.editor());
2483 return snap_frame_to_frame (editor.pixel_to_frame (x));
2486 /** @param x Pixel relative to the region position.
2487 * @return Snapped pixel relative to the region position.
2490 MidiRegionView::snap_to_pixel(double x)
2492 return (double) trackview.editor().frame_to_pixel(snap_pixel_to_frame(x));
2496 MidiRegionView::get_position_pixels()
2498 framepos_t region_frame = get_position();
2499 return trackview.editor().frame_to_pixel(region_frame);
2503 MidiRegionView::get_end_position_pixels()
2505 framepos_t frame = get_position() + get_duration ();
2506 return trackview.editor().frame_to_pixel(frame);
2510 MidiRegionView::source_beats_to_absolute_frames(double beats) const
2512 /* the time converter will return the frame corresponding to `beats'
2513 relative to the start of the source. The start of the source
2514 is an implied position given by region->position - region->start
2516 const framepos_t source_start = _region->position() - _region->start();
2517 return source_start + _source_relative_time_converter.to (beats);
2521 MidiRegionView::absolute_frames_to_source_beats(framepos_t frames) const
2523 /* the `frames' argument needs to be converted into a frame count
2524 relative to the start of the source before being passed in to the
2527 const framepos_t source_start = _region->position() - _region->start();
2528 return _source_relative_time_converter.from (frames - source_start);
2532 MidiRegionView::region_beats_to_region_frames(double beats) const
2534 return _region_relative_time_converter.to(beats);
2538 MidiRegionView::region_frames_to_region_beats(framepos_t frames) const
2540 return _region_relative_time_converter.from(frames);
2544 MidiRegionView::begin_resizing (bool /*at_front*/)
2546 _resize_data.clear();
2548 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2549 CanvasNote *note = dynamic_cast<CanvasNote *> (*i);
2551 // only insert CanvasNotes into the map
2553 NoteResizeData *resize_data = new NoteResizeData();
2554 resize_data->canvas_note = note;
2556 // create a new SimpleRect from the note which will be the resize preview
2557 SimpleRect *resize_rect = new SimpleRect(
2558 *_note_group, note->x1(), note->y1(), note->x2(), note->y2());
2560 // calculate the colors: get the color settings
2561 uint32_t fill_color = UINT_RGBA_CHANGE_A(
2562 ARDOUR_UI::config()->canvasvar_MidiNoteSelected.get(),
2565 // make the resize preview notes more transparent and bright
2566 fill_color = UINT_INTERPOLATE(fill_color, 0xFFFFFF40, 0.5);
2568 // calculate color based on note velocity
2569 resize_rect->property_fill_color_rgba() = UINT_INTERPOLATE(
2570 CanvasNoteEvent::meter_style_fill_color(note->note()->velocity(), note->selected()),
2574 resize_rect->property_outline_color_rgba() = CanvasNoteEvent::calculate_outline(
2575 ARDOUR_UI::config()->canvasvar_MidiNoteSelected.get());
2577 resize_data->resize_rect = resize_rect;
2578 _resize_data.push_back(resize_data);
2583 /** Update resizing notes while user drags.
2584 * @param primary `primary' note for the drag; ie the one that is used as the reference in non-relative mode.
2585 * @param at_front which end of the note (true == note on, false == note off)
2586 * @param delta_x change in mouse position since the start of the drag
2587 * @param relative true if relative resizing is taking place, false if absolute resizing. This only makes
2588 * a difference when multiple notes are being resized; in relative mode, each note's length is changed by the
2589 * amount of the drag. In non-relative mode, all selected notes are set to have the same start or end point
2590 * as the \a primary note.
2593 MidiRegionView::update_resizing (ArdourCanvas::CanvasNoteEvent* primary, bool at_front, double delta_x, bool relative)
2595 bool cursor_set = false;
2597 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2598 SimpleRect* resize_rect = (*i)->resize_rect;
2599 CanvasNote* canvas_note = (*i)->canvas_note;
2604 current_x = canvas_note->x1() + delta_x;
2606 current_x = primary->x1() + delta_x;
2610 current_x = canvas_note->x2() + delta_x;
2612 current_x = primary->x2() + delta_x;
2617 resize_rect->property_x1() = snap_to_pixel(current_x);
2618 resize_rect->property_x2() = canvas_note->x2();
2620 resize_rect->property_x2() = snap_to_pixel(current_x);
2621 resize_rect->property_x1() = canvas_note->x1();
2627 beats = snap_pixel_to_frame (current_x);
2628 beats = region_frames_to_region_beats (beats);
2633 if (beats < canvas_note->note()->end_time()) {
2634 len = canvas_note->note()->time() - beats;
2635 len += canvas_note->note()->length();
2640 if (beats >= canvas_note->note()->time()) {
2641 len = beats - canvas_note->note()->time();
2648 snprintf (buf, sizeof (buf), "%.3g beats", len);
2649 show_verbose_cursor (buf, 0, 0);
2658 /** Finish resizing notes when the user releases the mouse button.
2659 * Parameters the same as for \a update_resizing().
2662 MidiRegionView::commit_resizing (ArdourCanvas::CanvasNoteEvent* primary, bool at_front, double delta_x, bool relative)
2664 start_note_diff_command (_("resize notes"));
2666 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2667 CanvasNote* canvas_note = (*i)->canvas_note;
2668 SimpleRect* resize_rect = (*i)->resize_rect;
2670 /* Get the new x position for this resize, which is in pixels relative
2671 * to the region position.
2678 current_x = canvas_note->x1() + delta_x;
2680 current_x = primary->x1() + delta_x;
2684 current_x = canvas_note->x2() + delta_x;
2686 current_x = primary->x2() + delta_x;
2690 /* Convert that to a frame within the source */
2691 current_x = snap_pixel_to_frame (current_x) + _region->start ();
2693 /* and then to beats */
2694 current_x = region_frames_to_region_beats (current_x);
2696 if (at_front && current_x < canvas_note->note()->end_time()) {
2697 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::StartTime, current_x);
2699 double len = canvas_note->note()->time() - current_x;
2700 len += canvas_note->note()->length();
2703 /* XXX convert to beats */
2704 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
2709 double len = current_x - canvas_note->note()->time();
2712 /* XXX convert to beats */
2713 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
2721 _resize_data.clear();
2726 MidiRegionView::abort_resizing ()
2728 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2729 delete (*i)->resize_rect;
2733 _resize_data.clear ();
2737 MidiRegionView::change_note_velocity(CanvasNoteEvent* event, int8_t velocity, bool relative)
2739 uint8_t new_velocity;
2742 new_velocity = event->note()->velocity() + velocity;
2743 clamp_to_0_127(new_velocity);
2745 new_velocity = velocity;
2748 event->set_selected (event->selected()); // change color
2750 note_diff_add_change (event, MidiModel::NoteDiffCommand::Velocity, new_velocity);
2754 MidiRegionView::change_note_note (CanvasNoteEvent* event, int8_t note, bool relative)
2759 new_note = event->note()->note() + note;
2764 clamp_to_0_127 (new_note);
2765 note_diff_add_change (event, MidiModel::NoteDiffCommand::NoteNumber, new_note);
2769 MidiRegionView::trim_note (CanvasNoteEvent* event, Evoral::MusicalTime front_delta, Evoral::MusicalTime end_delta)
2771 bool change_start = false;
2772 bool change_length = false;
2773 Evoral::MusicalTime new_start = 0;
2774 Evoral::MusicalTime new_length = 0;
2776 /* NOTE: the semantics of the two delta arguments are slightly subtle:
2778 front_delta: if positive - move the start of the note later in time (shortening it)
2779 if negative - move the start of the note earlier in time (lengthening it)
2781 end_delta: if positive - move the end of the note later in time (lengthening it)
2782 if negative - move the end of the note earlier in time (shortening it)
2786 if (front_delta < 0) {
2788 if (event->note()->time() < -front_delta) {
2791 new_start = event->note()->time() + front_delta; // moves earlier
2794 /* start moved toward zero, so move the end point out to where it used to be.
2795 Note that front_delta is negative, so this increases the length.
2798 new_length = event->note()->length() - front_delta;
2799 change_start = true;
2800 change_length = true;
2804 Evoral::MusicalTime new_pos = event->note()->time() + front_delta;
2806 if (new_pos < event->note()->end_time()) {
2807 new_start = event->note()->time() + front_delta;
2808 /* start moved toward the end, so move the end point back to where it used to be */
2809 new_length = event->note()->length() - front_delta;
2810 change_start = true;
2811 change_length = true;
2818 bool can_change = true;
2819 if (end_delta < 0) {
2820 if (event->note()->length() < -end_delta) {
2826 new_length = event->note()->length() + end_delta;
2827 change_length = true;
2832 note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_start);
2835 if (change_length) {
2836 note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, new_length);
2841 MidiRegionView::change_note_channel (CanvasNoteEvent* event, int8_t chn, bool relative)
2843 uint8_t new_channel;
2847 if (event->note()->channel() < -chn) {
2850 new_channel = event->note()->channel() + chn;
2853 new_channel = event->note()->channel() + chn;
2856 new_channel = (uint8_t) chn;
2859 note_diff_add_change (event, MidiModel::NoteDiffCommand::Channel, new_channel);
2863 MidiRegionView::change_note_time (CanvasNoteEvent* event, Evoral::MusicalTime delta, bool relative)
2865 Evoral::MusicalTime new_time;
2869 if (event->note()->time() < -delta) {
2872 new_time = event->note()->time() + delta;
2875 new_time = event->note()->time() + delta;
2881 note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_time);
2885 MidiRegionView::change_note_length (CanvasNoteEvent* event, Evoral::MusicalTime t)
2887 note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, t);
2891 MidiRegionView::change_velocities (bool up, bool fine, bool allow_smush, bool all_together)
2896 if (_selection.empty()) {
2911 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2912 if ((*i)->note()->velocity() + delta == 0 || (*i)->note()->velocity() + delta == 127) {
2918 start_note_diff_command (_("change velocities"));
2920 for (Selection::iterator i = _selection.begin(); i != _selection.end();) {
2921 Selection::iterator next = i;
2925 if (i == _selection.begin()) {
2926 change_note_velocity (*i, delta, true);
2927 value = (*i)->note()->velocity() + delta;
2929 change_note_velocity (*i, value, false);
2933 change_note_velocity (*i, delta, true);
2941 if (!_selection.empty()) {
2943 snprintf (buf, sizeof (buf), "Vel %d",
2944 (int) (*_selection.begin())->note()->velocity());
2945 show_verbose_cursor (buf, 10, 10);
2951 MidiRegionView::transpose (bool up, bool fine, bool allow_smush)
2953 if (_selection.empty()) {
2970 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2972 if ((int8_t) (*i)->note()->note() + delta <= 0) {
2976 if ((int8_t) (*i)->note()->note() + delta > 127) {
2983 start_note_diff_command (_("transpose"));
2985 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2986 Selection::iterator next = i;
2988 change_note_note (*i, delta, true);
2996 MidiRegionView::change_note_lengths (bool fine, bool shorter, Evoral::MusicalTime delta, bool start, bool end)
3002 /* grab the current grid distance */
3004 delta = trackview.editor().get_grid_type_as_beats (success, _region->position());
3006 /* XXX cannot get grid type as beats ... should always be possible ... FIX ME */
3007 error << string_compose (_("programming error: %1"), "Grid type not available as beats - TO BE FIXED") << endmsg;
3017 start_note_diff_command (_("change note lengths"));
3019 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3020 Selection::iterator next = i;
3023 /* note the negation of the delta for start */
3025 trim_note (*i, (start ? -delta : 0), (end ? delta : 0));
3034 MidiRegionView::nudge_notes (bool forward)
3036 if (_selection.empty()) {
3040 /* pick a note as the point along the timeline to get the nudge distance.
3041 its not necessarily the earliest note, so we may want to pull the notes out
3042 into a vector and sort before using the first one.
3045 framepos_t ref_point = source_beats_to_absolute_frames ((*(_selection.begin()))->note()->time());
3047 framecnt_t distance;
3049 if (trackview.editor().snap_mode() == Editing::SnapOff) {
3051 /* grid is off - use nudge distance */
3053 distance = trackview.editor().get_nudge_distance (ref_point, unused);
3059 framepos_t next_pos = ref_point;
3062 if (max_framepos - 1 < next_pos) {
3066 if (next_pos == 0) {
3072 trackview.editor().snap_to (next_pos, (forward ? 1 : -1), false);
3073 distance = ref_point - next_pos;
3076 if (distance == 0) {
3080 Evoral::MusicalTime delta = region_frames_to_region_beats (fabs (distance));
3086 start_note_diff_command (_("nudge"));
3088 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3089 Selection::iterator next = i;
3091 change_note_time (*i, delta, true);
3099 MidiRegionView::change_channel(uint8_t channel)
3101 start_note_diff_command(_("change channel"));
3102 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3103 note_diff_add_change (*i, MidiModel::NoteDiffCommand::Channel, channel);
3111 MidiRegionView::note_entered(ArdourCanvas::CanvasNoteEvent* ev)
3113 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3115 pre_enter_cursor = editor->get_canvas_cursor ();
3117 if (_mouse_state == SelectTouchDragging) {
3118 note_selected (ev, true);
3121 show_verbose_cursor (ev->note ());
3125 MidiRegionView::note_left (ArdourCanvas::CanvasNoteEvent*)
3127 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3129 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3130 (*i)->hide_velocity ();
3133 editor->verbose_cursor()->hide ();
3135 if (pre_enter_cursor) {
3136 editor->set_canvas_cursor (pre_enter_cursor);
3137 pre_enter_cursor = 0;
3142 MidiRegionView::patch_entered (ArdourCanvas::CanvasPatchChange* ev)
3145 /* XXX should get patch name if we can */
3146 s << _("Bank:") << (ev->patch()->bank() + MIDI_BP_ZERO) << '\n' << _("Program:") << ((int) ev->patch()->program()) + MIDI_BP_ZERO << '\n' << _("Channel:") << ((int) ev->patch()->channel() + 1);
3147 show_verbose_cursor (s.str(), 10, 20);
3151 MidiRegionView::patch_left (ArdourCanvas::CanvasPatchChange *)
3153 trackview.editor().verbose_cursor()->hide ();
3157 MidiRegionView::note_mouse_position (float x_fraction, float /*y_fraction*/, bool can_set_cursor)
3159 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3160 Editing::MouseMode mm = editor->current_mouse_mode();
3161 bool trimmable = (mm == MouseObject || mm == MouseTimeFX || mm == MouseDraw);
3163 if (trimmable && x_fraction > 0.0 && x_fraction < 0.2) {
3164 editor->set_canvas_cursor (editor->cursors()->left_side_trim);
3165 } else if (trimmable && x_fraction >= 0.8 && x_fraction < 1.0) {
3166 editor->set_canvas_cursor (editor->cursors()->right_side_trim);
3168 if (pre_enter_cursor && can_set_cursor) {
3169 editor->set_canvas_cursor (pre_enter_cursor);
3175 MidiRegionView::set_frame_color()
3179 TimeAxisViewItem::set_frame_color ();
3186 f = ARDOUR_UI::config()->canvasvar_SelectedFrameBase.get();
3187 } else if (high_enough_for_name) {
3188 f= ARDOUR_UI::config()->canvasvar_MidiFrameBase.get();
3193 if (!rect_visible) {
3194 f = UINT_RGBA_CHANGE_A (f, 0);
3197 frame->property_fill_color_rgba() = f;
3201 MidiRegionView::midi_channel_mode_changed(ChannelMode mode, uint16_t mask)
3203 if (mode == ForceChannel) {
3204 mask = 0xFFFF; // Show all notes as active (below)
3207 // Update notes for selection
3208 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3209 (*i)->on_channel_selection_change(mask);
3212 _last_channel_selection = mask;
3214 _patch_changes.clear ();
3215 display_patch_changes ();
3219 MidiRegionView::instrument_settings_changed ()
3225 MidiRegionView::cut_copy_clear (Editing::CutCopyOp op)
3227 if (_selection.empty()) {
3231 PublicEditor& editor (trackview.editor());
3235 /* XXX what to do ? */
3239 editor.get_cut_buffer().add (selection_as_cut_buffer());
3247 start_note_diff_command();
3249 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3256 note_diff_remove_note (*i);
3266 MidiRegionView::selection_as_cut_buffer () const
3270 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3271 NoteType* n = (*i)->note().get();
3272 notes.insert (boost::shared_ptr<NoteType> (new NoteType (*n)));
3275 MidiCutBuffer* cb = new MidiCutBuffer (trackview.session());
3281 /** This method handles undo */
3283 MidiRegionView::paste (framepos_t pos, float times, const MidiCutBuffer& mcb)
3289 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("MIDI paste @ %1 times %2\n", pos, times));
3291 trackview.session()->begin_reversible_command (_("paste"));
3293 start_note_diff_command (_("paste"));
3295 Evoral::MusicalTime beat_delta;
3296 Evoral::MusicalTime paste_pos_beats;
3297 Evoral::MusicalTime duration;
3298 Evoral::MusicalTime end_point = 0;
3300 duration = (*mcb.notes().rbegin())->end_time() - (*mcb.notes().begin())->time();
3301 paste_pos_beats = absolute_frames_to_source_beats (pos);
3302 beat_delta = (*mcb.notes().begin())->time() - paste_pos_beats;
3303 paste_pos_beats = 0;
3305 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste data spans from %1 to %2 (%3) ; paste pos beats = %4 (based on %5 - %6 ; beat delta = %7\n",
3306 (*mcb.notes().begin())->time(),
3307 (*mcb.notes().rbegin())->end_time(),
3308 duration, pos, _region->position(),
3309 paste_pos_beats, beat_delta));
3313 for (int n = 0; n < (int) times; ++n) {
3315 for (Notes::const_iterator i = mcb.notes().begin(); i != mcb.notes().end(); ++i) {
3317 boost::shared_ptr<NoteType> copied_note (new NoteType (*((*i).get())));
3318 copied_note->set_time (paste_pos_beats + copied_note->time() - beat_delta);
3320 /* make all newly added notes selected */
3322 note_diff_add_note (copied_note, true);
3323 end_point = copied_note->end_time();
3326 paste_pos_beats += duration;
3329 /* if we pasted past the current end of the region, extend the region */
3331 framepos_t end_frame = source_beats_to_absolute_frames (end_point);
3332 framepos_t region_end = _region->position() + _region->length() - 1;
3334 if (end_frame > region_end) {
3336 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste extended region from %1 to %2\n", region_end, end_frame));
3338 _region->clear_changes ();
3339 _region->set_length (end_frame - _region->position());
3340 trackview.session()->add_command (new StatefulDiffCommand (_region));
3345 trackview.session()->commit_reversible_command ();
3348 struct EventNoteTimeEarlyFirstComparator {
3349 bool operator() (CanvasNoteEvent* a, CanvasNoteEvent* b) {
3350 return a->note()->time() < b->note()->time();
3355 MidiRegionView::time_sort_events ()
3357 if (!_sort_needed) {
3361 EventNoteTimeEarlyFirstComparator cmp;
3364 _sort_needed = false;
3368 MidiRegionView::goto_next_note (bool add_to_selection)
3370 bool use_next = false;
3372 if (_events.back()->selected()) {
3376 time_sort_events ();
3378 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3379 uint16_t const channel_mask = mtv->channel_selector().get_selected_channels ();
3381 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3382 if ((*i)->selected()) {
3385 } else if (use_next) {
3386 if (channel_mask & (1 << (*i)->note()->channel())) {
3387 if (!add_to_selection) {
3390 note_selected (*i, true, false);
3397 /* use the first one */
3399 if (!_events.empty() && (channel_mask & (1 << _events.front()->note()->channel ()))) {
3400 unique_select (_events.front());
3405 MidiRegionView::goto_previous_note (bool add_to_selection)
3407 bool use_next = false;
3409 if (_events.front()->selected()) {
3413 time_sort_events ();
3415 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3416 uint16_t const channel_mask = mtv->channel_selector().get_selected_channels ();
3418 for (Events::reverse_iterator i = _events.rbegin(); i != _events.rend(); ++i) {
3419 if ((*i)->selected()) {
3422 } else if (use_next) {
3423 if (channel_mask & (1 << (*i)->note()->channel())) {
3424 if (!add_to_selection) {
3427 note_selected (*i, true, false);
3434 /* use the last one */
3436 if (!_events.empty() && (channel_mask & (1 << (*_events.rbegin())->note()->channel ()))) {
3437 unique_select (*(_events.rbegin()));
3442 MidiRegionView::selection_as_notelist (Notes& selected, bool allow_all_if_none_selected)
3444 bool had_selected = false;
3446 time_sort_events ();
3448 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3449 if ((*i)->selected()) {
3450 selected.insert ((*i)->note());
3451 had_selected = true;
3455 if (allow_all_if_none_selected && !had_selected) {
3456 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3457 selected.insert ((*i)->note());
3463 MidiRegionView::update_ghost_note (double x, double y)
3465 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3470 _note_group->w2i (x, y);
3472 PublicEditor& editor = trackview.editor ();
3474 framepos_t const unsnapped_frame = editor.pixel_to_frame (x);
3475 framecnt_t grid_frames;
3476 framepos_t const f = snap_frame_to_grid_underneath (unsnapped_frame, grid_frames);
3478 /* use region_frames... because we are converting a delta within the region
3482 double length = editor.get_grid_type_as_beats (success, unsnapped_frame);
3488 /* note that this sets the time of the ghost note in beats relative to
3489 the start of the source; that is how all note times are stored.
3491 _ghost_note->note()->set_time (absolute_frames_to_source_beats (f + _region->position ()));
3492 _ghost_note->note()->set_length (length);
3493 _ghost_note->note()->set_note (midi_stream_view()->y_to_note (y));
3494 _ghost_note->note()->set_channel (mtv->get_channel_for_add ());
3496 /* the ghost note does not appear in ghost regions, so pass false in here */
3497 update_note (_ghost_note, false);
3499 show_verbose_cursor (_ghost_note->note ());
3503 MidiRegionView::create_ghost_note (double x, double y)
3505 remove_ghost_note ();
3507 boost::shared_ptr<NoteType> g (new NoteType);
3508 _ghost_note = new NoEventCanvasNote (*this, *_note_group, g);
3509 _ghost_note->property_outline_color_rgba() = 0x000000aa;
3510 update_ghost_note (x, y);
3511 _ghost_note->show ();
3516 show_verbose_cursor (_ghost_note->note ());
3520 MidiRegionView::snap_changed ()
3526 create_ghost_note (_last_ghost_x, _last_ghost_y);
3530 MidiRegionView::drop_down_keys ()
3532 _mouse_state = None;
3536 MidiRegionView::maybe_select_by_position (GdkEventButton* ev, double /*x*/, double y)
3538 double note = midi_stream_view()->y_to_note(y);
3540 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3542 uint16_t chn_mask = mtv->channel_selector().get_selected_channels();
3544 if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
3545 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchGreaterThanOrEqual, (uint8_t) floor (note), chn_mask);
3546 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
3547 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchLessThanOrEqual, (uint8_t) floor (note), chn_mask);
3552 bool add_mrv_selection = false;
3554 if (_selection.empty()) {
3555 add_mrv_selection = true;
3558 for (Events::iterator i = e.begin(); i != e.end(); ++i) {
3559 if (_selection.insert (*i).second) {
3560 (*i)->set_selected (true);
3564 if (add_mrv_selection) {
3565 PublicEditor& editor (trackview.editor());
3566 editor.get_selection().add (this);
3571 MidiRegionView::color_handler ()
3573 RegionView::color_handler ();
3575 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3576 (*i)->set_selected ((*i)->selected()); // will change color
3579 /* XXX probably more to do here */
3583 MidiRegionView::enable_display (bool yn)
3585 RegionView::enable_display (yn);
3592 MidiRegionView::show_step_edit_cursor (Evoral::MusicalTime pos)
3594 if (_step_edit_cursor == 0) {
3595 ArdourCanvas::Group* const group = (ArdourCanvas::Group*)get_canvas_group();
3597 _step_edit_cursor = new ArdourCanvas::SimpleRect (*group);
3598 _step_edit_cursor->property_y1() = 0;
3599 _step_edit_cursor->property_y2() = midi_stream_view()->contents_height();
3600 _step_edit_cursor->property_fill_color_rgba() = RGBA_TO_UINT (45,0,0,90);
3601 _step_edit_cursor->property_outline_color_rgba() = RGBA_TO_UINT (85,0,0,90);
3604 move_step_edit_cursor (pos);
3605 _step_edit_cursor->show ();
3609 MidiRegionView::move_step_edit_cursor (Evoral::MusicalTime pos)
3611 _step_edit_cursor_position = pos;
3613 if (_step_edit_cursor) {
3614 double pixel = trackview.editor().frame_to_pixel (region_beats_to_region_frames (pos));
3615 _step_edit_cursor->property_x1() = pixel;
3616 set_step_edit_cursor_width (_step_edit_cursor_width);
3621 MidiRegionView::hide_step_edit_cursor ()
3623 if (_step_edit_cursor) {
3624 _step_edit_cursor->hide ();
3629 MidiRegionView::set_step_edit_cursor_width (Evoral::MusicalTime beats)
3631 _step_edit_cursor_width = beats;
3633 if (_step_edit_cursor) {
3634 _step_edit_cursor->property_x2() = _step_edit_cursor->property_x1() + trackview.editor().frame_to_pixel (region_beats_to_region_frames (beats));
3638 /** Called when a diskstream on our track has received some data. Update the view, if applicable.
3639 * @param w Source that the data will end up in.
3642 MidiRegionView::data_recorded (boost::weak_ptr<MidiSource> w)
3644 if (!_active_notes) {
3645 /* we aren't actively being recorded to */
3649 boost::shared_ptr<MidiSource> src = w.lock ();
3650 if (!src || src != midi_region()->midi_source()) {
3651 /* recorded data was not destined for our source */
3655 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (&trackview);
3657 boost::shared_ptr<MidiBuffer> buf = mtv->midi_track()->get_gui_feed_buffer ();
3659 BeatsFramesConverter converter (trackview.session()->tempo_map(), mtv->midi_track()->get_capture_start_frame (0));
3661 framepos_t back = max_framepos;
3663 for (MidiBuffer::iterator i = buf->begin(); i != buf->end(); ++i) {
3664 Evoral::MIDIEvent<MidiBuffer::TimeType> const ev (*i, false);
3665 assert (ev.buffer ());
3667 /* ev.time() is in session frames, so (ev.time() - converter.origin_b()) is
3668 frames from the start of the source, and so time_beats is in terms of the
3672 Evoral::MusicalTime const time_beats = converter.from (ev.time () - converter.origin_b ());
3674 if (ev.type() == MIDI_CMD_NOTE_ON) {
3676 boost::shared_ptr<NoteType> note (
3677 new NoteType (ev.channel(), time_beats, 0, ev.note(), ev.velocity())
3680 add_note (note, true);
3682 /* fix up our note range */
3683 if (ev.note() < _current_range_min) {
3684 midi_stream_view()->apply_note_range (ev.note(), _current_range_max, true);
3685 } else if (ev.note() > _current_range_max) {
3686 midi_stream_view()->apply_note_range (_current_range_min, ev.note(), true);
3689 } else if (ev.type() == MIDI_CMD_NOTE_OFF) {
3690 resolve_note (ev.note (), time_beats);
3696 midi_stream_view()->check_record_layers (region(), back);
3700 MidiRegionView::trim_front_starting ()
3702 /* Reparent the note group to the region view's parent, so that it doesn't change
3703 when the region view is trimmed.
3705 _temporary_note_group = new ArdourCanvas::Group (*group->property_parent ());
3706 _temporary_note_group->move (group->property_x(), group->property_y());
3707 _note_group->reparent (*_temporary_note_group);
3711 MidiRegionView::trim_front_ending ()
3713 _note_group->reparent (*group);
3714 delete _temporary_note_group;
3715 _temporary_note_group = 0;
3717 if (_region->start() < 0) {
3718 /* Trim drag made start time -ve; fix this */
3719 midi_region()->fix_negative_start ();
3724 MidiRegionView::edit_patch_change (ArdourCanvas::CanvasPatchChange* pc)
3726 PatchChangeDialog d (&_source_relative_time_converter, trackview.session(), *pc->patch (), instrument_info(), Gtk::Stock::APPLY);
3727 if (d.run () != Gtk::RESPONSE_ACCEPT) {
3731 change_patch_change (pc->patch(), d.patch ());
3736 MidiRegionView::show_verbose_cursor (boost::shared_ptr<NoteType> n) const
3739 snprintf (buf, sizeof (buf), "%s (%d) Chn %d\nVel %d",
3740 Evoral::midi_note_name (n->note()).c_str(),
3742 (int) n->channel() + 1,
3743 (int) n->velocity());
3745 show_verbose_cursor (buf, 10, 20);
3749 MidiRegionView::show_verbose_cursor (string const & text, double xoffset, double yoffset) const
3753 trackview.editor().get_pointer_position (wx, wy);
3758 /* Flip the cursor above the mouse pointer if it would overlap the bottom of the canvas */
3760 double x1, y1, x2, y2;
3761 trackview.editor().verbose_cursor()->canvas_item()->get_bounds (x1, y1, x2, y2);
3763 if ((wy + y2 - y1) > trackview.editor().canvas_height()) {
3764 wy -= (y2 - y1) + 2 * yoffset;
3767 trackview.editor().verbose_cursor()->set (text, wx, wy);
3768 trackview.editor().verbose_cursor()->show ();
3771 /** @param p A session framepos.
3772 * @param grid_frames Filled in with the number of frames that a grid interval is at p.
3773 * @return p snapped to the grid subdivision underneath it.
3776 MidiRegionView::snap_frame_to_grid_underneath (framepos_t p, framecnt_t& grid_frames) const
3778 PublicEditor& editor = trackview.editor ();
3781 Evoral::MusicalTime grid_beats = editor.get_grid_type_as_beats (success, p);
3787 grid_frames = region_beats_to_region_frames (grid_beats);
3789 /* Hack so that we always snap to the note that we are over, instead of snapping
3790 to the next one if we're more than halfway through the one we're over.
3792 if (editor.snap_mode() == SnapNormal && p >= grid_frames / 2) {
3793 p -= grid_frames / 2;
3796 return snap_frame_to_frame (p);
3799 /** Called when the selection has been cleared in any MidiRegionView.
3800 * @param rv MidiRegionView that the selection was cleared in.
3803 MidiRegionView::selection_cleared (MidiRegionView* rv)
3809 /* Clear our selection in sympathy; but don't signal the fact */
3810 clear_selection (false);
3814 MidiRegionView::note_button_release ()
3816 delete _note_player;