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"
48 #include "automation_region_view.h"
49 #include "automation_time_axis.h"
52 #include "editor_drag.h"
53 #include "ghostregion.h"
54 #include "gui_thread.h"
56 #include "midi_channel_dialog.h"
57 #include "midi_cut_buffer.h"
58 #include "midi_list_editor.h"
59 #include "midi_region_view.h"
60 #include "midi_streamview.h"
61 #include "midi_time_axis.h"
62 #include "midi_util.h"
63 #include "midi_velocity_dialog.h"
64 #include "mouse_cursors.h"
65 #include "note_player.h"
66 #include "public_editor.h"
67 #include "route_time_axis.h"
68 #include "rgb_macros.h"
69 #include "selection.h"
70 #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::Group *parent, RouteTimeAxisView &tv,
92 boost::shared_ptr<MidiRegion> r, double spu, Gdk::Color const & 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::Group (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::Group *parent, RouteTimeAxisView &tv,
128 boost::shared_ptr<MidiRegion> r, double spu, Gdk::Color& 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::Group (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 == "diplay-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::Group (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 UINT_TO_RGBA (other.fill_color, &r, &g, &b, &a);
204 c.set_rgb_p (r/255.0, g/255.0, b/255.0);
209 MidiRegionView::MidiRegionView (const MidiRegionView& other, boost::shared_ptr<MidiRegion> region)
210 : RegionView (other, boost::shared_ptr<Region> (region))
211 , _current_range_min(0)
212 , _current_range_max(0)
214 , _note_group (new ArdourCanvas::Group (get_canvas_group()))
215 , _note_diff_command (0)
217 , _step_edit_cursor (0)
218 , _step_edit_cursor_width (1.0)
219 , _step_edit_cursor_position (0.0)
220 , _channel_selection_scoped_note (0)
221 , _temporary_note_group (0)
224 , _sort_needed (true)
225 , _optimization_iterator (_events.end())
227 , _no_sound_notes (false)
230 , pre_enter_cursor (0)
231 , pre_press_cursor (0)
237 UINT_TO_RGBA (other.fill_color, &r, &g, &b, &a);
238 c.set_rgb_p (r/255.0, g/255.0, b/255.0);
244 MidiRegionView::init (Gdk::Color const & basic_color, bool wfd)
246 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
248 NoteBase::NoteBaseDeleted.connect (note_delete_connection, MISSING_INVALIDATOR,
249 boost::bind (&MidiRegionView::maybe_remove_deleted_note_from_selection, this, _1),
253 midi_region()->midi_source(0)->load_model();
256 _model = midi_region()->midi_source(0)->model();
257 _enable_display = false;
259 RegionView::init (basic_color, false);
261 compute_colors (basic_color);
263 set_height (trackview.current_height());
266 region_sync_changed ();
267 region_resized (ARDOUR::bounds_change);
272 _enable_display = true;
275 display_model (_model);
279 reset_width_dependent_items (_pixel_width);
281 group->raise_to_top();
283 midi_view()->midi_track()->PlaybackChannelModeChanged.connect (_channel_mode_changed_connection, invalidator (*this),
284 boost::bind (&MidiRegionView::midi_channel_mode_changed, this),
287 instrument_info().Changed.connect (_instrument_changed_connection, invalidator (*this),
288 boost::bind (&MidiRegionView::instrument_settings_changed, this), gui_context());
290 trackview.editor().SnapChanged.connect(snap_changed_connection, invalidator(*this),
291 boost::bind (&MidiRegionView::snap_changed, this),
294 Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&MidiRegionView::parameter_changed, this, _1), gui_context());
295 connect_to_diskstream ();
297 SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), boost::bind (&MidiRegionView::selection_cleared, this, _1), gui_context ());
301 MidiRegionView::instrument_info () const
303 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
304 return route_ui->route()->instrument_info();
307 const boost::shared_ptr<ARDOUR::MidiRegion>
308 MidiRegionView::midi_region() const
310 return boost::dynamic_pointer_cast<ARDOUR::MidiRegion>(_region);
314 MidiRegionView::connect_to_diskstream ()
316 midi_view()->midi_track()->DataRecorded.connect(
317 *this, invalidator(*this),
318 boost::bind (&MidiRegionView::data_recorded, this, _1),
323 MidiRegionView::canvas_group_event(GdkEvent* ev)
328 case GDK_ENTER_NOTIFY:
329 case GDK_LEAVE_NOTIFY:
330 _last_event_x = ev->crossing.x;
331 _last_event_y = ev->crossing.y;
333 case GDK_MOTION_NOTIFY:
334 _last_event_x = ev->motion.x;
335 _last_event_y = ev->motion.y;
341 if (ev->type == GDK_2BUTTON_PRESS) {
342 // cannot use double-click to exit internal mode if single-click is being used
343 MouseMode m = trackview.editor().current_mouse_mode();
345 if ((m != MouseObject || !Keyboard::modifier_state_contains (ev->button.state, Keyboard::insert_note_modifier())) && (m != MouseDraw)) {
346 return trackview.editor().toggle_internal_editing_from_double_click (ev);
350 if ((!trackview.editor().internal_editing() && trackview.editor().current_mouse_mode() != MouseGain) ||
351 (trackview.editor().current_mouse_mode() == MouseTimeFX) ||
352 (trackview.editor().current_mouse_mode() == MouseZoom)) {
353 // handle non-internal-edit/non-draw modes elsewhere
354 return RegionView::canvas_group_event (ev);
359 return scroll (&ev->scroll);
362 return key_press (&ev->key);
364 case GDK_KEY_RELEASE:
365 return key_release (&ev->key);
367 case GDK_BUTTON_PRESS:
368 return button_press (&ev->button);
370 case GDK_BUTTON_RELEASE:
371 r = button_release (&ev->button);
376 case GDK_ENTER_NOTIFY:
377 return enter_notify (&ev->crossing);
379 case GDK_LEAVE_NOTIFY:
380 return leave_notify (&ev->crossing);
382 case GDK_MOTION_NOTIFY:
383 return motion (&ev->motion);
389 return trackview.editor().canvas_region_view_event (ev, group, this);
393 MidiRegionView::enter_notify (GdkEventCrossing* ev)
395 trackview.editor().MouseModeChanged.connect (
396 _mouse_mode_connection, invalidator (*this), boost::bind (&MidiRegionView::mouse_mode_changed, this), gui_context ()
399 if (trackview.editor().current_mouse_mode() == MouseDraw && _mouse_state != AddDragging) {
400 create_ghost_note (ev->x, ev->y);
403 if (!trackview.editor().internal_editing()) {
404 Keyboard::magic_widget_drop_focus();
406 Keyboard::magic_widget_grab_focus();
410 // if current operation is non-operational in a midi region, change the cursor to so indicate
411 if (trackview.editor().current_mouse_mode() == MouseGain) {
412 Editor* editor = dynamic_cast<Editor *> (&trackview.editor());
413 pre_enter_cursor = editor->get_canvas_cursor();
414 editor->set_canvas_cursor(editor->cursors()->timebar);
421 MidiRegionView::leave_notify (GdkEventCrossing*)
423 _mouse_mode_connection.disconnect ();
425 trackview.editor().verbose_cursor()->hide ();
426 remove_ghost_note ();
428 if (pre_enter_cursor) {
429 Editor* editor = dynamic_cast<Editor *> (&trackview.editor());
430 editor->set_canvas_cursor(pre_enter_cursor);
437 MidiRegionView::mouse_mode_changed ()
439 if (trackview.editor().current_mouse_mode() == MouseDraw && trackview.editor().internal_editing()) {
440 create_ghost_note (_last_event_x, _last_event_y);
442 remove_ghost_note ();
443 trackview.editor().verbose_cursor()->hide ();
446 if (!trackview.editor().internal_editing()) {
447 Keyboard::magic_widget_drop_focus();
449 Keyboard::magic_widget_grab_focus();
455 MidiRegionView::button_press (GdkEventButton* ev)
457 if (ev->button != 1) {
461 Editor* editor = dynamic_cast<Editor *> (&trackview.editor());
462 MouseMode m = editor->current_mouse_mode();
464 if (m == MouseObject && Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())) {
465 pre_press_cursor = editor->get_canvas_cursor ();
466 editor->set_canvas_cursor (editor->cursors()->midi_pencil);
469 if (_mouse_state != SelectTouchDragging) {
471 _pressed_button = ev->button;
472 _mouse_state = Pressed;
477 _pressed_button = ev->button;
483 MidiRegionView::button_release (GdkEventButton* ev)
485 double event_x, event_y;
487 if (ev->button != 1) {
494 group->canvas_to_item (event_x, event_y);
497 PublicEditor& editor = trackview.editor ();
499 if (pre_press_cursor) {
500 dynamic_cast<Editor*>(&editor)->set_canvas_cursor (pre_press_cursor, false);
501 pre_press_cursor = 0;
504 switch (_mouse_state) {
505 case Pressed: // Clicked
507 switch (editor.current_mouse_mode()) {
509 /* no motion occured - simple click */
518 if (Keyboard::is_insert_note_event(ev)) {
520 double event_x, event_y;
524 group->canvas_to_item (event_x, event_y);
527 Evoral::MusicalTime beats = editor.get_grid_type_as_beats (success, editor.pixel_to_sample (event_x));
533 /* Shorten the length by 1 tick so that we can add a new note at the next
534 grid snap without it overlapping this one.
536 beats -= 1.0 / Timecode::BBT_Time::ticks_per_beat;
538 create_note_at (editor.pixel_to_sample (event_x), event_y, beats, true);
546 Evoral::MusicalTime beats = editor.get_grid_type_as_beats (success, editor.pixel_to_sample (event_x));
552 /* Shorten the length by 1 tick so that we can add a new note at the next
553 grid snap without it overlapping this one.
555 beats -= 1.0 / Timecode::BBT_Time::ticks_per_beat;
557 create_note_at (editor.pixel_to_sample (event_x), event_y, beats, true);
568 case SelectRectDragging:
570 editor.drags()->end_grab ((GdkEvent *) ev);
572 create_ghost_note (ev->x, ev->y);
584 MidiRegionView::motion (GdkEventMotion* ev)
586 PublicEditor& editor = trackview.editor ();
588 if (!_ghost_note && editor.current_mouse_mode() == MouseObject &&
589 Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier()) &&
590 _mouse_state != AddDragging) {
592 create_ghost_note (ev->x, ev->y);
594 } else if (_ghost_note && editor.current_mouse_mode() == MouseObject &&
595 Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())) {
597 update_ghost_note (ev->x, ev->y);
599 } else if (_ghost_note && editor.current_mouse_mode() == MouseObject) {
601 remove_ghost_note ();
602 editor.verbose_cursor()->hide ();
604 } else if (_ghost_note && editor.current_mouse_mode() == MouseDraw) {
606 update_ghost_note (ev->x, ev->y);
609 /* any motion immediately hides velocity text that may have been visible */
611 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
612 (*i)->hide_velocity ();
615 switch (_mouse_state) {
618 if (_pressed_button == 1) {
620 MouseMode m = editor.current_mouse_mode();
622 if (m == MouseDraw || (m == MouseObject && Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier()))) {
623 editor.drags()->set (new NoteCreateDrag (dynamic_cast<Editor *> (&editor), group, this), (GdkEvent *) ev);
624 _mouse_state = AddDragging;
625 remove_ghost_note ();
626 editor.verbose_cursor()->hide ();
628 } else if (m == MouseObject) {
629 editor.drags()->set (new MidiRubberbandSelectDrag (dynamic_cast<Editor *> (&editor), this), (GdkEvent *) ev);
631 _mouse_state = SelectRectDragging;
633 } else if (m == MouseRange) {
634 editor.drags()->set (new MidiVerticalSelectDrag (dynamic_cast<Editor *> (&editor), this), (GdkEvent *) ev);
635 _mouse_state = SelectVerticalDragging;
642 case SelectRectDragging:
643 case SelectVerticalDragging:
645 editor.drags()->motion_handler ((GdkEvent *) ev, false);
648 case SelectTouchDragging:
656 /* we may be dragging some non-note object (eg. patch-change, sysex)
659 return editor.drags()->motion_handler ((GdkEvent *) ev, false);
664 MidiRegionView::scroll (GdkEventScroll* ev)
666 if (_selection.empty()) {
670 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
671 /* XXX: bit of a hack; allow PrimaryModifier scroll through so that
672 it still works for zoom.
677 trackview.editor().verbose_cursor()->hide ();
679 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
680 bool together = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
682 if (ev->direction == GDK_SCROLL_UP) {
683 change_velocities (true, fine, false, together);
684 } else if (ev->direction == GDK_SCROLL_DOWN) {
685 change_velocities (false, fine, false, together);
687 /* left, right: we don't use them */
695 MidiRegionView::key_press (GdkEventKey* ev)
697 /* since GTK bindings are generally activated on press, and since
698 detectable auto-repeat is the name of the game and only sends
699 repeated presses, carry out key actions at key press, not release.
702 bool unmodified = Keyboard::no_modifier_keys_pressed (ev);
704 if (unmodified && (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R)) {
705 _mouse_state = SelectTouchDragging;
708 } else if (ev->keyval == GDK_Escape && unmodified) {
712 } else if (unmodified && (ev->keyval == GDK_comma || ev->keyval == GDK_period)) {
714 bool start = (ev->keyval == GDK_comma);
715 bool end = (ev->keyval == GDK_period);
716 bool shorter = Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier);
717 bool fine = Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
719 change_note_lengths (fine, shorter, 0.0, start, end);
723 } else if ((ev->keyval == GDK_BackSpace || ev->keyval == GDK_Delete) && unmodified) {
725 if (_selection.empty()) {
732 } else if (ev->keyval == GDK_Tab) {
734 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
735 goto_previous_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
737 goto_next_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
741 } else if (ev->keyval == GDK_ISO_Left_Tab) {
743 /* Shift-TAB generates ISO Left Tab, for some reason */
745 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
746 goto_previous_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
748 goto_next_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
754 } else if (ev->keyval == GDK_Up) {
756 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
757 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
758 bool together = Keyboard::modifier_state_contains (ev->state, Keyboard::Level4Modifier);
760 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
761 change_velocities (true, fine, allow_smush, together);
763 transpose (true, fine, allow_smush);
767 } else if (ev->keyval == GDK_Down) {
769 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
770 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
771 bool together = Keyboard::modifier_state_contains (ev->state, Keyboard::Level4Modifier);
773 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
774 change_velocities (false, fine, allow_smush, together);
776 transpose (false, fine, allow_smush);
780 } else if (ev->keyval == GDK_Left && unmodified) {
785 } else if (ev->keyval == GDK_Right && unmodified) {
790 } else if (ev->keyval == GDK_c && unmodified) {
794 } else if (ev->keyval == GDK_v && unmodified) {
803 MidiRegionView::key_release (GdkEventKey* ev)
805 if ((_mouse_state == SelectTouchDragging) && (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R)) {
813 MidiRegionView::channel_edit ()
815 if (_selection.empty()) {
819 /* pick a note somewhat at random (since Selection is a set<>) to
820 * provide the "current" channel for the dialog.
823 uint8_t current_channel = (*_selection.begin())->note()->channel ();
824 MidiChannelDialog channel_dialog (current_channel);
825 int ret = channel_dialog.run ();
828 case Gtk::RESPONSE_OK:
834 uint8_t new_channel = channel_dialog.active_channel ();
836 start_note_diff_command (_("channel edit"));
838 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
839 Selection::iterator next = i;
841 change_note_channel (*i, new_channel);
849 MidiRegionView::velocity_edit ()
851 if (_selection.empty()) {
855 /* pick a note somewhat at random (since Selection is a set<>) to
856 * provide the "current" velocity for the dialog.
859 uint8_t current_velocity = (*_selection.begin())->note()->velocity ();
860 MidiVelocityDialog velocity_dialog (current_velocity);
861 int ret = velocity_dialog.run ();
864 case Gtk::RESPONSE_OK:
870 uint8_t new_velocity = velocity_dialog.velocity ();
872 start_note_diff_command (_("velocity edit"));
874 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
875 Selection::iterator next = i;
877 change_note_velocity (*i, new_velocity, false);
885 MidiRegionView::show_list_editor ()
888 _list_editor = new MidiListEditor (trackview.session(), midi_region(), midi_view()->midi_track());
890 _list_editor->present ();
893 /** Add a note to the model, and the view, at a canvas (click) coordinate.
894 * \param t time in frames relative to the position of the region
895 * \param y vertical position in pixels
896 * \param length duration of the note in beats
897 * \param snap_t true to snap t to the grid, otherwise false.
900 MidiRegionView::create_note_at (framepos_t t, double y, double length, bool snap_t)
902 if (length < 2 * DBL_EPSILON) {
906 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
907 MidiStreamView* const view = mtv->midi_view();
909 const double note = view->y_to_note(y);
911 // Start of note in frames relative to region start
913 framecnt_t grid_frames;
914 t = snap_frame_to_grid_underneath (t, grid_frames);
917 const boost::shared_ptr<NoteType> new_note (
918 new NoteType (mtv->get_channel_for_add (),
919 region_frames_to_region_beats(t + _region->start()),
921 (uint8_t)note, 0x40));
923 if (_model->contains (new_note)) {
927 view->update_note_range(new_note->note());
929 MidiModel::NoteDiffCommand* cmd = _model->new_note_diff_command(_("add note"));
931 _model->apply_command(*trackview.session(), cmd);
933 play_midi_note (new_note);
937 MidiRegionView::clear_events (bool with_selection_signal)
939 clear_selection (with_selection_signal);
942 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
943 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
948 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
953 _patch_changes.clear();
955 _optimization_iterator = _events.end();
959 MidiRegionView::display_model(boost::shared_ptr<MidiModel> model)
963 content_connection.disconnect ();
964 _model->ContentsChanged.connect (content_connection, invalidator (*this), boost::bind (&MidiRegionView::redisplay_model, this), gui_context());
968 if (_enable_display) {
974 MidiRegionView::start_note_diff_command (string name)
976 if (!_note_diff_command) {
977 _note_diff_command = _model->new_note_diff_command (name);
982 MidiRegionView::note_diff_add_note (const boost::shared_ptr<NoteType> note, bool selected, bool show_velocity)
984 if (_note_diff_command) {
985 _note_diff_command->add (note);
988 _marked_for_selection.insert(note);
991 _marked_for_velocity.insert(note);
996 MidiRegionView::note_diff_remove_note (NoteBase* ev)
998 if (_note_diff_command && ev->note()) {
999 _note_diff_command->remove(ev->note());
1004 MidiRegionView::note_diff_add_change (NoteBase* ev,
1005 MidiModel::NoteDiffCommand::Property property,
1008 if (_note_diff_command) {
1009 _note_diff_command->change (ev->note(), property, val);
1014 MidiRegionView::note_diff_add_change (NoteBase* ev,
1015 MidiModel::NoteDiffCommand::Property property,
1016 Evoral::MusicalTime val)
1018 if (_note_diff_command) {
1019 _note_diff_command->change (ev->note(), property, val);
1024 MidiRegionView::apply_diff (bool as_subcommand)
1028 if (!_note_diff_command) {
1032 if ((add_or_remove = _note_diff_command->adds_or_removes())) {
1033 // Mark all selected notes for selection when model reloads
1034 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1035 _marked_for_selection.insert((*i)->note());
1039 if (as_subcommand) {
1040 _model->apply_command_as_subcommand (*trackview.session(), _note_diff_command);
1042 _model->apply_command (*trackview.session(), _note_diff_command);
1045 _note_diff_command = 0;
1046 midi_view()->midi_track()->playlist_modified();
1048 if (add_or_remove) {
1049 _marked_for_selection.clear();
1052 _marked_for_velocity.clear();
1056 MidiRegionView::abort_command()
1058 delete _note_diff_command;
1059 _note_diff_command = 0;
1064 MidiRegionView::find_canvas_note (boost::shared_ptr<NoteType> note)
1066 if (_optimization_iterator != _events.end()) {
1067 ++_optimization_iterator;
1070 if (_optimization_iterator != _events.end() && (*_optimization_iterator)->note() == note) {
1071 return *_optimization_iterator;
1074 for (_optimization_iterator = _events.begin(); _optimization_iterator != _events.end(); ++_optimization_iterator) {
1075 if ((*_optimization_iterator)->note() == note) {
1076 return *_optimization_iterator;
1084 MidiRegionView::get_events (Events& e, Evoral::Sequence<Evoral::MusicalTime>::NoteOperator op, uint8_t val, int chan_mask)
1086 MidiModel::Notes notes;
1087 _model->get_notes (notes, op, val, chan_mask);
1089 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1090 NoteBase* cne = find_canvas_note (*n);
1098 MidiRegionView::redisplay_model()
1100 // Don't redisplay the model if we're currently recording and displaying that
1101 if (_active_notes) {
1109 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1110 (*i)->invalidate ();
1113 MidiModel::ReadLock lock(_model->read_lock());
1115 MidiModel::Notes& notes (_model->notes());
1116 _optimization_iterator = _events.begin();
1118 bool empty_when_starting = !_events.empty();
1120 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1122 boost::shared_ptr<NoteType> note (*n);
1126 if (note_in_region_range (note, visible)) {
1128 if (empty_when_starting && (cne = find_canvas_note (note)) != 0) {
1135 if ((cn = dynamic_cast<Note*>(cne)) != 0) {
1137 } else if ((ch = dynamic_cast<Hit*>(cne)) != 0) {
1149 add_note (note, visible);
1154 if (empty_when_starting && (cne = find_canvas_note (note)) != 0) {
1162 /* remove note items that are no longer valid */
1164 if (empty_when_starting) {
1165 for (Events::iterator i = _events.begin(); i != _events.end(); ) {
1166 if (!(*i)->valid ()) {
1168 for (vector<GhostRegion*>::iterator j = ghosts.begin(); j != ghosts.end(); ++j) {
1169 MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*j);
1171 gr->remove_note (*i);
1176 i = _events.erase (i);
1184 _patch_changes.clear();
1188 display_patch_changes ();
1190 _marked_for_selection.clear ();
1191 _marked_for_velocity.clear ();
1193 /* we may have caused _events to contain things out of order (e.g. if a note
1194 moved earlier or later). we don't generally need them in time order, but
1195 make a note that a sort is required for those cases that require it.
1198 _sort_needed = true;
1202 MidiRegionView::display_patch_changes ()
1204 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1205 uint16_t chn_mask = mtv->midi_track()->get_playback_channel_mask();
1207 for (uint8_t i = 0; i < 16; ++i) {
1208 display_patch_changes_on_channel (i, chn_mask & (1 << i));
1212 /** @param active_channel true to display patch changes fully, false to display
1213 * them `greyed-out' (as on an inactive channel)
1216 MidiRegionView::display_patch_changes_on_channel (uint8_t channel, bool active_channel)
1218 for (MidiModel::PatchChanges::const_iterator i = _model->patch_changes().begin(); i != _model->patch_changes().end(); ++i) {
1220 if ((*i)->channel() != channel) {
1224 const string patch_name = instrument_info().get_patch_name ((*i)->bank(), (*i)->program(), channel);
1225 add_canvas_patch_change (*i, patch_name, active_channel);
1230 MidiRegionView::display_sysexes()
1232 bool have_periodic_system_messages = false;
1233 bool display_periodic_messages = true;
1235 if (!Config->get_never_display_periodic_midi()) {
1237 for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
1238 const boost::shared_ptr<const Evoral::MIDIEvent<Evoral::MusicalTime> > mev =
1239 boost::static_pointer_cast<const Evoral::MIDIEvent<Evoral::MusicalTime> > (*i);
1242 if (mev->is_spp() || mev->is_mtc_quarter() || mev->is_mtc_full()) {
1243 have_periodic_system_messages = true;
1249 if (have_periodic_system_messages) {
1250 double zoom = trackview.editor().get_current_zoom (); // frames per pixel
1252 /* get an approximate value for the number of samples per video frame */
1254 double video_frame = trackview.session()->frame_rate() * (1.0/30);
1256 /* if we are zoomed out beyond than the cutoff (i.e. more
1257 * frames per pixel than frames per 4 video frames), don't
1258 * show periodic sysex messages.
1261 if (zoom > (video_frame*4)) {
1262 display_periodic_messages = false;
1266 display_periodic_messages = false;
1269 for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
1271 const boost::shared_ptr<const Evoral::MIDIEvent<Evoral::MusicalTime> > mev =
1272 boost::static_pointer_cast<const Evoral::MIDIEvent<Evoral::MusicalTime> > (*i);
1274 Evoral::MusicalTime time = (*i)->time();
1277 if (mev->is_spp() || mev->is_mtc_quarter() || mev->is_mtc_full()) {
1278 if (!display_periodic_messages) {
1286 for (uint32_t b = 0; b < (*i)->size(); ++b) {
1287 str << int((*i)->buffer()[b]);
1288 if (b != (*i)->size() -1) {
1292 string text = str.str();
1294 const double x = trackview.editor().sample_to_pixel(source_beats_to_region_frames(time));
1296 double height = midi_stream_view()->contents_height();
1298 // CAIROCANVAS: no longer passing *i (the sysex event) to the
1299 // SysEx canvas object!!!
1301 boost::shared_ptr<SysEx> sysex = boost::shared_ptr<SysEx>(
1302 new SysEx (*this, _note_group, text, height, x, 1.0));
1304 // Show unless message is beyond the region bounds
1305 if (time - _region->start() >= _region->length() || time < _region->start()) {
1311 _sys_exes.push_back(sysex);
1315 MidiRegionView::~MidiRegionView ()
1317 in_destructor = true;
1319 trackview.editor().verbose_cursor()->hide ();
1321 note_delete_connection.disconnect ();
1323 delete _list_editor;
1325 RegionViewGoingAway (this); /* EMIT_SIGNAL */
1327 if (_active_notes) {
1331 _selection_cleared_connection.disconnect ();
1334 clear_events (false);
1337 delete _note_diff_command;
1338 delete _step_edit_cursor;
1339 delete _temporary_note_group;
1343 MidiRegionView::region_resized (const PropertyChange& what_changed)
1345 RegionView::region_resized(what_changed);
1347 if (what_changed.contains (ARDOUR::Properties::position)) {
1348 set_duration(_region->length(), 0);
1349 if (_enable_display) {
1356 MidiRegionView::reset_width_dependent_items (double pixel_width)
1358 RegionView::reset_width_dependent_items(pixel_width);
1360 if (_enable_display) {
1364 for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
1365 if ((*x)->canvas_item()->width() >= _pixel_width) {
1372 move_step_edit_cursor (_step_edit_cursor_position);
1373 set_step_edit_cursor_width (_step_edit_cursor_width);
1377 MidiRegionView::set_height (double height)
1379 static const double FUDGE = 2.0;
1380 const double old_height = _height;
1381 RegionView::set_height(height);
1382 _height = height - FUDGE;
1384 apply_note_range(midi_stream_view()->lowest_note(),
1385 midi_stream_view()->highest_note(),
1386 height != old_height + FUDGE);
1389 name_text->raise_to_top();
1392 for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
1393 (*x)->set_height (midi_stream_view()->contents_height());
1396 if (_step_edit_cursor) {
1397 _step_edit_cursor->set_y1 (midi_stream_view()->contents_height());
1402 /** Apply the current note range from the stream view
1403 * by repositioning/hiding notes as necessary
1406 MidiRegionView::apply_note_range (uint8_t min, uint8_t max, bool force)
1408 if (!_enable_display) {
1412 if (!force && _current_range_min == min && _current_range_max == max) {
1416 _current_range_min = min;
1417 _current_range_max = max;
1419 for (Events::const_iterator i = _events.begin(); i != _events.end(); ++i) {
1420 NoteBase* event = *i;
1421 boost::shared_ptr<NoteType> note (event->note());
1423 if (note->note() < _current_range_min ||
1424 note->note() > _current_range_max) {
1430 if (Note* cnote = dynamic_cast<Note*>(event)) {
1432 const double y0 = midi_stream_view()->note_to_y(note->note());
1433 const double y1 = y0 + floor(midi_stream_view()->note_height());
1438 } else if (Hit* chit = dynamic_cast<Hit*>(event)) {
1446 MidiRegionView::add_ghost (TimeAxisView& tv)
1450 double unit_position = _region->position () / samples_per_pixel;
1451 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(&tv);
1452 MidiGhostRegion* ghost;
1454 if (mtv && mtv->midi_view()) {
1455 /* if ghost is inserted into midi track, use a dedicated midi ghost canvas group
1456 to allow having midi notes on top of note lines and waveforms.
1458 ghost = new MidiGhostRegion (*mtv->midi_view(), trackview, unit_position);
1460 ghost = new MidiGhostRegion (tv, trackview, unit_position);
1463 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1464 if ((note = dynamic_cast<Note*>(*i)) != 0) {
1465 ghost->add_note(note);
1469 ghost->set_height ();
1470 ghost->set_duration (_region->length() / samples_per_pixel);
1471 ghosts.push_back (ghost);
1473 GhostRegion::CatchDeletion.connect (*this, invalidator (*this), boost::bind (&RegionView::remove_ghost, this, _1), gui_context());
1479 /** Begin tracking note state for successive calls to add_event
1482 MidiRegionView::begin_write()
1484 if (_active_notes) {
1485 delete[] _active_notes;
1487 _active_notes = new Note*[128];
1488 for (unsigned i = 0; i < 128; ++i) {
1489 _active_notes[i] = 0;
1494 /** Destroy note state for add_event
1497 MidiRegionView::end_write()
1499 delete[] _active_notes;
1501 _marked_for_selection.clear();
1502 _marked_for_velocity.clear();
1506 /** Resolve an active MIDI note (while recording).
1509 MidiRegionView::resolve_note(uint8_t note, double end_time)
1511 if (midi_view()->note_mode() != Sustained) {
1515 if (_active_notes && _active_notes[note]) {
1517 /* XXX is end_time really region-centric? I think so, because
1518 this is a new region that we're recording, so source zero is
1519 the same as region zero
1521 const framepos_t end_time_frames = region_beats_to_region_frames(end_time);
1523 _active_notes[note]->set_x1 (trackview.editor().sample_to_pixel(end_time_frames));
1524 _active_notes[note]->set_outline_what (0xf);
1525 _active_notes[note] = 0;
1530 /** Extend active notes to rightmost edge of region (if length is changed)
1533 MidiRegionView::extend_active_notes()
1535 if (!_active_notes) {
1539 for (unsigned i=0; i < 128; ++i) {
1540 if (_active_notes[i]) {
1541 _active_notes[i]->set_x1 (trackview.editor().sample_to_pixel(_region->length()));
1548 MidiRegionView::play_midi_note(boost::shared_ptr<NoteType> note)
1550 if (_no_sound_notes || !Config->get_sound_midi_notes()) {
1554 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1556 if (!route_ui || !route_ui->midi_track()) {
1560 NotePlayer* np = new NotePlayer (route_ui->midi_track ());
1564 /* NotePlayer deletes itself */
1568 MidiRegionView::start_playing_midi_note(boost::shared_ptr<NoteType> note)
1570 if (_no_sound_notes || !Config->get_sound_midi_notes()) {
1574 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1576 if (!route_ui || !route_ui->midi_track()) {
1580 delete _note_player;
1581 _note_player = new NotePlayer (route_ui->midi_track ());
1582 _note_player->add (note);
1583 _note_player->on ();
1587 MidiRegionView::start_playing_midi_chord (vector<boost::shared_ptr<NoteType> > notes)
1589 if (_no_sound_notes || !Config->get_sound_midi_notes()) {
1593 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1595 if (!route_ui || !route_ui->midi_track()) {
1599 delete _note_player;
1600 _note_player = new NotePlayer (route_ui->midi_track());
1602 for (vector<boost::shared_ptr<NoteType> >::iterator n = notes.begin(); n != notes.end(); ++n) {
1603 _note_player->add (*n);
1606 _note_player->on ();
1611 MidiRegionView::note_in_region_range (const boost::shared_ptr<NoteType> note, bool& visible) const
1613 const framepos_t note_start_frames = source_beats_to_region_frames (note->time());
1614 bool outside = (note_start_frames < 0) || (note_start_frames > _region->last_frame());
1616 visible = (note->note() >= midi_stream_view()->lowest_note()) &&
1617 (note->note() <= midi_stream_view()->highest_note());
1622 /** Update a canvas note's size from its model note.
1623 * @param ev Canvas note to update.
1624 * @param update_ghost_regions true to update the note in any ghost regions that we have, otherwise false.
1627 MidiRegionView::update_note (Note* ev, bool update_ghost_regions)
1629 boost::shared_ptr<NoteType> note = ev->note();
1630 const double x = trackview.editor().sample_to_pixel (source_beats_to_region_frames (note->time()));
1631 const double y0 = midi_stream_view()->note_to_y(note->note());
1636 /* trim note display to not overlap the end of its region */
1638 if (note->length() > 0) {
1639 const framepos_t note_end_frames = min (source_beats_to_region_frames (note->end_time()), _region->length());
1640 ev->set_x1 (trackview.editor().sample_to_pixel (note_end_frames));
1642 ev->set_x1 (trackview.editor().sample_to_pixel (_region->length()));
1645 ev->set_y1 (y0 + floor(midi_stream_view()->note_height()));
1647 if (note->length() == 0) {
1648 if (_active_notes && note->note() < 128) {
1649 // If this note is already active there's a stuck note,
1650 // finish the old note rectangle
1651 if (_active_notes[note->note()]) {
1652 Note* const old_rect = _active_notes[note->note()];
1653 boost::shared_ptr<NoteType> old_note = old_rect->note();
1654 old_rect->set_x1 (x);
1655 old_rect->set_outline_what (0xF);
1657 _active_notes[note->note()] = ev;
1659 /* outline all but right edge */
1660 ev->set_outline_what (0x1 & 0x4 & 0x8);
1662 /* outline all edges */
1663 ev->set_outline_what (0xF);
1666 if (update_ghost_regions) {
1667 for (std::vector<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
1668 MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*i);
1670 gr->update_note (ev);
1677 MidiRegionView::update_hit (Hit* ev)
1679 boost::shared_ptr<NoteType> note = ev->note();
1681 const framepos_t note_start_frames = source_beats_to_region_frames(note->time());
1682 const double x = trackview.editor().sample_to_pixel(note_start_frames);
1683 const double diamond_size = midi_stream_view()->note_height();
1684 const double y = midi_stream_view()->note_to_y(note->note()) + (diamond_size/2.0);
1686 ev->set_position (ArdourCanvas::Duple (x, y));
1687 ev->set_height (diamond_size);
1690 /** Add a MIDI note to the view (with length).
1692 * If in sustained mode, notes with length 0 will be considered active
1693 * notes, and resolve_note should be called when the corresponding note off
1694 * event arrives, to properly display the note.
1697 MidiRegionView::add_note(const boost::shared_ptr<NoteType> note, bool visible)
1699 NoteBase* event = 0;
1701 if (midi_view()->note_mode() == Sustained) {
1703 Note* ev_rect = new Note (*this, _note_group, note);
1705 update_note (ev_rect);
1709 MidiGhostRegion* gr;
1711 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
1712 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
1713 gr->add_note(ev_rect);
1717 } else if (midi_view()->note_mode() == Percussive) {
1719 const double diamond_size = midi_stream_view()->note_height() / 2.0;
1721 Hit* ev_diamond = new Hit (*this, _note_group, diamond_size, note);
1723 update_hit (ev_diamond);
1732 if (_marked_for_selection.find(note) != _marked_for_selection.end()) {
1733 note_selected(event, true);
1736 if (_marked_for_velocity.find(note) != _marked_for_velocity.end()) {
1737 event->show_velocity();
1740 event->on_channel_selection_change (get_selected_channels());
1741 _events.push_back(event);
1750 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1751 MidiStreamView* const view = mtv->midi_view();
1753 view->update_note_range (note->note());
1757 MidiRegionView::step_add_note (uint8_t channel, uint8_t number, uint8_t velocity,
1758 Evoral::MusicalTime pos, Evoral::MusicalTime len)
1760 boost::shared_ptr<NoteType> new_note (new NoteType (channel, pos, len, number, velocity));
1762 /* potentially extend region to hold new note */
1764 framepos_t end_frame = source_beats_to_absolute_frames (new_note->end_time());
1765 framepos_t region_end = _region->last_frame();
1767 if (end_frame > region_end) {
1768 _region->set_length (end_frame - _region->position());
1771 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1772 MidiStreamView* const view = mtv->midi_view();
1774 view->update_note_range(new_note->note());
1776 _marked_for_selection.clear ();
1779 start_note_diff_command (_("step add"));
1780 note_diff_add_note (new_note, true, false);
1783 // last_step_edit_note = new_note;
1787 MidiRegionView::step_sustain (Evoral::MusicalTime beats)
1789 change_note_lengths (false, false, beats, false, true);
1792 /** Add a new patch change flag to the canvas.
1793 * @param patch the patch change to add
1794 * @param the text to display in the flag
1795 * @param active_channel true to display the flag as on an active channel, false to grey it out for an inactive channel.
1798 MidiRegionView::add_canvas_patch_change (MidiModel::PatchChangePtr patch, const string& displaytext, bool /*active_channel*/)
1800 framecnt_t region_frames = source_beats_to_region_frames (patch->time());
1801 const double x = trackview.editor().sample_to_pixel (region_frames);
1803 double const height = midi_stream_view()->contents_height();
1805 // CAIROCANVAS: active_channel info removed from PatcChange constructor
1806 // so we need to do something more sophisticated to keep its color
1807 // appearance (MidiPatchChangeFill/MidiPatchChangeInactiveChannelFill)
1810 boost::shared_ptr<PatchChange> patch_change = boost::shared_ptr<PatchChange>(
1811 new PatchChange(*this, group,
1818 if (patch_change->item().width() < _pixel_width) {
1819 // Show unless patch change is beyond the region bounds
1820 if (region_frames < 0 || region_frames >= _region->length()) {
1821 patch_change->hide();
1823 patch_change->show();
1826 patch_change->hide ();
1829 _patch_changes.push_back (patch_change);
1832 MIDI::Name::PatchPrimaryKey
1833 MidiRegionView::patch_change_to_patch_key (MidiModel::PatchChangePtr p)
1835 return MIDI::Name::PatchPrimaryKey (p->program(), p->bank());
1838 /// Return true iff @p pc applies to the given time on the given channel.
1840 patch_applies (const ARDOUR::MidiModel::constPatchChangePtr pc, double time, uint8_t channel)
1842 return pc->time() <= time && pc->channel() == channel;
1846 MidiRegionView::get_patch_key_at (double time, uint8_t channel, MIDI::Name::PatchPrimaryKey& key) const
1848 // The earliest event not before time
1849 MidiModel::PatchChanges::iterator i = _model->patch_change_lower_bound (time);
1851 // Go backwards until we find the latest PC for this channel, or the start
1852 while (i != _model->patch_changes().begin() &&
1853 (i == _model->patch_changes().end() ||
1854 !patch_applies(*i, time, channel))) {
1858 if (i != _model->patch_changes().end() && patch_applies(*i, time, channel)) {
1859 key.bank_number = (*i)->bank();
1860 key.program_number = (*i)->program ();
1862 key.bank_number = key.program_number = 0;
1865 if (!key.is_sane()) {
1866 error << string_compose(_("insane MIDI patch key %1:%2"),
1867 key.bank_number, key.program_number) << endmsg;
1872 MidiRegionView::change_patch_change (PatchChange& pc, const MIDI::Name::PatchPrimaryKey& new_patch)
1874 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("alter patch change"));
1876 if (pc.patch()->program() != new_patch.program_number) {
1877 c->change_program (pc.patch (), new_patch.program_number);
1880 int const new_bank = new_patch.bank_number;
1881 if (pc.patch()->bank() != new_bank) {
1882 c->change_bank (pc.patch (), new_bank);
1885 _model->apply_command (*trackview.session(), c);
1887 _patch_changes.clear ();
1888 display_patch_changes ();
1892 MidiRegionView::change_patch_change (MidiModel::PatchChangePtr old_change, const Evoral::PatchChange<Evoral::MusicalTime> & new_change)
1894 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("alter patch change"));
1896 if (old_change->time() != new_change.time()) {
1897 c->change_time (old_change, new_change.time());
1900 if (old_change->channel() != new_change.channel()) {
1901 c->change_channel (old_change, new_change.channel());
1904 if (old_change->program() != new_change.program()) {
1905 c->change_program (old_change, new_change.program());
1908 if (old_change->bank() != new_change.bank()) {
1909 c->change_bank (old_change, new_change.bank());
1912 _model->apply_command (*trackview.session(), c);
1914 _patch_changes.clear ();
1915 display_patch_changes ();
1918 /** Add a patch change to the region.
1919 * @param t Time in frames relative to region position
1920 * @param patch Patch to add; time and channel are ignored (time is converted from t, and channel comes from
1921 * MidiTimeAxisView::get_channel_for_add())
1924 MidiRegionView::add_patch_change (framecnt_t t, Evoral::PatchChange<Evoral::MusicalTime> const & patch)
1926 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1928 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("add patch change"));
1929 c->add (MidiModel::PatchChangePtr (
1930 new Evoral::PatchChange<Evoral::MusicalTime> (
1931 absolute_frames_to_source_beats (_region->position() + t),
1932 mtv->get_channel_for_add(), patch.program(), patch.bank()
1937 _model->apply_command (*trackview.session(), c);
1939 _patch_changes.clear ();
1940 display_patch_changes ();
1944 MidiRegionView::move_patch_change (PatchChange& pc, Evoral::MusicalTime t)
1946 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("move patch change"));
1947 c->change_time (pc.patch (), t);
1948 _model->apply_command (*trackview.session(), c);
1950 _patch_changes.clear ();
1951 display_patch_changes ();
1955 MidiRegionView::delete_patch_change (PatchChange* pc)
1957 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("delete patch change"));
1958 c->remove (pc->patch ());
1959 _model->apply_command (*trackview.session(), c);
1961 _patch_changes.clear ();
1962 display_patch_changes ();
1966 MidiRegionView::previous_patch (PatchChange& patch)
1968 if (patch.patch()->program() < 127) {
1969 MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch());
1970 key.program_number++;
1971 change_patch_change (patch, key);
1976 MidiRegionView::next_patch (PatchChange& patch)
1978 if (patch.patch()->program() > 0) {
1979 MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch());
1980 key.program_number--;
1981 change_patch_change (patch, key);
1986 MidiRegionView::next_bank (PatchChange& patch)
1988 if (patch.patch()->program() < 127) {
1989 MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch());
1990 if (key.bank_number > 0) {
1992 change_patch_change (patch, key);
1998 MidiRegionView::previous_bank (PatchChange& patch)
2000 if (patch.patch()->program() > 0) {
2001 MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch());
2002 if (key.bank_number < 127) {
2004 change_patch_change (patch, key);
2010 MidiRegionView::maybe_remove_deleted_note_from_selection (NoteBase* cne)
2012 if (_selection.empty()) {
2016 _selection.erase (cne);
2020 MidiRegionView::delete_selection()
2022 if (_selection.empty()) {
2026 start_note_diff_command (_("delete selection"));
2028 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2029 if ((*i)->selected()) {
2030 _note_diff_command->remove((*i)->note());
2040 MidiRegionView::delete_note (boost::shared_ptr<NoteType> n)
2042 start_note_diff_command (_("delete note"));
2043 _note_diff_command->remove (n);
2046 trackview.editor().verbose_cursor()->hide ();
2050 MidiRegionView::clear_selection_except (NoteBase* ev, bool signal)
2052 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2054 Selection::iterator tmp = i;
2057 (*i)->set_selected (false);
2058 (*i)->hide_velocity ();
2059 _selection.erase (i);
2067 /* this does not change the status of this regionview w.r.t the editor
2072 SelectionCleared (this); /* EMIT SIGNAL */
2077 MidiRegionView::unique_select(NoteBase* ev)
2079 clear_selection_except (ev);
2081 /* don't bother with checking to see if we should remove this
2082 regionview from the editor selection, since we're about to add
2083 another note, and thus put/keep this regionview in the editor
2087 if (!ev->selected()) {
2088 add_to_selection (ev);
2093 MidiRegionView::select_all_notes ()
2097 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2098 add_to_selection (*i);
2103 MidiRegionView::select_range (framepos_t start, framepos_t end)
2107 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2108 framepos_t t = source_beats_to_absolute_frames((*i)->note()->time());
2109 if (t >= start && t <= end) {
2110 add_to_selection (*i);
2116 MidiRegionView::invert_selection ()
2118 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2119 if ((*i)->selected()) {
2120 remove_from_selection(*i);
2122 add_to_selection (*i);
2128 MidiRegionView::select_matching_notes (uint8_t notenum, uint16_t channel_mask, bool add, bool extend)
2130 bool have_selection = !_selection.empty();
2131 uint8_t low_note = 127;
2132 uint8_t high_note = 0;
2133 MidiModel::Notes& notes (_model->notes());
2134 _optimization_iterator = _events.begin();
2136 if (extend && !have_selection) {
2140 /* scan existing selection to get note range */
2142 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2143 if ((*i)->note()->note() < low_note) {
2144 low_note = (*i)->note()->note();
2146 if ((*i)->note()->note() > high_note) {
2147 high_note = (*i)->note()->note();
2154 if (!extend && (low_note == high_note) && (high_note == notenum)) {
2155 /* only note previously selected is the one we are
2156 * reselecting. treat this as cancelling the selection.
2163 low_note = min (low_note, notenum);
2164 high_note = max (high_note, notenum);
2167 _no_sound_notes = true;
2169 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
2171 boost::shared_ptr<NoteType> note (*n);
2173 bool select = false;
2175 if (((1 << note->channel()) & channel_mask) != 0) {
2177 if ((note->note() >= low_note && note->note() <= high_note)) {
2180 } else if (note->note() == notenum) {
2186 if ((cne = find_canvas_note (note)) != 0) {
2187 // extend is false because we've taken care of it,
2188 // since it extends by time range, not pitch.
2189 note_selected (cne, add, false);
2193 add = true; // we need to add all remaining matching notes, even if the passed in value was false (for "set")
2197 _no_sound_notes = false;
2201 MidiRegionView::toggle_matching_notes (uint8_t notenum, uint16_t channel_mask)
2203 MidiModel::Notes& notes (_model->notes());
2204 _optimization_iterator = _events.begin();
2206 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
2208 boost::shared_ptr<NoteType> note (*n);
2211 if (note->note() == notenum && (((0x0001 << note->channel()) & channel_mask) != 0)) {
2212 if ((cne = find_canvas_note (note)) != 0) {
2213 if (cne->selected()) {
2214 note_deselected (cne);
2216 note_selected (cne, true, false);
2224 MidiRegionView::note_selected (NoteBase* ev, bool add, bool extend)
2227 clear_selection_except (ev);
2228 if (!_selection.empty()) {
2229 PublicEditor& editor (trackview.editor());
2230 editor.get_selection().add (this);
2236 if (!ev->selected()) {
2237 add_to_selection (ev);
2241 /* find end of latest note selected, select all between that and the start of "ev" */
2243 Evoral::MusicalTime earliest = Evoral::MaxMusicalTime;
2244 Evoral::MusicalTime latest = 0;
2246 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2247 if ((*i)->note()->end_time() > latest) {
2248 latest = (*i)->note()->end_time();
2250 if ((*i)->note()->time() < earliest) {
2251 earliest = (*i)->note()->time();
2255 if (ev->note()->end_time() > latest) {
2256 latest = ev->note()->end_time();
2259 if (ev->note()->time() < earliest) {
2260 earliest = ev->note()->time();
2263 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2265 /* find notes entirely within OR spanning the earliest..latest range */
2267 if (((*i)->note()->time() >= earliest && (*i)->note()->end_time() <= latest) ||
2268 ((*i)->note()->time() <= earliest && (*i)->note()->end_time() >= latest)) {
2269 add_to_selection (*i);
2277 MidiRegionView::note_deselected(NoteBase* ev)
2279 remove_from_selection (ev);
2283 MidiRegionView::update_drag_selection(double x0, double x1, double y0, double y1, bool extend)
2285 // TODO: Make this faster by storing the last updated selection rect, and only
2286 // adjusting things that are in the area that appears/disappeared.
2287 // We probably need a tree to be able to find events in O(log(n)) time.
2289 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2290 if ((*i)->x0() < x1 && (*i)->x1() > x0 && (*i)->y0() < y1 && (*i)->y1() > y0) {
2291 // Rectangles intersect
2292 if (!(*i)->selected()) {
2293 add_to_selection (*i);
2295 } else if ((*i)->selected() && !extend) {
2296 // Rectangles do not intersect
2297 remove_from_selection (*i);
2303 MidiRegionView::update_vertical_drag_selection (double y1, double y2, bool extend)
2309 // TODO: Make this faster by storing the last updated selection rect, and only
2310 // adjusting things that are in the area that appears/disappeared.
2311 // We probably need a tree to be able to find events in O(log(n)) time.
2313 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2314 if (((*i)->y1() >= y1 && (*i)->y1() <= y2)) {
2315 // within y- (note-) range
2316 if (!(*i)->selected()) {
2317 add_to_selection (*i);
2319 } else if ((*i)->selected() && !extend) {
2320 remove_from_selection (*i);
2326 MidiRegionView::remove_from_selection (NoteBase* ev)
2328 Selection::iterator i = _selection.find (ev);
2330 if (i != _selection.end()) {
2331 _selection.erase (i);
2334 ev->set_selected (false);
2335 ev->hide_velocity ();
2337 if (_selection.empty()) {
2338 PublicEditor& editor (trackview.editor());
2339 editor.get_selection().remove (this);
2344 MidiRegionView::add_to_selection (NoteBase* ev)
2346 bool add_mrv_selection = false;
2348 if (_selection.empty()) {
2349 add_mrv_selection = true;
2352 if (_selection.insert (ev).second) {
2353 ev->set_selected (true);
2354 start_playing_midi_note ((ev)->note());
2357 if (add_mrv_selection) {
2358 PublicEditor& editor (trackview.editor());
2359 editor.get_selection().add (this);
2364 MidiRegionView::move_selection(double dx, double dy, double cumulative_dy)
2366 typedef vector<boost::shared_ptr<NoteType> > PossibleChord;
2367 PossibleChord to_play;
2368 Evoral::MusicalTime earliest = Evoral::MaxMusicalTime;
2370 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2371 if ((*i)->note()->time() < earliest) {
2372 earliest = (*i)->note()->time();
2376 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2377 if (Evoral::musical_time_equal ((*i)->note()->time(), earliest)) {
2378 to_play.push_back ((*i)->note());
2380 (*i)->move_event(dx, dy);
2383 if (dy && !_selection.empty() && !_no_sound_notes && Config->get_sound_midi_notes()) {
2385 if (to_play.size() > 1) {
2387 PossibleChord shifted;
2389 for (PossibleChord::iterator n = to_play.begin(); n != to_play.end(); ++n) {
2390 boost::shared_ptr<NoteType> moved_note (new NoteType (**n));
2391 moved_note->set_note (moved_note->note() + cumulative_dy);
2392 shifted.push_back (moved_note);
2395 start_playing_midi_chord (shifted);
2397 } else if (!to_play.empty()) {
2399 boost::shared_ptr<NoteType> moved_note (new NoteType (*to_play.front()));
2400 moved_note->set_note (moved_note->note() + cumulative_dy);
2401 start_playing_midi_note (moved_note);
2407 MidiRegionView::note_dropped(NoteBase *, frameoffset_t dt, int8_t dnote)
2409 uint8_t lowest_note_in_selection = 127;
2410 uint8_t highest_note_in_selection = 0;
2411 uint8_t highest_note_difference = 0;
2413 // find highest and lowest notes first
2415 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2416 uint8_t pitch = (*i)->note()->note();
2417 lowest_note_in_selection = std::min(lowest_note_in_selection, pitch);
2418 highest_note_in_selection = std::max(highest_note_in_selection, pitch);
2422 cerr << "dnote: " << (int) dnote << endl;
2423 cerr << "lowest note (streamview): " << int(midi_stream_view()->lowest_note())
2424 << " highest note (streamview): " << int(midi_stream_view()->highest_note()) << endl;
2425 cerr << "lowest note (selection): " << int(lowest_note_in_selection) << " highest note(selection): "
2426 << int(highest_note_in_selection) << endl;
2427 cerr << "selection size: " << _selection.size() << endl;
2428 cerr << "Highest note in selection: " << (int) highest_note_in_selection << endl;
2431 // Make sure the note pitch does not exceed the MIDI standard range
2432 if (highest_note_in_selection + dnote > 127) {
2433 highest_note_difference = highest_note_in_selection - 127;
2436 start_note_diff_command (_("move notes"));
2438 for (Selection::iterator i = _selection.begin(); i != _selection.end() ; ++i) {
2440 framepos_t new_frames = source_beats_to_absolute_frames ((*i)->note()->time()) + dt;
2441 Evoral::MusicalTime new_time = absolute_frames_to_source_beats (new_frames);
2447 note_diff_add_change (*i, MidiModel::NoteDiffCommand::StartTime, new_time);
2449 uint8_t original_pitch = (*i)->note()->note();
2450 uint8_t new_pitch = original_pitch + dnote - highest_note_difference;
2452 // keep notes in standard midi range
2453 clamp_to_0_127(new_pitch);
2455 lowest_note_in_selection = std::min(lowest_note_in_selection, new_pitch);
2456 highest_note_in_selection = std::max(highest_note_in_selection, new_pitch);
2458 note_diff_add_change (*i, MidiModel::NoteDiffCommand::NoteNumber, new_pitch);
2463 // care about notes being moved beyond the upper/lower bounds on the canvas
2464 if (lowest_note_in_selection < midi_stream_view()->lowest_note() ||
2465 highest_note_in_selection > midi_stream_view()->highest_note()) {
2466 midi_stream_view()->set_note_range(MidiStreamView::ContentsRange);
2470 /** @param x Pixel relative to the region position.
2471 * @return Snapped frame relative to the region position.
2474 MidiRegionView::snap_pixel_to_sample(double x)
2476 PublicEditor& editor (trackview.editor());
2477 return snap_frame_to_frame (editor.pixel_to_sample (x));
2480 /** @param x Pixel relative to the region position.
2481 * @return Snapped pixel relative to the region position.
2484 MidiRegionView::snap_to_pixel(double x)
2486 return (double) trackview.editor().sample_to_pixel(snap_pixel_to_sample(x));
2490 MidiRegionView::get_position_pixels()
2492 framepos_t region_frame = get_position();
2493 return trackview.editor().sample_to_pixel(region_frame);
2497 MidiRegionView::get_end_position_pixels()
2499 framepos_t frame = get_position() + get_duration ();
2500 return trackview.editor().sample_to_pixel(frame);
2504 MidiRegionView::source_beats_to_absolute_frames(double beats) const
2506 /* the time converter will return the frame corresponding to `beats'
2507 relative to the start of the source. The start of the source
2508 is an implied position given by region->position - region->start
2510 const framepos_t source_start = _region->position() - _region->start();
2511 return source_start + _source_relative_time_converter.to (beats);
2515 MidiRegionView::absolute_frames_to_source_beats(framepos_t frames) const
2517 /* the `frames' argument needs to be converted into a frame count
2518 relative to the start of the source before being passed in to the
2521 const framepos_t source_start = _region->position() - _region->start();
2522 return _source_relative_time_converter.from (frames - source_start);
2526 MidiRegionView::region_beats_to_region_frames(double beats) const
2528 return _region_relative_time_converter.to(beats);
2532 MidiRegionView::region_frames_to_region_beats(framepos_t frames) const
2534 return _region_relative_time_converter.from(frames);
2538 MidiRegionView::begin_resizing (bool /*at_front*/)
2540 _resize_data.clear();
2542 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2543 Note *note = dynamic_cast<Note*> (*i);
2545 // only insert CanvasNotes into the map
2547 NoteResizeData *resize_data = new NoteResizeData();
2548 resize_data->note = note;
2550 // create a new SimpleRect from the note which will be the resize preview
2551 ArdourCanvas::Rectangle *resize_rect = new ArdourCanvas::Rectangle (_note_group,
2552 ArdourCanvas::Rect (note->x0(), note->y0(), note->x0(), note->y1()));
2554 // calculate the colors: get the color settings
2555 uint32_t fill_color = UINT_RGBA_CHANGE_A(
2556 ARDOUR_UI::config()->get_canvasvar_MidiNoteSelected(),
2559 // make the resize preview notes more transparent and bright
2560 fill_color = UINT_INTERPOLATE(fill_color, 0xFFFFFF40, 0.5);
2562 // calculate color based on note velocity
2563 resize_rect->set_fill_color (UINT_INTERPOLATE(
2564 NoteBase::meter_style_fill_color(note->note()->velocity(), note->selected()),
2568 resize_rect->set_outline_color (NoteBase::calculate_outline (
2569 ARDOUR_UI::config()->get_canvasvar_MidiNoteSelected()));
2571 resize_data->resize_rect = resize_rect;
2572 _resize_data.push_back(resize_data);
2577 /** Update resizing notes while user drags.
2578 * @param primary `primary' note for the drag; ie the one that is used as the reference in non-relative mode.
2579 * @param at_front which end of the note (true == note on, false == note off)
2580 * @param delta_x change in mouse position since the start of the drag
2581 * @param relative true if relative resizing is taking place, false if absolute resizing. This only makes
2582 * a difference when multiple notes are being resized; in relative mode, each note's length is changed by the
2583 * amount of the drag. In non-relative mode, all selected notes are set to have the same start or end point
2584 * as the \a primary note.
2587 MidiRegionView::update_resizing (NoteBase* primary, bool at_front, double delta_x, bool relative)
2589 bool cursor_set = false;
2591 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2592 ArdourCanvas::Rectangle* resize_rect = (*i)->resize_rect;
2593 Note* canvas_note = (*i)->note;
2598 current_x = canvas_note->x0() + delta_x;
2600 current_x = primary->x0() + delta_x;
2604 current_x = canvas_note->x1() + delta_x;
2606 current_x = primary->x1() + delta_x;
2611 resize_rect->set_x0 (snap_to_pixel(current_x));
2612 resize_rect->set_x1 (canvas_note->x1());
2614 resize_rect->set_x1 (snap_to_pixel(current_x));
2615 resize_rect->set_x0 (canvas_note->x0());
2621 beats = snap_pixel_to_sample (current_x);
2622 beats = region_frames_to_region_beats (beats);
2627 if (beats < canvas_note->note()->end_time()) {
2628 len = canvas_note->note()->time() - beats;
2629 len += canvas_note->note()->length();
2634 if (beats >= canvas_note->note()->time()) {
2635 len = beats - canvas_note->note()->time();
2642 snprintf (buf, sizeof (buf), "%.3g beats", len);
2643 show_verbose_cursor (buf, 0, 0);
2652 /** Finish resizing notes when the user releases the mouse button.
2653 * Parameters the same as for \a update_resizing().
2656 MidiRegionView::commit_resizing (NoteBase* primary, bool at_front, double delta_x, bool relative)
2658 start_note_diff_command (_("resize notes"));
2660 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2661 Note* canvas_note = (*i)->note;
2662 ArdourCanvas::Rectangle* resize_rect = (*i)->resize_rect;
2664 /* Get the new x position for this resize, which is in pixels relative
2665 * to the region position.
2672 current_x = canvas_note->x0() + delta_x;
2674 current_x = primary->x0() + delta_x;
2678 current_x = canvas_note->x1() + delta_x;
2680 current_x = primary->x1() + delta_x;
2684 /* Convert that to a frame within the source */
2685 current_x = snap_pixel_to_sample (current_x) + _region->start ();
2687 /* and then to beats */
2688 current_x = region_frames_to_region_beats (current_x);
2690 if (at_front && current_x < canvas_note->note()->end_time()) {
2691 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::StartTime, current_x);
2693 double len = canvas_note->note()->time() - current_x;
2694 len += canvas_note->note()->length();
2697 /* XXX convert to beats */
2698 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
2703 double len = current_x - canvas_note->note()->time();
2706 /* XXX convert to beats */
2707 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
2715 _resize_data.clear();
2720 MidiRegionView::abort_resizing ()
2722 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2723 delete (*i)->resize_rect;
2727 _resize_data.clear ();
2731 MidiRegionView::change_note_velocity(NoteBase* event, int8_t velocity, bool relative)
2733 uint8_t new_velocity;
2736 new_velocity = event->note()->velocity() + velocity;
2737 clamp_to_0_127(new_velocity);
2739 new_velocity = velocity;
2742 event->set_selected (event->selected()); // change color
2744 note_diff_add_change (event, MidiModel::NoteDiffCommand::Velocity, new_velocity);
2748 MidiRegionView::change_note_note (NoteBase* event, int8_t note, bool relative)
2753 new_note = event->note()->note() + note;
2758 clamp_to_0_127 (new_note);
2759 note_diff_add_change (event, MidiModel::NoteDiffCommand::NoteNumber, new_note);
2763 MidiRegionView::trim_note (NoteBase* event, Evoral::MusicalTime front_delta, Evoral::MusicalTime end_delta)
2765 bool change_start = false;
2766 bool change_length = false;
2767 Evoral::MusicalTime new_start = 0;
2768 Evoral::MusicalTime new_length = 0;
2770 /* NOTE: the semantics of the two delta arguments are slightly subtle:
2772 front_delta: if positive - move the start of the note later in time (shortening it)
2773 if negative - move the start of the note earlier in time (lengthening it)
2775 end_delta: if positive - move the end of the note later in time (lengthening it)
2776 if negative - move the end of the note earlier in time (shortening it)
2780 if (front_delta < 0) {
2782 if (event->note()->time() < -front_delta) {
2785 new_start = event->note()->time() + front_delta; // moves earlier
2788 /* start moved toward zero, so move the end point out to where it used to be.
2789 Note that front_delta is negative, so this increases the length.
2792 new_length = event->note()->length() - front_delta;
2793 change_start = true;
2794 change_length = true;
2798 Evoral::MusicalTime new_pos = event->note()->time() + front_delta;
2800 if (new_pos < event->note()->end_time()) {
2801 new_start = event->note()->time() + front_delta;
2802 /* start moved toward the end, so move the end point back to where it used to be */
2803 new_length = event->note()->length() - front_delta;
2804 change_start = true;
2805 change_length = true;
2812 bool can_change = true;
2813 if (end_delta < 0) {
2814 if (event->note()->length() < -end_delta) {
2820 new_length = event->note()->length() + end_delta;
2821 change_length = true;
2826 note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_start);
2829 if (change_length) {
2830 note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, new_length);
2835 MidiRegionView::change_note_channel (NoteBase* event, int8_t chn, bool relative)
2837 uint8_t new_channel;
2841 if (event->note()->channel() < -chn) {
2844 new_channel = event->note()->channel() + chn;
2847 new_channel = event->note()->channel() + chn;
2850 new_channel = (uint8_t) chn;
2853 note_diff_add_change (event, MidiModel::NoteDiffCommand::Channel, new_channel);
2857 MidiRegionView::change_note_time (NoteBase* event, Evoral::MusicalTime delta, bool relative)
2859 Evoral::MusicalTime new_time;
2863 if (event->note()->time() < -delta) {
2866 new_time = event->note()->time() + delta;
2869 new_time = event->note()->time() + delta;
2875 note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_time);
2879 MidiRegionView::change_note_length (NoteBase* event, Evoral::MusicalTime t)
2881 note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, t);
2885 MidiRegionView::change_velocities (bool up, bool fine, bool allow_smush, bool all_together)
2890 if (_selection.empty()) {
2905 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2906 if ((*i)->note()->velocity() < -delta || (*i)->note()->velocity() + delta > 127) {
2912 start_note_diff_command (_("change velocities"));
2914 for (Selection::iterator i = _selection.begin(); i != _selection.end();) {
2915 Selection::iterator next = i;
2919 if (i == _selection.begin()) {
2920 change_note_velocity (*i, delta, true);
2921 value = (*i)->note()->velocity() + delta;
2923 change_note_velocity (*i, value, false);
2927 change_note_velocity (*i, delta, true);
2936 if (!_selection.empty()) {
2938 snprintf (buf, sizeof (buf), "Vel %d",
2939 (int) (*_selection.begin())->note()->velocity());
2940 show_verbose_cursor (buf, 10, 10);
2946 MidiRegionView::transpose (bool up, bool fine, bool allow_smush)
2948 if (_selection.empty()) {
2965 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2967 if ((int8_t) (*i)->note()->note() + delta <= 0) {
2971 if ((int8_t) (*i)->note()->note() + delta > 127) {
2978 start_note_diff_command (_("transpose"));
2980 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2981 Selection::iterator next = i;
2983 change_note_note (*i, delta, true);
2991 MidiRegionView::change_note_lengths (bool fine, bool shorter, Evoral::MusicalTime delta, bool start, bool end)
2997 /* grab the current grid distance */
2999 delta = trackview.editor().get_grid_type_as_beats (success, _region->position());
3001 /* XXX cannot get grid type as beats ... should always be possible ... FIX ME */
3002 error << string_compose (_("programming error: %1"), "Grid type not available as beats - TO BE FIXED") << endmsg;
3012 start_note_diff_command (_("change note lengths"));
3014 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3015 Selection::iterator next = i;
3018 /* note the negation of the delta for start */
3020 trim_note (*i, (start ? -delta : 0), (end ? delta : 0));
3029 MidiRegionView::nudge_notes (bool forward)
3031 if (_selection.empty()) {
3035 /* pick a note as the point along the timeline to get the nudge distance.
3036 its not necessarily the earliest note, so we may want to pull the notes out
3037 into a vector and sort before using the first one.
3040 framepos_t ref_point = source_beats_to_absolute_frames ((*(_selection.begin()))->note()->time());
3042 framecnt_t distance;
3044 if (trackview.editor().snap_mode() == Editing::SnapOff) {
3046 /* grid is off - use nudge distance */
3048 distance = trackview.editor().get_nudge_distance (ref_point, unused);
3054 framepos_t next_pos = ref_point;
3057 if (max_framepos - 1 < next_pos) {
3061 if (next_pos == 0) {
3067 trackview.editor().snap_to (next_pos, (forward ? 1 : -1), false);
3068 distance = ref_point - next_pos;
3071 if (distance == 0) {
3075 Evoral::MusicalTime delta = region_frames_to_region_beats (fabs ((double)distance));
3081 start_note_diff_command (_("nudge"));
3083 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3084 Selection::iterator next = i;
3086 change_note_time (*i, delta, true);
3094 MidiRegionView::change_channel(uint8_t channel)
3096 start_note_diff_command(_("change channel"));
3097 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3098 note_diff_add_change (*i, MidiModel::NoteDiffCommand::Channel, channel);
3106 MidiRegionView::note_entered(NoteBase* ev)
3108 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3110 pre_enter_cursor = editor->get_canvas_cursor ();
3112 if (_mouse_state == SelectTouchDragging) {
3113 note_selected (ev, true);
3116 show_verbose_cursor (ev->note ());
3120 MidiRegionView::note_left (NoteBase*)
3122 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3124 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3125 (*i)->hide_velocity ();
3128 editor->verbose_cursor()->hide ();
3130 if (pre_enter_cursor) {
3131 editor->set_canvas_cursor (pre_enter_cursor);
3132 pre_enter_cursor = 0;
3137 MidiRegionView::patch_entered (PatchChange* p)
3140 /* XXX should get patch name if we can */
3141 s << _("Bank ") << (p->patch()->bank() + MIDI_BP_ZERO) << '\n'
3142 << _("Program ") << ((int) p->patch()->program()) + MIDI_BP_ZERO << '\n'
3143 << _("Channel ") << ((int) p->patch()->channel() + 1);
3144 show_verbose_cursor (s.str(), 10, 20);
3145 p->item().grab_focus();
3149 MidiRegionView::patch_left (PatchChange *)
3151 trackview.editor().verbose_cursor()->hide ();
3152 /* focus will transfer back via the enter-notify event sent to this
3158 MidiRegionView::sysex_entered (SysEx* p)
3162 // need a way to extract text from p->_flag->_text
3164 // show_verbose_cursor (s.str(), 10, 20);
3165 p->item().grab_focus();
3169 MidiRegionView::sysex_left (SysEx *)
3171 trackview.editor().verbose_cursor()->hide ();
3172 /* focus will transfer back via the enter-notify event sent to this
3178 MidiRegionView::note_mouse_position (float x_fraction, float /*y_fraction*/, bool can_set_cursor)
3180 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3181 Editing::MouseMode mm = editor->current_mouse_mode();
3182 bool trimmable = (mm == MouseObject || mm == MouseTimeFX || mm == MouseDraw);
3184 if (trimmable && x_fraction > 0.0 && x_fraction < 0.2) {
3185 editor->set_canvas_cursor (editor->cursors()->left_side_trim);
3186 } else if (trimmable && x_fraction >= 0.8 && x_fraction < 1.0) {
3187 editor->set_canvas_cursor (editor->cursors()->right_side_trim);
3189 if (pre_enter_cursor && can_set_cursor) {
3190 editor->set_canvas_cursor (pre_enter_cursor);
3196 MidiRegionView::set_frame_color()
3200 TimeAxisViewItem::set_frame_color ();
3207 f = ARDOUR_UI::config()->get_canvasvar_SelectedFrameBase();
3208 } else if (high_enough_for_name) {
3209 f= ARDOUR_UI::config()->get_canvasvar_MidiFrameBase();
3214 if (!rect_visible) {
3215 f = UINT_RGBA_CHANGE_A (f, 0);
3218 frame->set_fill_color (f);
3222 MidiRegionView::midi_channel_mode_changed ()
3224 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3225 uint16_t mask = mtv->midi_track()->get_playback_channel_mask();
3226 ChannelMode mode = mtv->midi_track()->get_playback_channel_mode ();
3228 if (mode == ForceChannel) {
3229 mask = 0xFFFF; // Show all notes as active (below)
3232 // Update notes for selection
3233 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3234 (*i)->on_channel_selection_change (mask);
3237 _patch_changes.clear ();
3238 display_patch_changes ();
3242 MidiRegionView::instrument_settings_changed ()
3248 MidiRegionView::cut_copy_clear (Editing::CutCopyOp op)
3250 if (_selection.empty()) {
3254 PublicEditor& editor (trackview.editor());
3258 /* XXX what to do ? */
3262 editor.get_cut_buffer().add (selection_as_cut_buffer());
3270 start_note_diff_command();
3272 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3279 note_diff_remove_note (*i);
3289 MidiRegionView::selection_as_cut_buffer () const
3293 for (Selection::const_iterator i = _selection.begin(); i != _selection.end(); ++i) {
3294 NoteType* n = (*i)->note().get();
3295 notes.insert (boost::shared_ptr<NoteType> (new NoteType (*n)));
3298 MidiCutBuffer* cb = new MidiCutBuffer (trackview.session());
3304 /** This method handles undo */
3306 MidiRegionView::paste (framepos_t pos, float times, const MidiCutBuffer& mcb)
3312 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("MIDI paste @ %1 times %2\n", pos, times));
3314 trackview.session()->begin_reversible_command (_("paste"));
3316 start_note_diff_command (_("paste"));
3318 Evoral::MusicalTime beat_delta;
3319 Evoral::MusicalTime paste_pos_beats;
3320 Evoral::MusicalTime duration;
3321 Evoral::MusicalTime end_point = 0;
3323 duration = (*mcb.notes().rbegin())->end_time() - (*mcb.notes().begin())->time();
3324 paste_pos_beats = absolute_frames_to_source_beats (pos);
3325 beat_delta = (*mcb.notes().begin())->time() - paste_pos_beats;
3326 paste_pos_beats = 0;
3328 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",
3329 (*mcb.notes().begin())->time(),
3330 (*mcb.notes().rbegin())->end_time(),
3331 duration, pos, _region->position(),
3332 paste_pos_beats, beat_delta));
3336 for (int n = 0; n < (int) times; ++n) {
3338 for (Notes::const_iterator i = mcb.notes().begin(); i != mcb.notes().end(); ++i) {
3340 boost::shared_ptr<NoteType> copied_note (new NoteType (*((*i).get())));
3341 copied_note->set_time (paste_pos_beats + copied_note->time() - beat_delta);
3343 /* make all newly added notes selected */
3345 note_diff_add_note (copied_note, true);
3346 end_point = copied_note->end_time();
3349 paste_pos_beats += duration;
3352 /* if we pasted past the current end of the region, extend the region */
3354 framepos_t end_frame = source_beats_to_absolute_frames (end_point);
3355 framepos_t region_end = _region->position() + _region->length() - 1;
3357 if (end_frame > region_end) {
3359 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste extended region from %1 to %2\n", region_end, end_frame));
3361 _region->clear_changes ();
3362 _region->set_length (end_frame - _region->position());
3363 trackview.session()->add_command (new StatefulDiffCommand (_region));
3368 trackview.session()->commit_reversible_command ();
3371 struct EventNoteTimeEarlyFirstComparator {
3372 bool operator() (NoteBase* a, NoteBase* b) {
3373 return a->note()->time() < b->note()->time();
3378 MidiRegionView::time_sort_events ()
3380 if (!_sort_needed) {
3384 EventNoteTimeEarlyFirstComparator cmp;
3387 _sort_needed = false;
3391 MidiRegionView::goto_next_note (bool add_to_selection)
3393 bool use_next = false;
3395 if (_events.back()->selected()) {
3399 time_sort_events ();
3401 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3402 uint16_t const channel_mask = mtv->midi_track()->get_playback_channel_mask();
3404 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3405 if ((*i)->selected()) {
3408 } else if (use_next) {
3409 if (channel_mask & (1 << (*i)->note()->channel())) {
3410 if (!add_to_selection) {
3413 note_selected (*i, true, false);
3420 /* use the first one */
3422 if (!_events.empty() && (channel_mask & (1 << _events.front()->note()->channel ()))) {
3423 unique_select (_events.front());
3428 MidiRegionView::goto_previous_note (bool add_to_selection)
3430 bool use_next = false;
3432 if (_events.front()->selected()) {
3436 time_sort_events ();
3438 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3439 uint16_t const channel_mask = mtv->midi_track()->get_playback_channel_mask ();
3441 for (Events::reverse_iterator i = _events.rbegin(); i != _events.rend(); ++i) {
3442 if ((*i)->selected()) {
3445 } else if (use_next) {
3446 if (channel_mask & (1 << (*i)->note()->channel())) {
3447 if (!add_to_selection) {
3450 note_selected (*i, true, false);
3457 /* use the last one */
3459 if (!_events.empty() && (channel_mask & (1 << (*_events.rbegin())->note()->channel ()))) {
3460 unique_select (*(_events.rbegin()));
3465 MidiRegionView::selection_as_notelist (Notes& selected, bool allow_all_if_none_selected)
3467 bool had_selected = false;
3469 time_sort_events ();
3471 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3472 if ((*i)->selected()) {
3473 selected.insert ((*i)->note());
3474 had_selected = true;
3478 if (allow_all_if_none_selected && !had_selected) {
3479 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3480 selected.insert ((*i)->note());
3486 MidiRegionView::update_ghost_note (double x, double y)
3488 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3493 _note_group->canvas_to_item (x, y);
3495 PublicEditor& editor = trackview.editor ();
3497 framepos_t const unsnapped_frame = editor.pixel_to_sample (x);
3498 framecnt_t grid_frames;
3499 framepos_t const f = snap_frame_to_grid_underneath (unsnapped_frame, grid_frames);
3501 /* use region_frames... because we are converting a delta within the region
3505 double length = editor.get_grid_type_as_beats (success, unsnapped_frame);
3511 /* note that this sets the time of the ghost note in beats relative to
3512 the start of the source; that is how all note times are stored.
3514 _ghost_note->note()->set_time (absolute_frames_to_source_beats (f + _region->position ()));
3515 _ghost_note->note()->set_length (length);
3516 _ghost_note->note()->set_note (midi_stream_view()->y_to_note (y));
3517 _ghost_note->note()->set_channel (mtv->get_channel_for_add ());
3519 /* the ghost note does not appear in ghost regions, so pass false in here */
3520 update_note (_ghost_note, false);
3522 show_verbose_cursor (_ghost_note->note ());
3526 MidiRegionView::create_ghost_note (double x, double y)
3528 remove_ghost_note ();
3530 boost::shared_ptr<NoteType> g (new NoteType);
3531 _ghost_note = new Note (*this, _note_group, g);
3532 _ghost_note->set_ignore_events (true);
3533 _ghost_note->set_outline_color (0x000000aa);
3534 update_ghost_note (x, y);
3535 _ghost_note->show ();
3540 show_verbose_cursor (_ghost_note->note ());
3544 MidiRegionView::remove_ghost_note ()
3551 MidiRegionView::snap_changed ()
3557 create_ghost_note (_last_ghost_x, _last_ghost_y);
3561 MidiRegionView::drop_down_keys ()
3563 _mouse_state = None;
3567 MidiRegionView::maybe_select_by_position (GdkEventButton* ev, double /*x*/, double y)
3569 double note = midi_stream_view()->y_to_note(y);
3571 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3573 uint16_t chn_mask = mtv->midi_track()->get_playback_channel_mask();
3575 if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
3576 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchGreaterThanOrEqual, (uint8_t) floor (note), chn_mask);
3577 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
3578 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchLessThanOrEqual, (uint8_t) floor (note), chn_mask);
3583 bool add_mrv_selection = false;
3585 if (_selection.empty()) {
3586 add_mrv_selection = true;
3589 for (Events::iterator i = e.begin(); i != e.end(); ++i) {
3590 if (_selection.insert (*i).second) {
3591 (*i)->set_selected (true);
3595 if (add_mrv_selection) {
3596 PublicEditor& editor (trackview.editor());
3597 editor.get_selection().add (this);
3602 MidiRegionView::color_handler ()
3604 RegionView::color_handler ();
3606 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3607 (*i)->set_selected ((*i)->selected()); // will change color
3610 /* XXX probably more to do here */
3614 MidiRegionView::enable_display (bool yn)
3616 RegionView::enable_display (yn);
3623 MidiRegionView::show_step_edit_cursor (Evoral::MusicalTime pos)
3625 if (_step_edit_cursor == 0) {
3626 ArdourCanvas::Group* const group = (ArdourCanvas::Group*)get_canvas_group();
3628 _step_edit_cursor = new ArdourCanvas::Rectangle (group);
3629 _step_edit_cursor->set_y0 (0);
3630 _step_edit_cursor->set_y1 (midi_stream_view()->contents_height());
3631 _step_edit_cursor->set_fill_color (RGBA_TO_UINT (45,0,0,90));
3632 _step_edit_cursor->set_outline_color (RGBA_TO_UINT (85,0,0,90));
3635 move_step_edit_cursor (pos);
3636 _step_edit_cursor->show ();
3640 MidiRegionView::move_step_edit_cursor (Evoral::MusicalTime pos)
3642 _step_edit_cursor_position = pos;
3644 if (_step_edit_cursor) {
3645 double pixel = trackview.editor().sample_to_pixel (region_beats_to_region_frames (pos));
3646 _step_edit_cursor->set_x0 (pixel);
3647 set_step_edit_cursor_width (_step_edit_cursor_width);
3652 MidiRegionView::hide_step_edit_cursor ()
3654 if (_step_edit_cursor) {
3655 _step_edit_cursor->hide ();
3660 MidiRegionView::set_step_edit_cursor_width (Evoral::MusicalTime beats)
3662 _step_edit_cursor_width = beats;
3664 if (_step_edit_cursor) {
3665 _step_edit_cursor->set_x1 (_step_edit_cursor->x0() + trackview.editor().sample_to_pixel (region_beats_to_region_frames (beats)));
3669 /** Called when a diskstream on our track has received some data. Update the view, if applicable.
3670 * @param w Source that the data will end up in.
3673 MidiRegionView::data_recorded (boost::weak_ptr<MidiSource> w)
3675 if (!_active_notes) {
3676 /* we aren't actively being recorded to */
3680 boost::shared_ptr<MidiSource> src = w.lock ();
3681 if (!src || src != midi_region()->midi_source()) {
3682 /* recorded data was not destined for our source */
3686 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (&trackview);
3688 boost::shared_ptr<MidiBuffer> buf = mtv->midi_track()->get_gui_feed_buffer ();
3690 BeatsFramesConverter converter (trackview.session()->tempo_map(), mtv->midi_track()->get_capture_start_frame (0));
3692 framepos_t back = max_framepos;
3694 for (MidiBuffer::iterator i = buf->begin(); i != buf->end(); ++i) {
3695 Evoral::MIDIEvent<MidiBuffer::TimeType> const ev (*i, false);
3697 if (ev.is_channel_event()) {
3698 if (get_channel_mode() == FilterChannels) {
3699 if (((uint16_t(1) << ev.channel()) & get_selected_channels()) == 0) {
3705 /* ev.time() is in session frames, so (ev.time() - converter.origin_b()) is
3706 frames from the start of the source, and so time_beats is in terms of the
3710 Evoral::MusicalTime const time_beats = converter.from (ev.time () - converter.origin_b ());
3712 if (ev.type() == MIDI_CMD_NOTE_ON) {
3713 boost::shared_ptr<NoteType> note (
3714 new NoteType (ev.channel(), time_beats, 0, ev.note(), ev.velocity()));
3716 add_note (note, true);
3718 /* fix up our note range */
3719 if (ev.note() < _current_range_min) {
3720 midi_stream_view()->apply_note_range (ev.note(), _current_range_max, true);
3721 } else if (ev.note() > _current_range_max) {
3722 midi_stream_view()->apply_note_range (_current_range_min, ev.note(), true);
3725 } else if (ev.type() == MIDI_CMD_NOTE_OFF) {
3726 resolve_note (ev.note (), time_beats);
3732 midi_stream_view()->check_record_layers (region(), back);
3736 MidiRegionView::trim_front_starting ()
3738 /* Reparent the note group to the region view's parent, so that it doesn't change
3739 when the region view is trimmed.
3741 _temporary_note_group = new ArdourCanvas::Group (group->parent ());
3742 _temporary_note_group->move (group->position ());
3743 _note_group->reparent (_temporary_note_group);
3747 MidiRegionView::trim_front_ending ()
3749 _note_group->reparent (group);
3750 delete _temporary_note_group;
3751 _temporary_note_group = 0;
3753 if (_region->start() < 0) {
3754 /* Trim drag made start time -ve; fix this */
3755 midi_region()->fix_negative_start ();
3760 MidiRegionView::edit_patch_change (PatchChange* pc)
3762 PatchChangeDialog d (&_source_relative_time_converter, trackview.session(), *pc->patch (), instrument_info(), Gtk::Stock::APPLY, true);
3764 int response = d.run();
3767 case Gtk::RESPONSE_ACCEPT:
3769 case Gtk::RESPONSE_REJECT:
3770 delete_patch_change (pc);
3776 change_patch_change (pc->patch(), d.patch ());
3780 MidiRegionView::delete_sysex (SysEx* /*sysex*/)
3783 // sysyex object doesn't have a pointer to a sysex event
3784 // MidiModel::SysExDiffCommand* c = _model->new_sysex_diff_command (_("delete sysex"));
3785 // c->remove (sysex->sysex());
3786 // _model->apply_command (*trackview.session(), c);
3788 //_sys_exes.clear ();
3789 // display_sysexes();
3793 MidiRegionView::show_verbose_cursor (boost::shared_ptr<NoteType> n) const
3795 using namespace MIDI::Name;
3799 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3801 boost::shared_ptr<MasterDeviceNames> device_names(mtv->get_device_names());
3803 MIDI::Name::PatchPrimaryKey patch_key;
3804 get_patch_key_at(n->time(), n->channel(), patch_key);
3805 name = device_names->note_name(mtv->gui_property(X_("midnam-custom-device-mode")),
3807 patch_key.bank_number,
3808 patch_key.program_number,
3814 snprintf (buf, sizeof (buf), "%d %s\nCh %d Vel %d",
3816 name.empty() ? Evoral::midi_note_name (n->note()).c_str() : name.c_str(),
3817 (int) n->channel() + 1,
3818 (int) n->velocity());
3820 show_verbose_cursor(buf, 10, 20);
3824 MidiRegionView::show_verbose_cursor (string const & text, double xoffset, double yoffset) const
3828 trackview.editor().verbose_cursor()->set_text (text);
3829 trackview.editor().get_pointer_position (wx, wy);
3834 /* Flip the cursor above the mouse pointer if it would overlap the bottom of the canvas */
3836 boost::optional<ArdourCanvas::Rect> bbo = trackview.editor().verbose_cursor()->item().bounding_box();
3840 ArdourCanvas::Rect bb = bbo.get();
3842 if ((wy + bb.y1 - bb.y0) > trackview.editor().visible_canvas_height()) {
3843 wy -= (bb.y1 - bb.y0) + 2 * yoffset;
3846 trackview.editor().verbose_cursor()->set_position (wx, wy);
3847 trackview.editor().verbose_cursor()->show ();
3850 /** @param p A session framepos.
3851 * @param grid_frames Filled in with the number of frames that a grid interval is at p.
3852 * @return p snapped to the grid subdivision underneath it.
3855 MidiRegionView::snap_frame_to_grid_underneath (framepos_t p, framecnt_t& grid_frames) const
3857 PublicEditor& editor = trackview.editor ();
3860 Evoral::MusicalTime grid_beats = editor.get_grid_type_as_beats (success, p);
3866 grid_frames = region_beats_to_region_frames (grid_beats);
3868 /* Hack so that we always snap to the note that we are over, instead of snapping
3869 to the next one if we're more than halfway through the one we're over.
3871 if (editor.snap_mode() == SnapNormal && p >= grid_frames / 2) {
3872 p -= grid_frames / 2;
3875 return snap_frame_to_frame (p);
3878 /** Called when the selection has been cleared in any MidiRegionView.
3879 * @param rv MidiRegionView that the selection was cleared in.
3882 MidiRegionView::selection_cleared (MidiRegionView* rv)
3888 /* Clear our selection in sympathy; but don't signal the fact */
3889 clear_selection (false);
3893 MidiRegionView::note_button_release ()
3895 delete _note_player;
3900 MidiRegionView::get_channel_mode () const
3902 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (&trackview);
3903 return rtav->midi_track()->get_playback_channel_mode();
3907 MidiRegionView::get_selected_channels () const
3909 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (&trackview);
3910 return rtav->midi_track()->get_playback_channel_mask();