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.
26 #include "gtkmm2ext/gtk_ui.h"
28 #include <sigc++/signal.h>
30 #include "pbd/memento_command.h"
31 #include "pbd/stateful_diff_command.h"
33 #include "ardour/midi_model.h"
34 #include "ardour/midi_patch_manager.h"
35 #include "ardour/midi_region.h"
36 #include "ardour/midi_source.h"
37 #include "ardour/midi_track.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 "canvas/debug.h"
47 #include "canvas/text.h"
49 #include "automation_region_view.h"
50 #include "automation_time_axis.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 "route_time_axis.h"
69 #include "rgb_macros.h"
70 #include "selection.h"
71 #include "streamview.h"
72 #include "patch_change_dialog.h"
73 #include "verbose_cursor.h"
74 #include "ardour_ui.h"
77 #include "patch_change.h"
82 using namespace ARDOUR;
84 using namespace Editing;
85 using Gtkmm2ext::Keyboard;
87 PBD::Signal1<void, MidiRegionView *> MidiRegionView::SelectionCleared;
89 #define MIDI_BP_ZERO ((Config->get_first_midi_bank_is_zero())?0:1)
91 MidiRegionView::MidiRegionView (ArdourCanvas::Container *parent, RouteTimeAxisView &tv,
92 boost::shared_ptr<MidiRegion> r, double spu, uint32_t basic_color)
93 : RegionView (parent, tv, r, spu, basic_color)
94 , _current_range_min(0)
95 , _current_range_max(0)
97 , _note_group (new ArdourCanvas::Container (group))
98 , _note_diff_command (0)
100 , _step_edit_cursor (0)
101 , _step_edit_cursor_width (1.0)
102 , _step_edit_cursor_position (0.0)
103 , _channel_selection_scoped_note (0)
104 , _temporary_note_group (0)
107 , _sort_needed (true)
108 , _optimization_iterator (_events.end())
110 , _no_sound_notes (false)
113 , pre_enter_cursor (0)
114 , pre_press_cursor (0)
117 CANVAS_DEBUG_NAME (_note_group, string_compose ("note group for %1", get_item_name()));
118 _note_group->raise_to_top();
119 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
121 Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&MidiRegionView::parameter_changed, this, _1), gui_context());
122 connect_to_diskstream ();
124 SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), boost::bind (&MidiRegionView::selection_cleared, this, _1), gui_context ());
127 MidiRegionView::MidiRegionView (ArdourCanvas::Container *parent, RouteTimeAxisView &tv,
128 boost::shared_ptr<MidiRegion> r, double spu, uint32_t basic_color,
129 TimeAxisViewItem::Visibility visibility)
130 : RegionView (parent, tv, r, spu, basic_color, false, visibility)
131 , _current_range_min(0)
132 , _current_range_max(0)
134 , _note_group (new ArdourCanvas::Container (parent))
135 , _note_diff_command (0)
137 , _step_edit_cursor (0)
138 , _step_edit_cursor_width (1.0)
139 , _step_edit_cursor_position (0.0)
140 , _channel_selection_scoped_note (0)
141 , _temporary_note_group (0)
144 , _sort_needed (true)
145 , _optimization_iterator (_events.end())
147 , _no_sound_notes (false)
150 , pre_enter_cursor (0)
151 , pre_press_cursor (0)
154 CANVAS_DEBUG_NAME (_note_group, string_compose ("note group for %1", get_item_name()));
155 _note_group->raise_to_top();
157 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
159 connect_to_diskstream ();
161 SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), boost::bind (&MidiRegionView::selection_cleared, this, _1), gui_context ());
165 MidiRegionView::parameter_changed (std::string const & p)
167 if (p == "display-first-midi-bank-as-zero") {
168 if (_enable_display) {
174 MidiRegionView::MidiRegionView (const MidiRegionView& other)
175 : sigc::trackable(other)
177 , _current_range_min(0)
178 , _current_range_max(0)
180 , _note_group (new ArdourCanvas::Container (get_canvas_group()))
181 , _note_diff_command (0)
183 , _step_edit_cursor (0)
184 , _step_edit_cursor_width (1.0)
185 , _step_edit_cursor_position (0.0)
186 , _channel_selection_scoped_note (0)
187 , _temporary_note_group (0)
190 , _sort_needed (true)
191 , _optimization_iterator (_events.end())
193 , _no_sound_notes (false)
196 , pre_enter_cursor (0)
197 , pre_press_cursor (0)
203 MidiRegionView::MidiRegionView (const MidiRegionView& other, boost::shared_ptr<MidiRegion> region)
204 : RegionView (other, boost::shared_ptr<Region> (region))
205 , _current_range_min(0)
206 , _current_range_max(0)
208 , _note_group (new ArdourCanvas::Container (get_canvas_group()))
209 , _note_diff_command (0)
211 , _step_edit_cursor (0)
212 , _step_edit_cursor_width (1.0)
213 , _step_edit_cursor_position (0.0)
214 , _channel_selection_scoped_note (0)
215 , _temporary_note_group (0)
218 , _sort_needed (true)
219 , _optimization_iterator (_events.end())
221 , _no_sound_notes (false)
224 , pre_enter_cursor (0)
225 , pre_press_cursor (0)
232 MidiRegionView::init (bool wfd)
234 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
236 NoteBase::NoteBaseDeleted.connect (note_delete_connection, MISSING_INVALIDATOR,
237 boost::bind (&MidiRegionView::maybe_remove_deleted_note_from_selection, this, _1),
241 midi_region()->midi_source(0)->load_model();
244 _model = midi_region()->midi_source(0)->model();
245 _enable_display = false;
247 RegionView::init (false);
249 set_height (trackview.current_height());
252 region_sync_changed ();
253 region_resized (ARDOUR::bounds_change);
258 _enable_display = true;
261 display_model (_model);
265 reset_width_dependent_items (_pixel_width);
267 group->raise_to_top();
269 midi_view()->midi_track()->PlaybackChannelModeChanged.connect (_channel_mode_changed_connection, invalidator (*this),
270 boost::bind (&MidiRegionView::midi_channel_mode_changed, this),
273 instrument_info().Changed.connect (_instrument_changed_connection, invalidator (*this),
274 boost::bind (&MidiRegionView::instrument_settings_changed, this), gui_context());
276 trackview.editor().SnapChanged.connect(snap_changed_connection, invalidator(*this),
277 boost::bind (&MidiRegionView::snap_changed, this),
280 Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&MidiRegionView::parameter_changed, this, _1), gui_context());
281 connect_to_diskstream ();
283 SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), boost::bind (&MidiRegionView::selection_cleared, this, _1), gui_context ());
287 MidiRegionView::instrument_info () const
289 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
290 return route_ui->route()->instrument_info();
293 const boost::shared_ptr<ARDOUR::MidiRegion>
294 MidiRegionView::midi_region() const
296 return boost::dynamic_pointer_cast<ARDOUR::MidiRegion>(_region);
300 MidiRegionView::connect_to_diskstream ()
302 midi_view()->midi_track()->DataRecorded.connect(
303 *this, invalidator(*this),
304 boost::bind (&MidiRegionView::data_recorded, this, _1),
309 MidiRegionView::canvas_group_event(GdkEvent* ev)
314 case GDK_ENTER_NOTIFY:
315 case GDK_LEAVE_NOTIFY:
316 _last_event_x = ev->crossing.x;
317 _last_event_y = ev->crossing.y;
319 case GDK_MOTION_NOTIFY:
320 _last_event_x = ev->motion.x;
321 _last_event_y = ev->motion.y;
327 if (ev->type == GDK_2BUTTON_PRESS) {
328 // cannot use double-click to exit internal mode if single-click is being used
329 MouseMode m = trackview.editor().current_mouse_mode();
331 if ((m != MouseObject || !Keyboard::modifier_state_contains (ev->button.state, Keyboard::insert_note_modifier())) && (m != MouseDraw)) {
332 return trackview.editor().toggle_internal_editing_from_double_click (ev);
336 if ((!trackview.editor().internal_editing() && trackview.editor().current_mouse_mode() != MouseGain) ||
337 (trackview.editor().current_mouse_mode() == MouseTimeFX)) {
338 // handle non-internal-edit/non-draw modes elsewhere
339 return RegionView::canvas_group_event (ev);
344 if (scroll (&ev->scroll)) {
350 return key_press (&ev->key);
352 case GDK_KEY_RELEASE:
353 return key_release (&ev->key);
355 case GDK_BUTTON_PRESS:
356 return button_press (&ev->button);
358 case GDK_BUTTON_RELEASE:
359 r = button_release (&ev->button);
364 case GDK_ENTER_NOTIFY:
365 return enter_notify (&ev->crossing);
367 case GDK_LEAVE_NOTIFY:
368 return leave_notify (&ev->crossing);
370 case GDK_MOTION_NOTIFY:
371 return motion (&ev->motion);
377 return trackview.editor().canvas_region_view_event (ev, group, this);
381 MidiRegionView::enter_notify (GdkEventCrossing* ev)
383 trackview.editor().MouseModeChanged.connect (
384 _mouse_mode_connection, invalidator (*this), boost::bind (&MidiRegionView::mouse_mode_changed, this), gui_context ()
387 if (trackview.editor().current_mouse_mode() == MouseDraw && _mouse_state != AddDragging) {
388 create_ghost_note (ev->x, ev->y);
391 if (!trackview.editor().internal_editing()) {
392 Keyboard::magic_widget_drop_focus();
394 Keyboard::magic_widget_grab_focus();
398 // if current operation is non-operational in a midi region, change the cursor to so indicate
399 if (trackview.editor().current_mouse_mode() == MouseGain) {
400 Editor* editor = dynamic_cast<Editor *> (&trackview.editor());
401 pre_enter_cursor = editor->get_canvas_cursor();
402 editor->set_canvas_cursor(editor->cursors()->timebar);
409 MidiRegionView::leave_notify (GdkEventCrossing*)
411 _mouse_mode_connection.disconnect ();
413 trackview.editor().verbose_cursor()->hide ();
414 remove_ghost_note ();
416 if (trackview.editor().internal_editing()) {
417 Keyboard::magic_widget_drop_focus();
420 if (pre_enter_cursor) {
421 Editor* editor = dynamic_cast<Editor *> (&trackview.editor());
422 editor->set_canvas_cursor(pre_enter_cursor);
429 MidiRegionView::mouse_mode_changed ()
431 if (trackview.editor().current_mouse_mode() == MouseDraw && trackview.editor().internal_editing()) {
432 create_ghost_note (_last_event_x, _last_event_y);
434 remove_ghost_note ();
435 trackview.editor().verbose_cursor()->hide ();
438 if (!trackview.editor().internal_editing()) {
439 Keyboard::magic_widget_drop_focus();
441 Keyboard::magic_widget_grab_focus();
447 MidiRegionView::button_press (GdkEventButton* ev)
449 if (ev->button != 1) {
453 Editor* editor = dynamic_cast<Editor *> (&trackview.editor());
454 MouseMode m = editor->current_mouse_mode();
456 if (m == MouseObject && Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())) {
457 pre_press_cursor = editor->get_canvas_cursor ();
458 editor->set_canvas_cursor (editor->cursors()->midi_pencil);
461 if (_mouse_state != SelectTouchDragging) {
463 _pressed_button = ev->button;
464 _mouse_state = Pressed;
469 _pressed_button = ev->button;
475 MidiRegionView::button_release (GdkEventButton* ev)
477 double event_x, event_y;
479 if (ev->button != 1) {
486 group->canvas_to_item (event_x, event_y);
489 PublicEditor& editor = trackview.editor ();
491 if (pre_press_cursor) {
492 dynamic_cast<Editor*>(&editor)->set_canvas_cursor (pre_press_cursor, false);
493 pre_press_cursor = 0;
496 switch (_mouse_state) {
497 case Pressed: // Clicked
499 switch (editor.current_mouse_mode()) {
501 /* no motion occured - simple click */
510 if (Keyboard::is_insert_note_event(ev)) {
512 double event_x, event_y;
516 group->canvas_to_item (event_x, event_y);
519 Evoral::MusicalTime beats = editor.get_grid_type_as_beats (success, editor.pixel_to_sample (event_x));
525 /* Shorten the length by 1 tick so that we can add a new note at the next
526 grid snap without it overlapping this one.
528 beats -= 1.0 / Timecode::BBT_Time::ticks_per_beat;
530 create_note_at (editor.pixel_to_sample (event_x), event_y, beats, true);
538 Evoral::MusicalTime beats = editor.get_grid_type_as_beats (success, editor.pixel_to_sample (event_x));
544 /* Shorten the length by 1 tick so that we can add a new note at the next
545 grid snap without it overlapping this one.
547 beats -= 1.0 / Timecode::BBT_Time::ticks_per_beat;
549 create_note_at (editor.pixel_to_sample (event_x), event_y, beats, true);
560 case SelectRectDragging:
562 editor.drags()->end_grab ((GdkEvent *) ev);
564 create_ghost_note (ev->x, ev->y);
576 MidiRegionView::motion (GdkEventMotion* ev)
578 PublicEditor& editor = trackview.editor ();
580 if (!_ghost_note && editor.current_mouse_mode() == MouseObject &&
581 Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier()) &&
582 _mouse_state != AddDragging) {
584 create_ghost_note (ev->x, ev->y);
586 } else if (_ghost_note && editor.current_mouse_mode() == MouseObject &&
587 Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())) {
589 update_ghost_note (ev->x, ev->y);
591 } else if (_ghost_note && editor.current_mouse_mode() == MouseObject) {
593 remove_ghost_note ();
594 editor.verbose_cursor()->hide ();
596 } else if (_ghost_note && editor.current_mouse_mode() == MouseDraw) {
598 update_ghost_note (ev->x, ev->y);
601 /* any motion immediately hides velocity text that may have been visible */
603 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
604 (*i)->hide_velocity ();
607 switch (_mouse_state) {
610 if (_pressed_button == 1) {
612 MouseMode m = editor.current_mouse_mode();
614 if (m == MouseDraw || (m == MouseObject && Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier()))) {
615 editor.drags()->set (new NoteCreateDrag (dynamic_cast<Editor *> (&editor), group, this), (GdkEvent *) ev);
616 _mouse_state = AddDragging;
617 remove_ghost_note ();
618 editor.verbose_cursor()->hide ();
620 } else if (m == MouseObject) {
621 editor.drags()->set (new MidiRubberbandSelectDrag (dynamic_cast<Editor *> (&editor), this), (GdkEvent *) ev);
623 _mouse_state = SelectRectDragging;
625 } else if (m == MouseRange) {
626 editor.drags()->set (new MidiVerticalSelectDrag (dynamic_cast<Editor *> (&editor), this), (GdkEvent *) ev);
627 _mouse_state = SelectVerticalDragging;
634 case SelectRectDragging:
635 case SelectVerticalDragging:
637 editor.drags()->motion_handler ((GdkEvent *) ev, false);
640 case SelectTouchDragging:
648 /* we may be dragging some non-note object (eg. patch-change, sysex)
651 return editor.drags()->motion_handler ((GdkEvent *) ev, false);
656 MidiRegionView::scroll (GdkEventScroll* ev)
658 if (_selection.empty()) {
662 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
663 /* XXX: bit of a hack; allow PrimaryModifier scroll through so that
664 it still works for zoom.
669 trackview.editor().verbose_cursor()->hide ();
671 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
672 bool together = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
674 if (ev->direction == GDK_SCROLL_UP) {
675 change_velocities (true, fine, false, together);
676 } else if (ev->direction == GDK_SCROLL_DOWN) {
677 change_velocities (false, fine, false, together);
679 /* left, right: we don't use them */
687 MidiRegionView::key_press (GdkEventKey* ev)
689 /* since GTK bindings are generally activated on press, and since
690 detectable auto-repeat is the name of the game and only sends
691 repeated presses, carry out key actions at key press, not release.
694 bool unmodified = Keyboard::no_modifier_keys_pressed (ev);
696 if (unmodified && (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R)) {
697 _mouse_state = SelectTouchDragging;
700 } else if (ev->keyval == GDK_Escape && unmodified) {
704 } else if (unmodified && (ev->keyval == GDK_comma || ev->keyval == GDK_period)) {
706 bool start = (ev->keyval == GDK_comma);
707 bool end = (ev->keyval == GDK_period);
708 bool shorter = Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier);
709 bool fine = Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
711 change_note_lengths (fine, shorter, 0.0, start, end);
715 } else if ((ev->keyval == GDK_BackSpace || ev->keyval == GDK_Delete) && unmodified) {
717 if (_selection.empty()) {
724 } else if (ev->keyval == GDK_Tab) {
726 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
727 goto_previous_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
729 goto_next_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
733 } else if (ev->keyval == GDK_ISO_Left_Tab) {
735 /* Shift-TAB generates ISO Left Tab, for some reason */
737 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
738 goto_previous_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
740 goto_next_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
746 } else if (ev->keyval == GDK_Up) {
748 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
749 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
750 bool together = Keyboard::modifier_state_contains (ev->state, Keyboard::Level4Modifier);
752 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
753 change_velocities (true, fine, allow_smush, together);
755 transpose (true, fine, allow_smush);
759 } else if (ev->keyval == GDK_Down) {
761 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
762 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
763 bool together = Keyboard::modifier_state_contains (ev->state, Keyboard::Level4Modifier);
765 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
766 change_velocities (false, fine, allow_smush, together);
768 transpose (false, fine, allow_smush);
772 } else if (ev->keyval == GDK_Left && unmodified) {
777 } else if (ev->keyval == GDK_Right && unmodified) {
782 } else if (ev->keyval == GDK_c && unmodified) {
786 } else if (ev->keyval == GDK_v && unmodified) {
795 MidiRegionView::key_release (GdkEventKey* ev)
797 if ((_mouse_state == SelectTouchDragging) && (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R)) {
805 MidiRegionView::channel_edit ()
807 if (_selection.empty()) {
811 /* pick a note somewhat at random (since Selection is a set<>) to
812 * provide the "current" channel for the dialog.
815 uint8_t current_channel = (*_selection.begin())->note()->channel ();
816 MidiChannelDialog channel_dialog (current_channel);
817 int ret = channel_dialog.run ();
820 case Gtk::RESPONSE_OK:
826 uint8_t new_channel = channel_dialog.active_channel ();
828 start_note_diff_command (_("channel edit"));
830 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
831 Selection::iterator next = i;
833 change_note_channel (*i, new_channel);
841 MidiRegionView::velocity_edit ()
843 if (_selection.empty()) {
847 /* pick a note somewhat at random (since Selection is a set<>) to
848 * provide the "current" velocity for the dialog.
851 uint8_t current_velocity = (*_selection.begin())->note()->velocity ();
852 MidiVelocityDialog velocity_dialog (current_velocity);
853 int ret = velocity_dialog.run ();
856 case Gtk::RESPONSE_OK:
862 uint8_t new_velocity = velocity_dialog.velocity ();
864 start_note_diff_command (_("velocity edit"));
866 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
867 Selection::iterator next = i;
869 change_note_velocity (*i, new_velocity, false);
877 MidiRegionView::show_list_editor ()
880 _list_editor = new MidiListEditor (trackview.session(), midi_region(), midi_view()->midi_track());
882 _list_editor->present ();
885 /** Add a note to the model, and the view, at a canvas (click) coordinate.
886 * \param t time in frames relative to the position of the region
887 * \param y vertical position in pixels
888 * \param length duration of the note in beats
889 * \param snap_t true to snap t to the grid, otherwise false.
892 MidiRegionView::create_note_at (framepos_t t, double y, double length, bool snap_t)
894 if (length < 2 * DBL_EPSILON) {
898 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
899 MidiStreamView* const view = mtv->midi_view();
901 const double note = view->y_to_note(y);
903 // Start of note in frames relative to region start
905 framecnt_t grid_frames;
906 t = snap_frame_to_grid_underneath (t, grid_frames);
909 const boost::shared_ptr<NoteType> new_note (
910 new NoteType (mtv->get_channel_for_add (),
911 region_frames_to_region_beats(t + _region->start()),
913 (uint8_t)note, 0x40));
915 if (_model->contains (new_note)) {
919 view->update_note_range(new_note->note());
921 MidiModel::NoteDiffCommand* cmd = _model->new_note_diff_command(_("add note"));
923 _model->apply_command(*trackview.session(), cmd);
925 play_midi_note (new_note);
929 MidiRegionView::clear_events (bool with_selection_signal)
931 clear_selection (with_selection_signal);
934 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
935 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
940 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
945 _patch_changes.clear();
947 _optimization_iterator = _events.end();
951 MidiRegionView::display_model(boost::shared_ptr<MidiModel> model)
955 content_connection.disconnect ();
956 _model->ContentsChanged.connect (content_connection, invalidator (*this), boost::bind (&MidiRegionView::redisplay_model, this), gui_context());
960 if (_enable_display) {
966 MidiRegionView::start_note_diff_command (string name)
968 if (!_note_diff_command) {
969 _note_diff_command = _model->new_note_diff_command (name);
974 MidiRegionView::note_diff_add_note (const boost::shared_ptr<NoteType> note, bool selected, bool show_velocity)
976 if (_note_diff_command) {
977 _note_diff_command->add (note);
980 _marked_for_selection.insert(note);
983 _marked_for_velocity.insert(note);
988 MidiRegionView::note_diff_remove_note (NoteBase* ev)
990 if (_note_diff_command && ev->note()) {
991 _note_diff_command->remove(ev->note());
996 MidiRegionView::note_diff_add_change (NoteBase* ev,
997 MidiModel::NoteDiffCommand::Property property,
1000 if (_note_diff_command) {
1001 _note_diff_command->change (ev->note(), property, val);
1006 MidiRegionView::note_diff_add_change (NoteBase* ev,
1007 MidiModel::NoteDiffCommand::Property property,
1008 Evoral::MusicalTime val)
1010 if (_note_diff_command) {
1011 _note_diff_command->change (ev->note(), property, val);
1016 MidiRegionView::apply_diff (bool as_subcommand)
1020 if (!_note_diff_command) {
1024 if ((add_or_remove = _note_diff_command->adds_or_removes())) {
1025 // Mark all selected notes for selection when model reloads
1026 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1027 _marked_for_selection.insert((*i)->note());
1031 if (as_subcommand) {
1032 _model->apply_command_as_subcommand (*trackview.session(), _note_diff_command);
1034 _model->apply_command (*trackview.session(), _note_diff_command);
1037 _note_diff_command = 0;
1038 midi_view()->midi_track()->playlist_modified();
1040 if (add_or_remove) {
1041 _marked_for_selection.clear();
1044 _marked_for_velocity.clear();
1048 MidiRegionView::abort_command()
1050 delete _note_diff_command;
1051 _note_diff_command = 0;
1056 MidiRegionView::find_canvas_note (boost::shared_ptr<NoteType> note)
1058 if (_optimization_iterator != _events.end()) {
1059 ++_optimization_iterator;
1062 if (_optimization_iterator != _events.end() && (*_optimization_iterator)->note() == note) {
1063 return *_optimization_iterator;
1066 for (_optimization_iterator = _events.begin(); _optimization_iterator != _events.end(); ++_optimization_iterator) {
1067 if ((*_optimization_iterator)->note() == note) {
1068 return *_optimization_iterator;
1076 MidiRegionView::get_events (Events& e, Evoral::Sequence<Evoral::MusicalTime>::NoteOperator op, uint8_t val, int chan_mask)
1078 MidiModel::Notes notes;
1079 _model->get_notes (notes, op, val, chan_mask);
1081 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1082 NoteBase* cne = find_canvas_note (*n);
1090 MidiRegionView::redisplay_model()
1092 // Don't redisplay the model if we're currently recording and displaying that
1093 if (_active_notes) {
1101 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1102 (*i)->invalidate ();
1105 MidiModel::ReadLock lock(_model->read_lock());
1107 MidiModel::Notes& notes (_model->notes());
1108 _optimization_iterator = _events.begin();
1110 bool empty_when_starting = _events.empty();
1112 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1114 boost::shared_ptr<NoteType> note (*n);
1118 if (note_in_region_range (note, visible)) {
1120 if (!empty_when_starting && (cne = find_canvas_note (note)) != 0) {
1127 if ((cn = dynamic_cast<Note*>(cne)) != 0) {
1129 } else if ((ch = dynamic_cast<Hit*>(cne)) != 0) {
1141 add_note (note, visible);
1146 if (!empty_when_starting && (cne = find_canvas_note (note)) != 0) {
1154 /* remove note items that are no longer valid */
1156 if (!empty_when_starting) {
1157 for (Events::iterator i = _events.begin(); i != _events.end(); ) {
1158 if (!(*i)->valid ()) {
1160 for (vector<GhostRegion*>::iterator j = ghosts.begin(); j != ghosts.end(); ++j) {
1161 MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*j);
1163 gr->remove_note (*i);
1168 i = _events.erase (i);
1176 _patch_changes.clear();
1180 display_patch_changes ();
1182 _marked_for_selection.clear ();
1183 _marked_for_velocity.clear ();
1185 /* we may have caused _events to contain things out of order (e.g. if a note
1186 moved earlier or later). we don't generally need them in time order, but
1187 make a note that a sort is required for those cases that require it.
1190 _sort_needed = true;
1194 MidiRegionView::display_patch_changes ()
1196 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1197 uint16_t chn_mask = mtv->midi_track()->get_playback_channel_mask();
1199 for (uint8_t i = 0; i < 16; ++i) {
1200 display_patch_changes_on_channel (i, chn_mask & (1 << i));
1204 /** @param active_channel true to display patch changes fully, false to display
1205 * them `greyed-out' (as on an inactive channel)
1208 MidiRegionView::display_patch_changes_on_channel (uint8_t channel, bool active_channel)
1210 for (MidiModel::PatchChanges::const_iterator i = _model->patch_changes().begin(); i != _model->patch_changes().end(); ++i) {
1212 if ((*i)->channel() != channel) {
1216 const string patch_name = instrument_info().get_patch_name ((*i)->bank(), (*i)->program(), channel);
1217 add_canvas_patch_change (*i, patch_name, active_channel);
1222 MidiRegionView::display_sysexes()
1224 bool have_periodic_system_messages = false;
1225 bool display_periodic_messages = true;
1227 if (!Config->get_never_display_periodic_midi()) {
1229 for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
1230 const boost::shared_ptr<const Evoral::MIDIEvent<Evoral::MusicalTime> > mev =
1231 boost::static_pointer_cast<const Evoral::MIDIEvent<Evoral::MusicalTime> > (*i);
1234 if (mev->is_spp() || mev->is_mtc_quarter() || mev->is_mtc_full()) {
1235 have_periodic_system_messages = true;
1241 if (have_periodic_system_messages) {
1242 double zoom = trackview.editor().get_current_zoom (); // frames per pixel
1244 /* get an approximate value for the number of samples per video frame */
1246 double video_frame = trackview.session()->frame_rate() * (1.0/30);
1248 /* if we are zoomed out beyond than the cutoff (i.e. more
1249 * frames per pixel than frames per 4 video frames), don't
1250 * show periodic sysex messages.
1253 if (zoom > (video_frame*4)) {
1254 display_periodic_messages = false;
1258 display_periodic_messages = false;
1261 for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
1263 const boost::shared_ptr<const Evoral::MIDIEvent<Evoral::MusicalTime> > mev =
1264 boost::static_pointer_cast<const Evoral::MIDIEvent<Evoral::MusicalTime> > (*i);
1266 Evoral::MusicalTime time = (*i)->time();
1269 if (mev->is_spp() || mev->is_mtc_quarter() || mev->is_mtc_full()) {
1270 if (!display_periodic_messages) {
1278 for (uint32_t b = 0; b < (*i)->size(); ++b) {
1279 str << int((*i)->buffer()[b]);
1280 if (b != (*i)->size() -1) {
1284 string text = str.str();
1286 const double x = trackview.editor().sample_to_pixel(source_beats_to_region_frames(time));
1288 double height = midi_stream_view()->contents_height();
1290 // CAIROCANVAS: no longer passing *i (the sysex event) to the
1291 // SysEx canvas object!!!
1293 boost::shared_ptr<SysEx> sysex = boost::shared_ptr<SysEx>(
1294 new SysEx (*this, _note_group, text, height, x, 1.0));
1296 // Show unless message is beyond the region bounds
1297 if (time - _region->start() >= _region->length() || time < _region->start()) {
1303 _sys_exes.push_back(sysex);
1307 MidiRegionView::~MidiRegionView ()
1309 in_destructor = true;
1311 trackview.editor().verbose_cursor()->hide ();
1313 note_delete_connection.disconnect ();
1315 delete _list_editor;
1317 RegionViewGoingAway (this); /* EMIT_SIGNAL */
1319 if (_active_notes) {
1323 _selection_cleared_connection.disconnect ();
1326 clear_events (false);
1329 delete _note_diff_command;
1330 delete _step_edit_cursor;
1331 delete _temporary_note_group;
1335 MidiRegionView::region_resized (const PropertyChange& what_changed)
1337 RegionView::region_resized(what_changed);
1339 if (what_changed.contains (ARDOUR::Properties::position)) {
1340 set_duration(_region->length(), 0);
1341 if (_enable_display) {
1348 MidiRegionView::reset_width_dependent_items (double pixel_width)
1350 RegionView::reset_width_dependent_items(pixel_width);
1352 if (_enable_display) {
1356 for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
1357 if ((*x)->canvas_item()->width() >= _pixel_width) {
1364 move_step_edit_cursor (_step_edit_cursor_position);
1365 set_step_edit_cursor_width (_step_edit_cursor_width);
1369 MidiRegionView::set_height (double height)
1371 double old_height = _height;
1372 RegionView::set_height(height);
1374 apply_note_range (midi_stream_view()->lowest_note(),
1375 midi_stream_view()->highest_note(),
1376 height != old_height);
1379 name_text->raise_to_top();
1382 for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
1383 (*x)->set_height (midi_stream_view()->contents_height());
1386 if (_step_edit_cursor) {
1387 _step_edit_cursor->set_y1 (midi_stream_view()->contents_height());
1392 /** Apply the current note range from the stream view
1393 * by repositioning/hiding notes as necessary
1396 MidiRegionView::apply_note_range (uint8_t min, uint8_t max, bool force)
1398 if (!_enable_display) {
1402 if (!force && _current_range_min == min && _current_range_max == max) {
1406 _current_range_min = min;
1407 _current_range_max = max;
1409 for (Events::const_iterator i = _events.begin(); i != _events.end(); ++i) {
1410 NoteBase* event = *i;
1411 boost::shared_ptr<NoteType> note (event->note());
1413 if (note->note() < _current_range_min ||
1414 note->note() > _current_range_max) {
1420 if (Note* cnote = dynamic_cast<Note*>(event)) {
1422 const double y0 = 1. + floor (midi_stream_view()->note_to_y(note->note()));
1423 const double y1 = y0 + std::max(1., floor(midi_stream_view()->note_height()) - 1.);
1428 } else if (Hit* chit = dynamic_cast<Hit*>(event)) {
1435 MidiRegionView::add_ghost (TimeAxisView& tv)
1439 double unit_position = _region->position () / samples_per_pixel;
1440 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(&tv);
1441 MidiGhostRegion* ghost;
1443 if (mtv && mtv->midi_view()) {
1444 /* if ghost is inserted into midi track, use a dedicated midi ghost canvas group
1445 to allow having midi notes on top of note lines and waveforms.
1447 ghost = new MidiGhostRegion (*mtv->midi_view(), trackview, unit_position);
1449 ghost = new MidiGhostRegion (tv, trackview, unit_position);
1452 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1453 if ((note = dynamic_cast<Note*>(*i)) != 0) {
1454 ghost->add_note(note);
1458 ghost->set_height ();
1459 ghost->set_duration (_region->length() / samples_per_pixel);
1460 ghosts.push_back (ghost);
1462 GhostRegion::CatchDeletion.connect (*this, invalidator (*this), boost::bind (&RegionView::remove_ghost, this, _1), gui_context());
1468 /** Begin tracking note state for successive calls to add_event
1471 MidiRegionView::begin_write()
1473 if (_active_notes) {
1474 delete[] _active_notes;
1476 _active_notes = new Note*[128];
1477 for (unsigned i = 0; i < 128; ++i) {
1478 _active_notes[i] = 0;
1483 /** Destroy note state for add_event
1486 MidiRegionView::end_write()
1488 delete[] _active_notes;
1490 _marked_for_selection.clear();
1491 _marked_for_velocity.clear();
1495 /** Resolve an active MIDI note (while recording).
1498 MidiRegionView::resolve_note(uint8_t note, double end_time)
1500 if (midi_view()->note_mode() != Sustained) {
1504 if (_active_notes && _active_notes[note]) {
1506 /* XXX is end_time really region-centric? I think so, because
1507 this is a new region that we're recording, so source zero is
1508 the same as region zero
1510 const framepos_t end_time_frames = region_beats_to_region_frames(end_time);
1512 _active_notes[note]->set_x1 (trackview.editor().sample_to_pixel(end_time_frames));
1513 _active_notes[note]->set_outline_all ();
1514 _active_notes[note] = 0;
1520 /** Extend active notes to rightmost edge of region (if length is changed)
1523 MidiRegionView::extend_active_notes()
1525 if (!_active_notes) {
1529 for (unsigned i=0; i < 128; ++i) {
1530 if (_active_notes[i]) {
1531 _active_notes[i]->set_x1 (trackview.editor().sample_to_pixel(_region->length()));
1538 MidiRegionView::play_midi_note(boost::shared_ptr<NoteType> note)
1540 if (_no_sound_notes || !Config->get_sound_midi_notes()) {
1544 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1546 if (!route_ui || !route_ui->midi_track()) {
1550 NotePlayer* np = new NotePlayer (route_ui->midi_track ());
1554 /* NotePlayer deletes itself */
1558 MidiRegionView::start_playing_midi_note(boost::shared_ptr<NoteType> note)
1560 if (_no_sound_notes || !Config->get_sound_midi_notes()) {
1564 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1566 if (!route_ui || !route_ui->midi_track()) {
1570 delete _note_player;
1571 _note_player = new NotePlayer (route_ui->midi_track ());
1572 _note_player->add (note);
1573 _note_player->on ();
1577 MidiRegionView::start_playing_midi_chord (vector<boost::shared_ptr<NoteType> > notes)
1579 if (_no_sound_notes || !Config->get_sound_midi_notes()) {
1583 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1585 if (!route_ui || !route_ui->midi_track()) {
1589 delete _note_player;
1590 _note_player = new NotePlayer (route_ui->midi_track());
1592 for (vector<boost::shared_ptr<NoteType> >::iterator n = notes.begin(); n != notes.end(); ++n) {
1593 _note_player->add (*n);
1596 _note_player->on ();
1601 MidiRegionView::note_in_region_range (const boost::shared_ptr<NoteType> note, bool& visible) const
1603 const framepos_t note_start_frames = source_beats_to_region_frames (note->time());
1604 bool outside = (note_start_frames < 0) || (note_start_frames > _region->last_frame());
1606 visible = (note->note() >= midi_stream_view()->lowest_note()) &&
1607 (note->note() <= midi_stream_view()->highest_note());
1612 /** Update a canvas note's size from its model note.
1613 * @param ev Canvas note to update.
1614 * @param update_ghost_regions true to update the note in any ghost regions that we have, otherwise false.
1617 MidiRegionView::update_note (Note* ev, bool update_ghost_regions)
1619 boost::shared_ptr<NoteType> note = ev->note();
1620 const double x = trackview.editor().sample_to_pixel (source_beats_to_region_frames (note->time()));
1621 const double y0 = 1 + floor(midi_stream_view()->note_to_y(note->note()));
1626 /* trim note display to not overlap the end of its region */
1628 if (note->length() > 0) {
1629 const framepos_t note_end_frames = min (source_beats_to_region_frames (note->end_time()), _region->length());
1630 ev->set_x1 (trackview.editor().sample_to_pixel (note_end_frames));
1632 ev->set_x1 (trackview.editor().sample_to_pixel (_region->length()));
1635 ev->set_y1 (y0 + std::max(1., floor(midi_stream_view()->note_height()) - 1));
1637 if (note->length() == 0) {
1638 if (_active_notes && note->note() < 128) {
1639 // If this note is already active there's a stuck note,
1640 // finish the old note rectangle
1641 if (_active_notes[note->note()]) {
1642 Note* const old_rect = _active_notes[note->note()];
1643 boost::shared_ptr<NoteType> old_note = old_rect->note();
1644 old_rect->set_x1 (x);
1645 old_rect->set_outline_all ();
1647 _active_notes[note->note()] = ev;
1649 /* outline all but right edge */
1650 ev->set_outline_what (ArdourCanvas::Rectangle::What (
1651 ArdourCanvas::Rectangle::TOP|
1652 ArdourCanvas::Rectangle::LEFT|
1653 ArdourCanvas::Rectangle::BOTTOM));
1655 /* outline all edges */
1656 ev->set_outline_all ();
1659 if (update_ghost_regions) {
1660 for (std::vector<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
1661 MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*i);
1663 gr->update_note (ev);
1670 MidiRegionView::update_hit (Hit* ev)
1672 boost::shared_ptr<NoteType> note = ev->note();
1674 const framepos_t note_start_frames = source_beats_to_region_frames(note->time());
1675 const double x = trackview.editor().sample_to_pixel(note_start_frames);
1676 const double diamond_size = std::max(1., floor(midi_stream_view()->note_height()) - 2.);
1677 const double y = 1.5 + floor(midi_stream_view()->note_to_y(note->note())) + diamond_size * .5;
1679 ev->set_position (ArdourCanvas::Duple (x, y));
1680 ev->set_height (diamond_size);
1683 /** Add a MIDI note to the view (with length).
1685 * If in sustained mode, notes with length 0 will be considered active
1686 * notes, and resolve_note should be called when the corresponding note off
1687 * event arrives, to properly display the note.
1690 MidiRegionView::add_note(const boost::shared_ptr<NoteType> note, bool visible)
1692 NoteBase* event = 0;
1694 if (midi_view()->note_mode() == Sustained) {
1696 Note* ev_rect = new Note (*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 = std::max(1., floor(midi_stream_view()->note_height()) - 2.);
1714 Hit* ev_diamond = new Hit (*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 (get_selected_channels());
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 framecnt_t region_frames = source_beats_to_region_frames (patch->time());
1794 const double x = trackview.editor().sample_to_pixel (region_frames);
1796 double const height = midi_stream_view()->contents_height();
1798 // CAIROCANVAS: active_channel info removed from PatcChange constructor
1799 // so we need to do something more sophisticated to keep its color
1800 // appearance (MidiPatchChangeFill/MidiPatchChangeInactiveChannelFill)
1803 boost::shared_ptr<PatchChange> patch_change = boost::shared_ptr<PatchChange>(
1804 new PatchChange(*this, group,
1811 if (patch_change->item().width() < _pixel_width) {
1812 // Show unless patch change is beyond the region bounds
1813 if (region_frames < 0 || region_frames >= _region->length()) {
1814 patch_change->hide();
1816 patch_change->show();
1819 patch_change->hide ();
1822 _patch_changes.push_back (patch_change);
1825 MIDI::Name::PatchPrimaryKey
1826 MidiRegionView::patch_change_to_patch_key (MidiModel::PatchChangePtr p)
1828 return MIDI::Name::PatchPrimaryKey (p->program(), p->bank());
1831 /// Return true iff @p pc applies to the given time on the given channel.
1833 patch_applies (const ARDOUR::MidiModel::constPatchChangePtr pc, double time, uint8_t channel)
1835 return pc->time() <= time && pc->channel() == channel;
1839 MidiRegionView::get_patch_key_at (double time, uint8_t channel, MIDI::Name::PatchPrimaryKey& key) const
1841 // The earliest event not before time
1842 MidiModel::PatchChanges::iterator i = _model->patch_change_lower_bound (time);
1844 // Go backwards until we find the latest PC for this channel, or the start
1845 while (i != _model->patch_changes().begin() &&
1846 (i == _model->patch_changes().end() ||
1847 !patch_applies(*i, time, channel))) {
1851 if (i != _model->patch_changes().end() && patch_applies(*i, time, channel)) {
1852 key.bank_number = (*i)->bank();
1853 key.program_number = (*i)->program ();
1855 key.bank_number = key.program_number = 0;
1858 if (!key.is_sane()) {
1859 error << string_compose(_("insane MIDI patch key %1:%2"),
1860 key.bank_number, key.program_number) << endmsg;
1865 MidiRegionView::change_patch_change (PatchChange& pc, const MIDI::Name::PatchPrimaryKey& new_patch)
1867 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("alter patch change"));
1869 if (pc.patch()->program() != new_patch.program_number) {
1870 c->change_program (pc.patch (), new_patch.program_number);
1873 int const new_bank = new_patch.bank_number;
1874 if (pc.patch()->bank() != new_bank) {
1875 c->change_bank (pc.patch (), new_bank);
1878 _model->apply_command (*trackview.session(), c);
1880 _patch_changes.clear ();
1881 display_patch_changes ();
1885 MidiRegionView::change_patch_change (MidiModel::PatchChangePtr old_change, const Evoral::PatchChange<Evoral::MusicalTime> & new_change)
1887 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("alter patch change"));
1889 if (old_change->time() != new_change.time()) {
1890 c->change_time (old_change, new_change.time());
1893 if (old_change->channel() != new_change.channel()) {
1894 c->change_channel (old_change, new_change.channel());
1897 if (old_change->program() != new_change.program()) {
1898 c->change_program (old_change, new_change.program());
1901 if (old_change->bank() != new_change.bank()) {
1902 c->change_bank (old_change, new_change.bank());
1905 _model->apply_command (*trackview.session(), c);
1907 _patch_changes.clear ();
1908 display_patch_changes ();
1911 /** Add a patch change to the region.
1912 * @param t Time in frames relative to region position
1913 * @param patch Patch to add; time and channel are ignored (time is converted from t, and channel comes from
1914 * MidiTimeAxisView::get_channel_for_add())
1917 MidiRegionView::add_patch_change (framecnt_t t, Evoral::PatchChange<Evoral::MusicalTime> const & patch)
1919 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1921 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("add patch change"));
1922 c->add (MidiModel::PatchChangePtr (
1923 new Evoral::PatchChange<Evoral::MusicalTime> (
1924 absolute_frames_to_source_beats (_region->position() + t),
1925 mtv->get_channel_for_add(), patch.program(), patch.bank()
1930 _model->apply_command (*trackview.session(), c);
1932 _patch_changes.clear ();
1933 display_patch_changes ();
1937 MidiRegionView::move_patch_change (PatchChange& pc, Evoral::MusicalTime t)
1939 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("move patch change"));
1940 c->change_time (pc.patch (), t);
1941 _model->apply_command (*trackview.session(), c);
1943 _patch_changes.clear ();
1944 display_patch_changes ();
1948 MidiRegionView::delete_patch_change (PatchChange* pc)
1950 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("delete patch change"));
1951 c->remove (pc->patch ());
1952 _model->apply_command (*trackview.session(), c);
1954 _patch_changes.clear ();
1955 display_patch_changes ();
1959 MidiRegionView::previous_patch (PatchChange& patch)
1961 if (patch.patch()->program() < 127) {
1962 MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch());
1963 key.program_number++;
1964 change_patch_change (patch, key);
1969 MidiRegionView::next_patch (PatchChange& patch)
1971 if (patch.patch()->program() > 0) {
1972 MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch());
1973 key.program_number--;
1974 change_patch_change (patch, key);
1979 MidiRegionView::next_bank (PatchChange& patch)
1981 if (patch.patch()->program() < 127) {
1982 MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch());
1983 if (key.bank_number > 0) {
1985 change_patch_change (patch, key);
1991 MidiRegionView::previous_bank (PatchChange& patch)
1993 if (patch.patch()->program() > 0) {
1994 MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch());
1995 if (key.bank_number < 127) {
1997 change_patch_change (patch, key);
2003 MidiRegionView::maybe_remove_deleted_note_from_selection (NoteBase* cne)
2005 if (_selection.empty()) {
2009 _selection.erase (cne);
2013 MidiRegionView::delete_selection()
2015 if (_selection.empty()) {
2019 start_note_diff_command (_("delete selection"));
2021 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2022 if ((*i)->selected()) {
2023 _note_diff_command->remove((*i)->note());
2033 MidiRegionView::delete_note (boost::shared_ptr<NoteType> n)
2035 start_note_diff_command (_("delete note"));
2036 _note_diff_command->remove (n);
2039 trackview.editor().verbose_cursor()->hide ();
2043 MidiRegionView::clear_selection_except (NoteBase* ev, bool signal)
2045 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2047 Selection::iterator tmp = i;
2050 (*i)->set_selected (false);
2051 (*i)->hide_velocity ();
2052 _selection.erase (i);
2060 /* this does not change the status of this regionview w.r.t the editor
2065 SelectionCleared (this); /* EMIT SIGNAL */
2070 MidiRegionView::unique_select(NoteBase* ev)
2072 clear_selection_except (ev);
2074 /* don't bother with checking to see if we should remove this
2075 regionview from the editor selection, since we're about to add
2076 another note, and thus put/keep this regionview in the editor
2080 if (!ev->selected()) {
2081 add_to_selection (ev);
2086 MidiRegionView::select_all_notes ()
2090 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2091 add_to_selection (*i);
2096 MidiRegionView::select_range (framepos_t start, framepos_t end)
2100 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2101 framepos_t t = source_beats_to_absolute_frames((*i)->note()->time());
2102 if (t >= start && t <= end) {
2103 add_to_selection (*i);
2109 MidiRegionView::invert_selection ()
2111 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2112 if ((*i)->selected()) {
2113 remove_from_selection(*i);
2115 add_to_selection (*i);
2121 MidiRegionView::select_matching_notes (uint8_t notenum, uint16_t channel_mask, bool add, bool extend)
2123 bool have_selection = !_selection.empty();
2124 uint8_t low_note = 127;
2125 uint8_t high_note = 0;
2126 MidiModel::Notes& notes (_model->notes());
2127 _optimization_iterator = _events.begin();
2129 if (extend && !have_selection) {
2133 /* scan existing selection to get note range */
2135 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2136 if ((*i)->note()->note() < low_note) {
2137 low_note = (*i)->note()->note();
2139 if ((*i)->note()->note() > high_note) {
2140 high_note = (*i)->note()->note();
2147 if (!extend && (low_note == high_note) && (high_note == notenum)) {
2148 /* only note previously selected is the one we are
2149 * reselecting. treat this as cancelling the selection.
2156 low_note = min (low_note, notenum);
2157 high_note = max (high_note, notenum);
2160 _no_sound_notes = true;
2162 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
2164 boost::shared_ptr<NoteType> note (*n);
2166 bool select = false;
2168 if (((1 << note->channel()) & channel_mask) != 0) {
2170 if ((note->note() >= low_note && note->note() <= high_note)) {
2173 } else if (note->note() == notenum) {
2179 if ((cne = find_canvas_note (note)) != 0) {
2180 // extend is false because we've taken care of it,
2181 // since it extends by time range, not pitch.
2182 note_selected (cne, add, false);
2186 add = true; // we need to add all remaining matching notes, even if the passed in value was false (for "set")
2190 _no_sound_notes = false;
2194 MidiRegionView::toggle_matching_notes (uint8_t notenum, uint16_t channel_mask)
2196 MidiModel::Notes& notes (_model->notes());
2197 _optimization_iterator = _events.begin();
2199 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
2201 boost::shared_ptr<NoteType> note (*n);
2204 if (note->note() == notenum && (((0x0001 << note->channel()) & channel_mask) != 0)) {
2205 if ((cne = find_canvas_note (note)) != 0) {
2206 if (cne->selected()) {
2207 note_deselected (cne);
2209 note_selected (cne, true, false);
2217 MidiRegionView::note_selected (NoteBase* ev, bool add, bool extend)
2220 clear_selection_except (ev);
2221 if (!_selection.empty()) {
2222 PublicEditor& editor (trackview.editor());
2223 editor.get_selection().add (this);
2229 if (!ev->selected()) {
2230 add_to_selection (ev);
2234 /* find end of latest note selected, select all between that and the start of "ev" */
2236 Evoral::MusicalTime earliest = Evoral::MaxMusicalTime;
2237 Evoral::MusicalTime latest = 0;
2239 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2240 if ((*i)->note()->end_time() > latest) {
2241 latest = (*i)->note()->end_time();
2243 if ((*i)->note()->time() < earliest) {
2244 earliest = (*i)->note()->time();
2248 if (ev->note()->end_time() > latest) {
2249 latest = ev->note()->end_time();
2252 if (ev->note()->time() < earliest) {
2253 earliest = ev->note()->time();
2256 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2258 /* find notes entirely within OR spanning the earliest..latest range */
2260 if (((*i)->note()->time() >= earliest && (*i)->note()->end_time() <= latest) ||
2261 ((*i)->note()->time() <= earliest && (*i)->note()->end_time() >= latest)) {
2262 add_to_selection (*i);
2270 MidiRegionView::note_deselected(NoteBase* ev)
2272 remove_from_selection (ev);
2276 MidiRegionView::update_drag_selection(double x0, double x1, double y0, double y1, bool extend)
2278 // TODO: Make this faster by storing the last updated selection rect, and only
2279 // adjusting things that are in the area that appears/disappeared.
2280 // We probably need a tree to be able to find events in O(log(n)) time.
2282 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2283 if ((*i)->x0() < x1 && (*i)->x1() > x0 && (*i)->y0() < y1 && (*i)->y1() > y0) {
2284 // Rectangles intersect
2285 if (!(*i)->selected()) {
2286 add_to_selection (*i);
2288 } else if ((*i)->selected() && !extend) {
2289 // Rectangles do not intersect
2290 remove_from_selection (*i);
2296 MidiRegionView::update_vertical_drag_selection (double y1, double y2, bool extend)
2302 // TODO: Make this faster by storing the last updated selection rect, and only
2303 // adjusting things that are in the area that appears/disappeared.
2304 // We probably need a tree to be able to find events in O(log(n)) time.
2306 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2307 if (((*i)->y1() >= y1 && (*i)->y1() <= y2)) {
2308 // within y- (note-) range
2309 if (!(*i)->selected()) {
2310 add_to_selection (*i);
2312 } else if ((*i)->selected() && !extend) {
2313 remove_from_selection (*i);
2319 MidiRegionView::remove_from_selection (NoteBase* ev)
2321 Selection::iterator i = _selection.find (ev);
2323 if (i != _selection.end()) {
2324 _selection.erase (i);
2327 ev->set_selected (false);
2328 ev->hide_velocity ();
2330 if (_selection.empty()) {
2331 PublicEditor& editor (trackview.editor());
2332 editor.get_selection().remove (this);
2337 MidiRegionView::add_to_selection (NoteBase* ev)
2339 bool add_mrv_selection = false;
2341 if (_selection.empty()) {
2342 add_mrv_selection = true;
2345 if (_selection.insert (ev).second) {
2346 ev->set_selected (true);
2347 start_playing_midi_note ((ev)->note());
2350 if (add_mrv_selection) {
2351 PublicEditor& editor (trackview.editor());
2352 editor.get_selection().add (this);
2357 MidiRegionView::move_selection(double dx, double dy, double cumulative_dy)
2359 typedef vector<boost::shared_ptr<NoteType> > PossibleChord;
2360 PossibleChord to_play;
2361 Evoral::MusicalTime earliest = Evoral::MaxMusicalTime;
2363 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2364 if ((*i)->note()->time() < earliest) {
2365 earliest = (*i)->note()->time();
2369 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2370 if (Evoral::musical_time_equal ((*i)->note()->time(), earliest)) {
2371 to_play.push_back ((*i)->note());
2373 (*i)->move_event(dx, dy);
2376 if (dy && !_selection.empty() && !_no_sound_notes && Config->get_sound_midi_notes()) {
2378 if (to_play.size() > 1) {
2380 PossibleChord shifted;
2382 for (PossibleChord::iterator n = to_play.begin(); n != to_play.end(); ++n) {
2383 boost::shared_ptr<NoteType> moved_note (new NoteType (**n));
2384 moved_note->set_note (moved_note->note() + cumulative_dy);
2385 shifted.push_back (moved_note);
2388 start_playing_midi_chord (shifted);
2390 } else if (!to_play.empty()) {
2392 boost::shared_ptr<NoteType> moved_note (new NoteType (*to_play.front()));
2393 moved_note->set_note (moved_note->note() + cumulative_dy);
2394 start_playing_midi_note (moved_note);
2400 MidiRegionView::note_dropped(NoteBase *, frameoffset_t dt, int8_t dnote)
2402 uint8_t lowest_note_in_selection = 127;
2403 uint8_t highest_note_in_selection = 0;
2404 uint8_t highest_note_difference = 0;
2406 // find highest and lowest notes first
2408 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2409 uint8_t pitch = (*i)->note()->note();
2410 lowest_note_in_selection = std::min(lowest_note_in_selection, pitch);
2411 highest_note_in_selection = std::max(highest_note_in_selection, pitch);
2415 cerr << "dnote: " << (int) dnote << endl;
2416 cerr << "lowest note (streamview): " << int(midi_stream_view()->lowest_note())
2417 << " highest note (streamview): " << int(midi_stream_view()->highest_note()) << endl;
2418 cerr << "lowest note (selection): " << int(lowest_note_in_selection) << " highest note(selection): "
2419 << int(highest_note_in_selection) << endl;
2420 cerr << "selection size: " << _selection.size() << endl;
2421 cerr << "Highest note in selection: " << (int) highest_note_in_selection << endl;
2424 // Make sure the note pitch does not exceed the MIDI standard range
2425 if (highest_note_in_selection + dnote > 127) {
2426 highest_note_difference = highest_note_in_selection - 127;
2429 start_note_diff_command (_("move notes"));
2431 for (Selection::iterator i = _selection.begin(); i != _selection.end() ; ++i) {
2433 framepos_t new_frames = source_beats_to_absolute_frames ((*i)->note()->time()) + dt;
2434 Evoral::MusicalTime new_time = absolute_frames_to_source_beats (new_frames);
2440 note_diff_add_change (*i, MidiModel::NoteDiffCommand::StartTime, new_time);
2442 uint8_t original_pitch = (*i)->note()->note();
2443 uint8_t new_pitch = original_pitch + dnote - highest_note_difference;
2445 // keep notes in standard midi range
2446 clamp_to_0_127(new_pitch);
2448 lowest_note_in_selection = std::min(lowest_note_in_selection, new_pitch);
2449 highest_note_in_selection = std::max(highest_note_in_selection, new_pitch);
2451 note_diff_add_change (*i, MidiModel::NoteDiffCommand::NoteNumber, new_pitch);
2456 // care about notes being moved beyond the upper/lower bounds on the canvas
2457 if (lowest_note_in_selection < midi_stream_view()->lowest_note() ||
2458 highest_note_in_selection > midi_stream_view()->highest_note()) {
2459 midi_stream_view()->set_note_range(MidiStreamView::ContentsRange);
2463 /** @param x Pixel relative to the region position.
2464 * @return Snapped frame relative to the region position.
2467 MidiRegionView::snap_pixel_to_sample(double x)
2469 PublicEditor& editor (trackview.editor());
2470 return snap_frame_to_frame (editor.pixel_to_sample (x));
2473 /** @param x Pixel relative to the region position.
2474 * @return Snapped pixel relative to the region position.
2477 MidiRegionView::snap_to_pixel(double x)
2479 return (double) trackview.editor().sample_to_pixel(snap_pixel_to_sample(x));
2483 MidiRegionView::get_position_pixels()
2485 framepos_t region_frame = get_position();
2486 return trackview.editor().sample_to_pixel(region_frame);
2490 MidiRegionView::get_end_position_pixels()
2492 framepos_t frame = get_position() + get_duration ();
2493 return trackview.editor().sample_to_pixel(frame);
2497 MidiRegionView::source_beats_to_absolute_frames(double beats) const
2499 /* the time converter will return the frame corresponding to `beats'
2500 relative to the start of the source. The start of the source
2501 is an implied position given by region->position - region->start
2503 const framepos_t source_start = _region->position() - _region->start();
2504 return source_start + _source_relative_time_converter.to (beats);
2508 MidiRegionView::absolute_frames_to_source_beats(framepos_t frames) const
2510 /* the `frames' argument needs to be converted into a frame count
2511 relative to the start of the source before being passed in to the
2514 const framepos_t source_start = _region->position() - _region->start();
2515 return _source_relative_time_converter.from (frames - source_start);
2519 MidiRegionView::region_beats_to_region_frames(double beats) const
2521 return _region_relative_time_converter.to(beats);
2525 MidiRegionView::region_frames_to_region_beats(framepos_t frames) const
2527 return _region_relative_time_converter.from(frames);
2531 MidiRegionView::begin_resizing (bool /*at_front*/)
2533 _resize_data.clear();
2535 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2536 Note *note = dynamic_cast<Note*> (*i);
2538 // only insert CanvasNotes into the map
2540 NoteResizeData *resize_data = new NoteResizeData();
2541 resize_data->note = note;
2543 // create a new SimpleRect from the note which will be the resize preview
2544 ArdourCanvas::Rectangle *resize_rect = new ArdourCanvas::Rectangle (_note_group,
2545 ArdourCanvas::Rect (note->x0(), note->y0(), note->x0(), note->y1()));
2547 // calculate the colors: get the color settings
2548 uint32_t fill_color = UINT_RGBA_CHANGE_A(
2549 ARDOUR_UI::config()->get_MidiNoteSelected(),
2552 // make the resize preview notes more transparent and bright
2553 fill_color = UINT_INTERPOLATE(fill_color, 0xFFFFFF40, 0.5);
2555 // calculate color based on note velocity
2556 resize_rect->set_fill_color (UINT_INTERPOLATE(
2557 NoteBase::meter_style_fill_color(note->note()->velocity(), note->selected()),
2561 resize_rect->set_outline_color (NoteBase::calculate_outline (
2562 ARDOUR_UI::config()->get_MidiNoteSelected()));
2564 resize_data->resize_rect = resize_rect;
2565 _resize_data.push_back(resize_data);
2570 /** Update resizing notes while user drags.
2571 * @param primary `primary' note for the drag; ie the one that is used as the reference in non-relative mode.
2572 * @param at_front which end of the note (true == note on, false == note off)
2573 * @param delta_x change in mouse position since the start of the drag
2574 * @param relative true if relative resizing is taking place, false if absolute resizing. This only makes
2575 * a difference when multiple notes are being resized; in relative mode, each note's length is changed by the
2576 * amount of the drag. In non-relative mode, all selected notes are set to have the same start or end point
2577 * as the \a primary note.
2580 MidiRegionView::update_resizing (NoteBase* primary, bool at_front, double delta_x, bool relative)
2582 bool cursor_set = false;
2584 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2585 ArdourCanvas::Rectangle* resize_rect = (*i)->resize_rect;
2586 Note* canvas_note = (*i)->note;
2591 current_x = canvas_note->x0() + delta_x;
2593 current_x = primary->x0() + delta_x;
2597 current_x = canvas_note->x1() + delta_x;
2599 current_x = primary->x1() + delta_x;
2603 if (current_x < 0) {
2604 // This works even with snapping because RegionView::snap_frame_to_frame()
2605 // snaps forward if the snapped sample is before the beginning of the region
2608 if (current_x > trackview.editor().sample_to_pixel(_region->length())) {
2609 current_x = trackview.editor().sample_to_pixel(_region->length());
2613 resize_rect->set_x0 (snap_to_pixel(current_x));
2614 resize_rect->set_x1 (canvas_note->x1());
2616 resize_rect->set_x1 (snap_to_pixel(current_x));
2617 resize_rect->set_x0 (canvas_note->x0());
2623 beats = snap_pixel_to_sample (current_x);
2624 beats = region_frames_to_region_beats (beats);
2629 if (beats < canvas_note->note()->end_time()) {
2630 len = canvas_note->note()->time() - beats;
2631 len += canvas_note->note()->length();
2636 if (beats >= canvas_note->note()->time()) {
2637 len = beats - canvas_note->note()->time();
2644 snprintf (buf, sizeof (buf), "%.3g beats", len);
2645 show_verbose_cursor (buf, 0, 0);
2654 /** Finish resizing notes when the user releases the mouse button.
2655 * Parameters the same as for \a update_resizing().
2658 MidiRegionView::commit_resizing (NoteBase* primary, bool at_front, double delta_x, bool relative)
2660 start_note_diff_command (_("resize notes"));
2662 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2663 Note* canvas_note = (*i)->note;
2664 ArdourCanvas::Rectangle* resize_rect = (*i)->resize_rect;
2666 /* Get the new x position for this resize, which is in pixels relative
2667 * to the region position.
2674 current_x = canvas_note->x0() + delta_x;
2676 current_x = primary->x0() + delta_x;
2680 current_x = canvas_note->x1() + delta_x;
2682 current_x = primary->x1() + delta_x;
2686 if (current_x < 0) {
2689 if (current_x > trackview.editor().sample_to_pixel(_region->length())) {
2690 current_x = trackview.editor().sample_to_pixel(_region->length());
2693 /* Convert that to a frame within the source */
2694 current_x = snap_pixel_to_sample (current_x) + _region->start ();
2696 /* and then to beats */
2697 current_x = region_frames_to_region_beats (current_x);
2699 if (at_front && current_x < canvas_note->note()->end_time()) {
2700 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::StartTime, current_x);
2702 double len = canvas_note->note()->time() - current_x;
2703 len += canvas_note->note()->length();
2706 /* XXX convert to beats */
2707 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
2712 double len = current_x - canvas_note->note()->time();
2715 /* XXX convert to beats */
2716 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
2724 _resize_data.clear();
2729 MidiRegionView::abort_resizing ()
2731 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2732 delete (*i)->resize_rect;
2736 _resize_data.clear ();
2740 MidiRegionView::change_note_velocity(NoteBase* event, int8_t velocity, bool relative)
2742 uint8_t new_velocity;
2745 new_velocity = event->note()->velocity() + velocity;
2746 clamp_to_0_127(new_velocity);
2748 new_velocity = velocity;
2751 event->set_selected (event->selected()); // change color
2753 note_diff_add_change (event, MidiModel::NoteDiffCommand::Velocity, new_velocity);
2757 MidiRegionView::change_note_note (NoteBase* event, int8_t note, bool relative)
2762 new_note = event->note()->note() + note;
2767 clamp_to_0_127 (new_note);
2768 note_diff_add_change (event, MidiModel::NoteDiffCommand::NoteNumber, new_note);
2772 MidiRegionView::trim_note (NoteBase* event, Evoral::MusicalTime front_delta, Evoral::MusicalTime end_delta)
2774 bool change_start = false;
2775 bool change_length = false;
2776 Evoral::MusicalTime new_start = 0;
2777 Evoral::MusicalTime new_length = 0;
2779 /* NOTE: the semantics of the two delta arguments are slightly subtle:
2781 front_delta: if positive - move the start of the note later in time (shortening it)
2782 if negative - move the start of the note earlier in time (lengthening it)
2784 end_delta: if positive - move the end of the note later in time (lengthening it)
2785 if negative - move the end of the note earlier in time (shortening it)
2789 if (front_delta < 0) {
2791 if (event->note()->time() < -front_delta) {
2794 new_start = event->note()->time() + front_delta; // moves earlier
2797 /* start moved toward zero, so move the end point out to where it used to be.
2798 Note that front_delta is negative, so this increases the length.
2801 new_length = event->note()->length() - front_delta;
2802 change_start = true;
2803 change_length = true;
2807 Evoral::MusicalTime new_pos = event->note()->time() + front_delta;
2809 if (new_pos < event->note()->end_time()) {
2810 new_start = event->note()->time() + front_delta;
2811 /* start moved toward the end, so move the end point back to where it used to be */
2812 new_length = event->note()->length() - front_delta;
2813 change_start = true;
2814 change_length = true;
2821 bool can_change = true;
2822 if (end_delta < 0) {
2823 if (event->note()->length() < -end_delta) {
2829 new_length = event->note()->length() + end_delta;
2830 change_length = true;
2835 note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_start);
2838 if (change_length) {
2839 note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, new_length);
2844 MidiRegionView::change_note_channel (NoteBase* event, int8_t chn, bool relative)
2846 uint8_t new_channel;
2850 if (event->note()->channel() < -chn) {
2853 new_channel = event->note()->channel() + chn;
2856 new_channel = event->note()->channel() + chn;
2859 new_channel = (uint8_t) chn;
2862 note_diff_add_change (event, MidiModel::NoteDiffCommand::Channel, new_channel);
2866 MidiRegionView::change_note_time (NoteBase* event, Evoral::MusicalTime delta, bool relative)
2868 Evoral::MusicalTime new_time;
2872 if (event->note()->time() < -delta) {
2875 new_time = event->note()->time() + delta;
2878 new_time = event->note()->time() + delta;
2884 note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_time);
2888 MidiRegionView::change_note_length (NoteBase* event, Evoral::MusicalTime t)
2890 note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, t);
2894 MidiRegionView::change_velocities (bool up, bool fine, bool allow_smush, bool all_together)
2899 if (_selection.empty()) {
2914 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2915 if ((*i)->note()->velocity() < -delta || (*i)->note()->velocity() + delta > 127) {
2921 start_note_diff_command (_("change velocities"));
2923 for (Selection::iterator i = _selection.begin(); i != _selection.end();) {
2924 Selection::iterator next = i;
2928 if (i == _selection.begin()) {
2929 change_note_velocity (*i, delta, true);
2930 value = (*i)->note()->velocity() + delta;
2932 change_note_velocity (*i, value, false);
2936 change_note_velocity (*i, delta, true);
2945 if (!_selection.empty()) {
2947 snprintf (buf, sizeof (buf), "Vel %d",
2948 (int) (*_selection.begin())->note()->velocity());
2949 show_verbose_cursor (buf, 10, 10);
2955 MidiRegionView::transpose (bool up, bool fine, bool allow_smush)
2957 if (_selection.empty()) {
2974 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2976 if ((int8_t) (*i)->note()->note() + delta <= 0) {
2980 if ((int8_t) (*i)->note()->note() + delta > 127) {
2987 start_note_diff_command (_("transpose"));
2989 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2990 Selection::iterator next = i;
2992 change_note_note (*i, delta, true);
3000 MidiRegionView::change_note_lengths (bool fine, bool shorter, Evoral::MusicalTime delta, bool start, bool end)
3006 /* grab the current grid distance */
3008 delta = trackview.editor().get_grid_type_as_beats (success, _region->position());
3010 /* XXX cannot get grid type as beats ... should always be possible ... FIX ME */
3011 error << string_compose (_("programming error: %1"), "Grid type not available as beats - TO BE FIXED") << endmsg;
3021 start_note_diff_command (_("change note lengths"));
3023 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3024 Selection::iterator next = i;
3027 /* note the negation of the delta for start */
3029 trim_note (*i, (start ? -delta : 0), (end ? delta : 0));
3038 MidiRegionView::nudge_notes (bool forward)
3040 if (_selection.empty()) {
3044 /* pick a note as the point along the timeline to get the nudge distance.
3045 its not necessarily the earliest note, so we may want to pull the notes out
3046 into a vector and sort before using the first one.
3049 framepos_t ref_point = source_beats_to_absolute_frames ((*(_selection.begin()))->note()->time());
3051 framecnt_t distance;
3053 if (trackview.editor().snap_mode() == Editing::SnapOff) {
3055 /* grid is off - use nudge distance */
3057 distance = trackview.editor().get_nudge_distance (ref_point, unused);
3063 framepos_t next_pos = ref_point;
3066 if (max_framepos - 1 < next_pos) {
3070 if (next_pos == 0) {
3076 trackview.editor().snap_to (next_pos, (forward ? 1 : -1), false);
3077 distance = ref_point - next_pos;
3080 if (distance == 0) {
3084 Evoral::MusicalTime delta = region_frames_to_region_beats (fabs ((double)distance));
3090 start_note_diff_command (_("nudge"));
3092 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3093 Selection::iterator next = i;
3095 change_note_time (*i, delta, true);
3103 MidiRegionView::change_channel(uint8_t channel)
3105 start_note_diff_command(_("change channel"));
3106 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3107 note_diff_add_change (*i, MidiModel::NoteDiffCommand::Channel, channel);
3115 MidiRegionView::note_entered(NoteBase* ev)
3117 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3119 pre_enter_cursor = editor->get_canvas_cursor ();
3121 if (_mouse_state == SelectTouchDragging) {
3122 note_selected (ev, true);
3125 show_verbose_cursor (ev->note ());
3129 MidiRegionView::note_left (NoteBase*)
3131 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3133 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3134 (*i)->hide_velocity ();
3137 editor->verbose_cursor()->hide ();
3139 if (pre_enter_cursor) {
3140 editor->set_canvas_cursor (pre_enter_cursor);
3141 pre_enter_cursor = 0;
3146 MidiRegionView::patch_entered (PatchChange* p)
3149 /* XXX should get patch name if we can */
3150 s << _("Bank ") << (p->patch()->bank() + MIDI_BP_ZERO) << '\n'
3151 << _("Program ") << ((int) p->patch()->program()) + MIDI_BP_ZERO << '\n'
3152 << _("Channel ") << ((int) p->patch()->channel() + 1);
3153 show_verbose_cursor (s.str(), 10, 20);
3154 p->item().grab_focus();
3158 MidiRegionView::patch_left (PatchChange *)
3160 trackview.editor().verbose_cursor()->hide ();
3161 /* focus will transfer back via the enter-notify event sent to this
3167 MidiRegionView::sysex_entered (SysEx* p)
3171 // need a way to extract text from p->_flag->_text
3173 // show_verbose_cursor (s.str(), 10, 20);
3174 p->item().grab_focus();
3178 MidiRegionView::sysex_left (SysEx *)
3180 trackview.editor().verbose_cursor()->hide ();
3181 /* focus will transfer back via the enter-notify event sent to this
3187 MidiRegionView::note_mouse_position (float x_fraction, float /*y_fraction*/, bool can_set_cursor)
3189 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3190 Editing::MouseMode mm = editor->current_mouse_mode();
3191 bool trimmable = (mm == MouseObject || mm == MouseTimeFX || mm == MouseDraw);
3193 if (trimmable && x_fraction > 0.0 && x_fraction < 0.2) {
3194 editor->set_canvas_cursor (editor->cursors()->left_side_trim);
3195 } else if (trimmable && x_fraction >= 0.8 && x_fraction < 1.0) {
3196 editor->set_canvas_cursor (editor->cursors()->right_side_trim);
3198 if (pre_enter_cursor && can_set_cursor) {
3199 editor->set_canvas_cursor (pre_enter_cursor);
3205 MidiRegionView::set_frame_color()
3209 TimeAxisViewItem::set_frame_color ();
3216 f = ARDOUR_UI::config()->get_SelectedFrameBase();
3217 } else if (high_enough_for_name) {
3218 f= ARDOUR_UI::config()->get_MidiFrameBase();
3223 if (!rect_visible) {
3224 f = UINT_RGBA_CHANGE_A (f, 80);
3227 frame->set_fill_color (f);
3231 MidiRegionView::midi_channel_mode_changed ()
3233 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3234 uint16_t mask = mtv->midi_track()->get_playback_channel_mask();
3235 ChannelMode mode = mtv->midi_track()->get_playback_channel_mode ();
3237 if (mode == ForceChannel) {
3238 mask = 0xFFFF; // Show all notes as active (below)
3241 // Update notes for selection
3242 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3243 (*i)->on_channel_selection_change (mask);
3246 _patch_changes.clear ();
3247 display_patch_changes ();
3251 MidiRegionView::instrument_settings_changed ()
3257 MidiRegionView::cut_copy_clear (Editing::CutCopyOp op)
3259 if (_selection.empty()) {
3263 PublicEditor& editor (trackview.editor());
3267 /* XXX what to do ? */
3271 editor.get_cut_buffer().add (selection_as_cut_buffer());
3279 start_note_diff_command();
3281 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3288 note_diff_remove_note (*i);
3298 MidiRegionView::selection_as_cut_buffer () const
3302 for (Selection::const_iterator i = _selection.begin(); i != _selection.end(); ++i) {
3303 NoteType* n = (*i)->note().get();
3304 notes.insert (boost::shared_ptr<NoteType> (new NoteType (*n)));
3307 MidiCutBuffer* cb = new MidiCutBuffer (trackview.session());
3313 /** This method handles undo */
3315 MidiRegionView::paste (framepos_t pos, float times, const MidiCutBuffer& mcb)
3321 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("MIDI paste @ %1 times %2\n", pos, times));
3323 trackview.session()->begin_reversible_command (_("paste"));
3325 start_note_diff_command (_("paste"));
3327 Evoral::MusicalTime beat_delta;
3328 Evoral::MusicalTime paste_pos_beats;
3329 Evoral::MusicalTime duration;
3330 Evoral::MusicalTime end_point = 0;
3332 duration = (*mcb.notes().rbegin())->end_time() - (*mcb.notes().begin())->time();
3333 paste_pos_beats = absolute_frames_to_source_beats (pos);
3334 beat_delta = (*mcb.notes().begin())->time() - paste_pos_beats;
3335 paste_pos_beats = 0;
3337 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",
3338 (*mcb.notes().begin())->time(),
3339 (*mcb.notes().rbegin())->end_time(),
3340 duration, pos, _region->position(),
3341 paste_pos_beats, beat_delta));
3345 for (int n = 0; n < (int) times; ++n) {
3347 for (Notes::const_iterator i = mcb.notes().begin(); i != mcb.notes().end(); ++i) {
3349 boost::shared_ptr<NoteType> copied_note (new NoteType (*((*i).get())));
3350 copied_note->set_time (paste_pos_beats + copied_note->time() - beat_delta);
3352 /* make all newly added notes selected */
3354 note_diff_add_note (copied_note, true);
3355 end_point = copied_note->end_time();
3358 paste_pos_beats += duration;
3361 /* if we pasted past the current end of the region, extend the region */
3363 framepos_t end_frame = source_beats_to_absolute_frames (end_point);
3364 framepos_t region_end = _region->position() + _region->length() - 1;
3366 if (end_frame > region_end) {
3368 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste extended region from %1 to %2\n", region_end, end_frame));
3370 _region->clear_changes ();
3371 _region->set_length (end_frame - _region->position());
3372 trackview.session()->add_command (new StatefulDiffCommand (_region));
3377 trackview.session()->commit_reversible_command ();
3380 struct EventNoteTimeEarlyFirstComparator {
3381 bool operator() (NoteBase* a, NoteBase* b) {
3382 return a->note()->time() < b->note()->time();
3387 MidiRegionView::time_sort_events ()
3389 if (!_sort_needed) {
3393 EventNoteTimeEarlyFirstComparator cmp;
3396 _sort_needed = false;
3400 MidiRegionView::goto_next_note (bool add_to_selection)
3402 bool use_next = false;
3404 if (_events.back()->selected()) {
3408 time_sort_events ();
3410 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3411 uint16_t const channel_mask = mtv->midi_track()->get_playback_channel_mask();
3413 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3414 if ((*i)->selected()) {
3417 } else if (use_next) {
3418 if (channel_mask & (1 << (*i)->note()->channel())) {
3419 if (!add_to_selection) {
3422 note_selected (*i, true, false);
3429 /* use the first one */
3431 if (!_events.empty() && (channel_mask & (1 << _events.front()->note()->channel ()))) {
3432 unique_select (_events.front());
3437 MidiRegionView::goto_previous_note (bool add_to_selection)
3439 bool use_next = false;
3441 if (_events.front()->selected()) {
3445 time_sort_events ();
3447 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3448 uint16_t const channel_mask = mtv->midi_track()->get_playback_channel_mask ();
3450 for (Events::reverse_iterator i = _events.rbegin(); i != _events.rend(); ++i) {
3451 if ((*i)->selected()) {
3454 } else if (use_next) {
3455 if (channel_mask & (1 << (*i)->note()->channel())) {
3456 if (!add_to_selection) {
3459 note_selected (*i, true, false);
3466 /* use the last one */
3468 if (!_events.empty() && (channel_mask & (1 << (*_events.rbegin())->note()->channel ()))) {
3469 unique_select (*(_events.rbegin()));
3474 MidiRegionView::selection_as_notelist (Notes& selected, bool allow_all_if_none_selected)
3476 bool had_selected = false;
3478 time_sort_events ();
3480 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3481 if ((*i)->selected()) {
3482 selected.insert ((*i)->note());
3483 had_selected = true;
3487 if (allow_all_if_none_selected && !had_selected) {
3488 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3489 selected.insert ((*i)->note());
3495 MidiRegionView::update_ghost_note (double x, double y)
3497 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3502 _note_group->canvas_to_item (x, y);
3504 PublicEditor& editor = trackview.editor ();
3506 framepos_t const unsnapped_frame = editor.pixel_to_sample (x);
3507 framecnt_t grid_frames;
3508 framepos_t const f = snap_frame_to_grid_underneath (unsnapped_frame, grid_frames);
3510 /* use region_frames... because we are converting a delta within the region
3514 double length = editor.get_grid_type_as_beats (success, unsnapped_frame);
3520 /* note that this sets the time of the ghost note in beats relative to
3521 the start of the source; that is how all note times are stored.
3523 _ghost_note->note()->set_time (
3524 std::max(0.0, absolute_frames_to_source_beats (f + _region->position ())));
3525 _ghost_note->note()->set_length (length);
3526 _ghost_note->note()->set_note (midi_stream_view()->y_to_note (y));
3527 _ghost_note->note()->set_channel (mtv->get_channel_for_add ());
3529 /* the ghost note does not appear in ghost regions, so pass false in here */
3530 update_note (_ghost_note, false);
3532 show_verbose_cursor (_ghost_note->note ());
3536 MidiRegionView::create_ghost_note (double x, double y)
3538 remove_ghost_note ();
3540 boost::shared_ptr<NoteType> g (new NoteType);
3541 _ghost_note = new Note (*this, _note_group, g);
3542 _ghost_note->set_ignore_events (true);
3543 _ghost_note->set_outline_color (0x000000aa);
3544 if (x < 0) { x = 0; }
3545 update_ghost_note (x, y);
3546 _ghost_note->show ();
3551 show_verbose_cursor (_ghost_note->note ());
3555 MidiRegionView::remove_ghost_note ()
3562 MidiRegionView::snap_changed ()
3568 create_ghost_note (_last_ghost_x, _last_ghost_y);
3572 MidiRegionView::drop_down_keys ()
3574 _mouse_state = None;
3578 MidiRegionView::maybe_select_by_position (GdkEventButton* ev, double /*x*/, double y)
3580 double note = midi_stream_view()->y_to_note(y);
3582 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3584 uint16_t chn_mask = mtv->midi_track()->get_playback_channel_mask();
3586 if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
3587 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchGreaterThanOrEqual, (uint8_t) floor (note), chn_mask);
3588 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
3589 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchLessThanOrEqual, (uint8_t) floor (note), chn_mask);
3594 bool add_mrv_selection = false;
3596 if (_selection.empty()) {
3597 add_mrv_selection = true;
3600 for (Events::iterator i = e.begin(); i != e.end(); ++i) {
3601 if (_selection.insert (*i).second) {
3602 (*i)->set_selected (true);
3606 if (add_mrv_selection) {
3607 PublicEditor& editor (trackview.editor());
3608 editor.get_selection().add (this);
3613 MidiRegionView::color_handler ()
3615 RegionView::color_handler ();
3617 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3618 (*i)->set_selected ((*i)->selected()); // will change color
3621 /* XXX probably more to do here */
3625 MidiRegionView::enable_display (bool yn)
3627 RegionView::enable_display (yn);
3634 MidiRegionView::show_step_edit_cursor (Evoral::MusicalTime pos)
3636 if (_step_edit_cursor == 0) {
3637 ArdourCanvas::Item* const group = get_canvas_group();
3639 _step_edit_cursor = new ArdourCanvas::Rectangle (group);
3640 _step_edit_cursor->set_y0 (0);
3641 _step_edit_cursor->set_y1 (midi_stream_view()->contents_height());
3642 _step_edit_cursor->set_fill_color (RGBA_TO_UINT (45,0,0,90));
3643 _step_edit_cursor->set_outline_color (RGBA_TO_UINT (85,0,0,90));
3646 move_step_edit_cursor (pos);
3647 _step_edit_cursor->show ();
3651 MidiRegionView::move_step_edit_cursor (Evoral::MusicalTime pos)
3653 _step_edit_cursor_position = pos;
3655 if (_step_edit_cursor) {
3656 double pixel = trackview.editor().sample_to_pixel (region_beats_to_region_frames (pos));
3657 _step_edit_cursor->set_x0 (pixel);
3658 set_step_edit_cursor_width (_step_edit_cursor_width);
3663 MidiRegionView::hide_step_edit_cursor ()
3665 if (_step_edit_cursor) {
3666 _step_edit_cursor->hide ();
3671 MidiRegionView::set_step_edit_cursor_width (Evoral::MusicalTime beats)
3673 _step_edit_cursor_width = beats;
3675 if (_step_edit_cursor) {
3676 _step_edit_cursor->set_x1 (_step_edit_cursor->x0() + trackview.editor().sample_to_pixel (region_beats_to_region_frames (beats)));
3680 /** Called when a diskstream on our track has received some data. Update the view, if applicable.
3681 * @param w Source that the data will end up in.
3684 MidiRegionView::data_recorded (boost::weak_ptr<MidiSource> w)
3686 if (!_active_notes) {
3687 /* we aren't actively being recorded to */
3691 boost::shared_ptr<MidiSource> src = w.lock ();
3692 if (!src || src != midi_region()->midi_source()) {
3693 /* recorded data was not destined for our source */
3697 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (&trackview);
3699 boost::shared_ptr<MidiBuffer> buf = mtv->midi_track()->get_gui_feed_buffer ();
3701 BeatsFramesConverter converter (trackview.session()->tempo_map(), mtv->midi_track()->get_capture_start_frame (0));
3703 framepos_t back = max_framepos;
3705 for (MidiBuffer::iterator i = buf->begin(); i != buf->end(); ++i) {
3706 Evoral::MIDIEvent<MidiBuffer::TimeType> const ev (*i, false);
3708 if (ev.is_channel_event()) {
3709 if (get_channel_mode() == FilterChannels) {
3710 if (((uint16_t(1) << ev.channel()) & get_selected_channels()) == 0) {
3716 /* ev.time() is in session frames, so (ev.time() - converter.origin_b()) is
3717 frames from the start of the source, and so time_beats is in terms of the
3721 Evoral::MusicalTime const time_beats = converter.from (ev.time () - converter.origin_b ());
3723 if (ev.type() == MIDI_CMD_NOTE_ON) {
3724 boost::shared_ptr<NoteType> note (
3725 new NoteType (ev.channel(), time_beats, 0, ev.note(), ev.velocity()));
3727 add_note (note, true);
3729 /* fix up our note range */
3730 if (ev.note() < _current_range_min) {
3731 midi_stream_view()->apply_note_range (ev.note(), _current_range_max, true);
3732 } else if (ev.note() > _current_range_max) {
3733 midi_stream_view()->apply_note_range (_current_range_min, ev.note(), true);
3736 } else if (ev.type() == MIDI_CMD_NOTE_OFF) {
3737 resolve_note (ev.note (), time_beats);
3743 midi_stream_view()->check_record_layers (region(), back);
3747 MidiRegionView::trim_front_starting ()
3749 /* Reparent the note group to the region view's parent, so that it doesn't change
3750 when the region view is trimmed.
3752 _temporary_note_group = new ArdourCanvas::Container (group->parent ());
3753 _temporary_note_group->move (group->position ());
3754 _note_group->reparent (_temporary_note_group);
3758 MidiRegionView::trim_front_ending ()
3760 _note_group->reparent (group);
3761 delete _temporary_note_group;
3762 _temporary_note_group = 0;
3764 if (_region->start() < 0) {
3765 /* Trim drag made start time -ve; fix this */
3766 midi_region()->fix_negative_start ();
3771 MidiRegionView::edit_patch_change (PatchChange* pc)
3773 PatchChangeDialog d (&_source_relative_time_converter, trackview.session(), *pc->patch (), instrument_info(), Gtk::Stock::APPLY, true);
3775 int response = d.run();
3778 case Gtk::RESPONSE_ACCEPT:
3780 case Gtk::RESPONSE_REJECT:
3781 delete_patch_change (pc);
3787 change_patch_change (pc->patch(), d.patch ());
3791 MidiRegionView::delete_sysex (SysEx* /*sysex*/)
3794 // sysyex object doesn't have a pointer to a sysex event
3795 // MidiModel::SysExDiffCommand* c = _model->new_sysex_diff_command (_("delete sysex"));
3796 // c->remove (sysex->sysex());
3797 // _model->apply_command (*trackview.session(), c);
3799 //_sys_exes.clear ();
3800 // display_sysexes();
3804 MidiRegionView::show_verbose_cursor (boost::shared_ptr<NoteType> n) const
3806 using namespace MIDI::Name;
3810 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3812 boost::shared_ptr<MasterDeviceNames> device_names(mtv->get_device_names());
3814 MIDI::Name::PatchPrimaryKey patch_key;
3815 get_patch_key_at(n->time(), n->channel(), patch_key);
3816 name = device_names->note_name(mtv->gui_property(X_("midnam-custom-device-mode")),
3818 patch_key.bank_number,
3819 patch_key.program_number,
3825 snprintf (buf, sizeof (buf), "%d %s\nCh %d Vel %d",
3827 name.empty() ? Evoral::midi_note_name (n->note()).c_str() : name.c_str(),
3828 (int) n->channel() + 1,
3829 (int) n->velocity());
3831 show_verbose_cursor(buf, 10, 20);
3835 MidiRegionView::show_verbose_cursor (string const & text, double xoffset, double yoffset) const
3837 trackview.editor().verbose_cursor()->set (text);
3838 trackview.editor().verbose_cursor()->show ();
3839 trackview.editor().verbose_cursor()->set_offset (ArdourCanvas::Duple (xoffset, yoffset));
3842 /** @param p A session framepos.
3843 * @param grid_frames Filled in with the number of frames that a grid interval is at p.
3844 * @return p snapped to the grid subdivision underneath it.
3847 MidiRegionView::snap_frame_to_grid_underneath (framepos_t p, framecnt_t& grid_frames) const
3849 PublicEditor& editor = trackview.editor ();
3852 Evoral::MusicalTime grid_beats = editor.get_grid_type_as_beats (success, p);
3858 grid_frames = region_beats_to_region_frames (grid_beats);
3860 /* Hack so that we always snap to the note that we are over, instead of snapping
3861 to the next one if we're more than halfway through the one we're over.
3863 if (editor.snap_mode() == SnapNormal && p >= grid_frames / 2) {
3864 p -= grid_frames / 2;
3867 return snap_frame_to_frame (p);
3870 /** Called when the selection has been cleared in any MidiRegionView.
3871 * @param rv MidiRegionView that the selection was cleared in.
3874 MidiRegionView::selection_cleared (MidiRegionView* rv)
3880 /* Clear our selection in sympathy; but don't signal the fact */
3881 clear_selection (false);
3885 MidiRegionView::note_button_release ()
3887 delete _note_player;
3892 MidiRegionView::get_channel_mode () const
3894 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (&trackview);
3895 return rtav->midi_track()->get_playback_channel_mode();
3899 MidiRegionView::get_selected_channels () const
3901 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (&trackview);
3902 return rtav->midi_track()->get_playback_channel_mask();