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-draw modes elsewhere
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::remove_ghost_note ()
400 MidiRegionView::enter_notify (GdkEventCrossing* ev)
402 trackview.editor().MouseModeChanged.connect (
403 _mouse_mode_connection, invalidator (*this), boost::bind (&MidiRegionView::mouse_mode_changed, this), gui_context ()
406 if (trackview.editor().current_mouse_mode() == MouseDraw && _mouse_state != AddDragging) {
407 create_ghost_note (ev->x, ev->y);
410 if (!trackview.editor().internal_editing()) {
411 Keyboard::magic_widget_drop_focus();
413 Keyboard::magic_widget_grab_focus();
417 // if current operation is non-operational in a midi region, change the cursor to so indicate
418 if (trackview.editor().current_mouse_mode() == MouseGain) {
419 Editor* editor = dynamic_cast<Editor *> (&trackview.editor());
420 pre_enter_cursor = editor->get_canvas_cursor();
421 editor->set_canvas_cursor(editor->cursors()->timebar);
428 MidiRegionView::leave_notify (GdkEventCrossing*)
430 _mouse_mode_connection.disconnect ();
432 trackview.editor().verbose_cursor()->hide ();
433 remove_ghost_note ();
435 if (pre_enter_cursor) {
436 Editor* editor = dynamic_cast<Editor *> (&trackview.editor());
437 editor->set_canvas_cursor(pre_enter_cursor);
444 MidiRegionView::mouse_mode_changed ()
446 if (trackview.editor().current_mouse_mode() == MouseDraw && trackview.editor().internal_editing()) {
447 create_ghost_note (_last_event_x, _last_event_y);
449 remove_ghost_note ();
450 trackview.editor().verbose_cursor()->hide ();
453 if (!trackview.editor().internal_editing()) {
454 Keyboard::magic_widget_drop_focus();
456 Keyboard::magic_widget_grab_focus();
462 MidiRegionView::button_press (GdkEventButton* ev)
464 if (ev->button != 1) {
468 Editor* editor = dynamic_cast<Editor *> (&trackview.editor());
469 MouseMode m = editor->current_mouse_mode();
471 if (m == MouseObject && Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())) {
472 pre_press_cursor = editor->get_canvas_cursor ();
473 editor->set_canvas_cursor (editor->cursors()->midi_pencil);
476 if (_mouse_state != SelectTouchDragging) {
478 _pressed_button = ev->button;
479 _mouse_state = Pressed;
484 _pressed_button = ev->button;
490 MidiRegionView::button_release (GdkEventButton* ev)
492 double event_x, event_y;
494 if (ev->button != 1) {
501 group->canvas_to_item (event_x, event_y);
504 PublicEditor& editor = trackview.editor ();
506 if (pre_press_cursor) {
507 dynamic_cast<Editor*>(&editor)->set_canvas_cursor (pre_press_cursor, false);
508 pre_press_cursor = 0;
511 switch (_mouse_state) {
512 case Pressed: // Clicked
514 switch (editor.current_mouse_mode()) {
516 /* no motion occured - simple click */
525 if (Keyboard::is_insert_note_event(ev)) {
527 double event_x, event_y;
531 group->canvas_to_item (event_x, event_y);
534 Evoral::MusicalTime beats = editor.get_grid_type_as_beats (success, editor.pixel_to_sample (event_x));
540 /* Shorten the length by 1 tick so that we can add a new note at the next
541 grid snap without it overlapping this one.
543 beats -= 1.0 / Timecode::BBT_Time::ticks_per_beat;
545 create_note_at (editor.pixel_to_sample (event_x), event_y, beats, true);
553 Evoral::MusicalTime beats = editor.get_grid_type_as_beats (success, editor.pixel_to_sample (event_x));
559 /* Shorten the length by 1 tick so that we can add a new note at the next
560 grid snap without it overlapping this one.
562 beats -= 1.0 / Timecode::BBT_Time::ticks_per_beat;
564 create_note_at (editor.pixel_to_sample (event_x), event_y, beats, true);
575 case SelectRectDragging:
577 editor.drags()->end_grab ((GdkEvent *) ev);
579 create_ghost_note (ev->x, ev->y);
591 MidiRegionView::motion (GdkEventMotion* ev)
593 PublicEditor& editor = trackview.editor ();
595 if (!_ghost_note && editor.current_mouse_mode() == MouseObject &&
596 Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier()) &&
597 _mouse_state != AddDragging) {
599 create_ghost_note (ev->x, ev->y);
601 } else if (_ghost_note && editor.current_mouse_mode() == MouseObject &&
602 Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())) {
604 update_ghost_note (ev->x, ev->y);
606 } else if (_ghost_note && editor.current_mouse_mode() == MouseObject) {
608 remove_ghost_note ();
609 editor.verbose_cursor()->hide ();
611 } else if (_ghost_note && editor.current_mouse_mode() == MouseDraw) {
613 update_ghost_note (ev->x, ev->y);
616 /* any motion immediately hides velocity text that may have been visible */
618 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
619 (*i)->hide_velocity ();
622 switch (_mouse_state) {
625 if (_pressed_button == 1) {
627 MouseMode m = editor.current_mouse_mode();
629 if (m == MouseDraw || (m == MouseObject && Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier()))) {
630 editor.drags()->set (new NoteCreateDrag (dynamic_cast<Editor *> (&editor), group, this), (GdkEvent *) ev);
631 _mouse_state = AddDragging;
632 remove_ghost_note ();
633 editor.verbose_cursor()->hide ();
635 } else if (m == MouseObject) {
636 editor.drags()->set (new MidiRubberbandSelectDrag (dynamic_cast<Editor *> (&editor), this), (GdkEvent *) ev);
638 _mouse_state = SelectRectDragging;
640 } else if (m == MouseRange) {
641 editor.drags()->set (new MidiVerticalSelectDrag (dynamic_cast<Editor *> (&editor), this), (GdkEvent *) ev);
642 _mouse_state = SelectVerticalDragging;
649 case SelectRectDragging:
650 case SelectVerticalDragging:
652 editor.drags()->motion_handler ((GdkEvent *) ev, false);
655 case SelectTouchDragging:
667 MidiRegionView::scroll (GdkEventScroll* ev)
669 if (_selection.empty()) {
673 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
674 /* XXX: bit of a hack; allow PrimaryModifier scroll through so that
675 it still works for zoom.
680 trackview.editor().verbose_cursor()->hide ();
682 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
683 bool together = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
685 if (ev->direction == GDK_SCROLL_UP) {
686 change_velocities (true, fine, false, together);
687 } else if (ev->direction == GDK_SCROLL_DOWN) {
688 change_velocities (false, fine, false, together);
690 /* left, right: we don't use them */
698 MidiRegionView::key_press (GdkEventKey* ev)
700 /* since GTK bindings are generally activated on press, and since
701 detectable auto-repeat is the name of the game and only sends
702 repeated presses, carry out key actions at key press, not release.
705 bool unmodified = Keyboard::no_modifier_keys_pressed (ev);
707 if (unmodified && (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R)) {
708 _mouse_state = SelectTouchDragging;
711 } else if (ev->keyval == GDK_Escape && unmodified) {
715 } else if (unmodified && (ev->keyval == GDK_comma || ev->keyval == GDK_period)) {
717 bool start = (ev->keyval == GDK_comma);
718 bool end = (ev->keyval == GDK_period);
719 bool shorter = Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier);
720 bool fine = Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
722 change_note_lengths (fine, shorter, 0.0, start, end);
726 } else if ((ev->keyval == GDK_BackSpace || ev->keyval == GDK_Delete) && unmodified) {
728 if (_selection.empty()) {
735 } else if (ev->keyval == GDK_Tab) {
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));
744 } else if (ev->keyval == GDK_ISO_Left_Tab) {
746 /* Shift-TAB generates ISO Left Tab, for some reason */
748 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
749 goto_previous_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
751 goto_next_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
757 } else if (ev->keyval == GDK_Up) {
759 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
760 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
761 bool together = Keyboard::modifier_state_contains (ev->state, Keyboard::Level4Modifier);
763 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
764 change_velocities (true, fine, allow_smush, together);
766 transpose (true, fine, allow_smush);
770 } else if (ev->keyval == GDK_Down) {
772 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
773 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
774 bool together = Keyboard::modifier_state_contains (ev->state, Keyboard::Level4Modifier);
776 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
777 change_velocities (false, fine, allow_smush, together);
779 transpose (false, fine, allow_smush);
783 } else if (ev->keyval == GDK_Left && unmodified) {
788 } else if (ev->keyval == GDK_Right && unmodified) {
793 } else if (ev->keyval == GDK_c && unmodified) {
797 } else if (ev->keyval == GDK_v && unmodified) {
806 MidiRegionView::key_release (GdkEventKey* ev)
808 if ((_mouse_state == SelectTouchDragging) && (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R)) {
816 MidiRegionView::channel_edit ()
818 if (_selection.empty()) {
822 /* pick a note somewhat at random (since Selection is a set<>) to
823 * provide the "current" channel for the dialog.
826 uint8_t current_channel = (*_selection.begin())->note()->channel ();
827 MidiChannelDialog channel_dialog (current_channel);
828 int ret = channel_dialog.run ();
831 case Gtk::RESPONSE_OK:
837 uint8_t new_channel = channel_dialog.active_channel ();
839 start_note_diff_command (_("channel edit"));
841 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
842 Selection::iterator next = i;
844 change_note_channel (*i, new_channel);
852 MidiRegionView::velocity_edit ()
854 if (_selection.empty()) {
858 /* pick a note somewhat at random (since Selection is a set<>) to
859 * provide the "current" velocity for the dialog.
862 uint8_t current_velocity = (*_selection.begin())->note()->velocity ();
863 MidiVelocityDialog velocity_dialog (current_velocity);
864 int ret = velocity_dialog.run ();
867 case Gtk::RESPONSE_OK:
873 uint8_t new_velocity = velocity_dialog.velocity ();
875 start_note_diff_command (_("velocity edit"));
877 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
878 Selection::iterator next = i;
880 change_note_velocity (*i, new_velocity, false);
888 MidiRegionView::show_list_editor ()
891 _list_editor = new MidiListEditor (trackview.session(), midi_region(), midi_view()->midi_track());
893 _list_editor->present ();
896 /** Add a note to the model, and the view, at a canvas (click) coordinate.
897 * \param t time in frames relative to the position of the region
898 * \param y vertical position in pixels
899 * \param length duration of the note in beats
900 * \param snap_t true to snap t to the grid, otherwise false.
903 MidiRegionView::create_note_at (framepos_t t, double y, double length, bool snap_t)
905 if (length < 2 * DBL_EPSILON) {
909 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
910 MidiStreamView* const view = mtv->midi_view();
912 const double note = view->y_to_note(y);
914 // Start of note in frames relative to region start
916 framecnt_t grid_frames;
917 t = snap_frame_to_grid_underneath (t, grid_frames);
920 const boost::shared_ptr<NoteType> new_note (
921 new NoteType (mtv->get_channel_for_add (),
922 region_frames_to_region_beats(t + _region->start()),
924 (uint8_t)note, 0x40));
926 if (_model->contains (new_note)) {
930 view->update_note_range(new_note->note());
932 MidiModel::NoteDiffCommand* cmd = _model->new_note_diff_command(_("add note"));
934 _model->apply_command(*trackview.session(), cmd);
936 play_midi_note (new_note);
940 MidiRegionView::clear_events (bool with_selection_signal)
942 clear_selection (with_selection_signal);
945 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
946 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
951 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
956 _patch_changes.clear();
958 _optimization_iterator = _events.end();
962 MidiRegionView::display_model(boost::shared_ptr<MidiModel> model)
966 content_connection.disconnect ();
967 _model->ContentsChanged.connect (content_connection, invalidator (*this), boost::bind (&MidiRegionView::redisplay_model, this), gui_context());
971 if (_enable_display) {
977 MidiRegionView::start_note_diff_command (string name)
979 if (!_note_diff_command) {
980 _note_diff_command = _model->new_note_diff_command (name);
985 MidiRegionView::note_diff_add_note (const boost::shared_ptr<NoteType> note, bool selected, bool show_velocity)
987 if (_note_diff_command) {
988 _note_diff_command->add (note);
991 _marked_for_selection.insert(note);
994 _marked_for_velocity.insert(note);
999 MidiRegionView::note_diff_remove_note (NoteBase* ev)
1001 if (_note_diff_command && ev->note()) {
1002 _note_diff_command->remove(ev->note());
1007 MidiRegionView::note_diff_add_change (NoteBase* ev,
1008 MidiModel::NoteDiffCommand::Property property,
1011 if (_note_diff_command) {
1012 _note_diff_command->change (ev->note(), property, val);
1017 MidiRegionView::note_diff_add_change (NoteBase* ev,
1018 MidiModel::NoteDiffCommand::Property property,
1019 Evoral::MusicalTime val)
1021 if (_note_diff_command) {
1022 _note_diff_command->change (ev->note(), property, val);
1027 MidiRegionView::apply_diff (bool as_subcommand)
1031 if (!_note_diff_command) {
1035 if ((add_or_remove = _note_diff_command->adds_or_removes())) {
1036 // Mark all selected notes for selection when model reloads
1037 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1038 _marked_for_selection.insert((*i)->note());
1042 if (as_subcommand) {
1043 _model->apply_command_as_subcommand (*trackview.session(), _note_diff_command);
1045 _model->apply_command (*trackview.session(), _note_diff_command);
1048 _note_diff_command = 0;
1049 midi_view()->midi_track()->playlist_modified();
1051 if (add_or_remove) {
1052 _marked_for_selection.clear();
1055 _marked_for_velocity.clear();
1059 MidiRegionView::abort_command()
1061 delete _note_diff_command;
1062 _note_diff_command = 0;
1067 MidiRegionView::find_canvas_note (boost::shared_ptr<NoteType> note)
1069 if (_optimization_iterator != _events.end()) {
1070 ++_optimization_iterator;
1073 if (_optimization_iterator != _events.end() && (*_optimization_iterator)->note() == note) {
1074 return *_optimization_iterator;
1077 for (_optimization_iterator = _events.begin(); _optimization_iterator != _events.end(); ++_optimization_iterator) {
1078 if ((*_optimization_iterator)->note() == note) {
1079 return *_optimization_iterator;
1087 MidiRegionView::get_events (Events& e, Evoral::Sequence<Evoral::MusicalTime>::NoteOperator op, uint8_t val, int chan_mask)
1089 MidiModel::Notes notes;
1090 _model->get_notes (notes, op, val, chan_mask);
1092 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1093 NoteBase* cne = find_canvas_note (*n);
1101 MidiRegionView::redisplay_model()
1103 // Don't redisplay the model if we're currently recording and displaying that
1104 if (_active_notes) {
1112 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1113 (*i)->invalidate ();
1116 MidiModel::ReadLock lock(_model->read_lock());
1118 MidiModel::Notes& notes (_model->notes());
1119 _optimization_iterator = _events.begin();
1121 bool empty_when_starting = !_events.empty();
1123 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1125 boost::shared_ptr<NoteType> note (*n);
1129 if (note_in_region_range (note, visible)) {
1131 if (empty_when_starting && (cne = find_canvas_note (note)) != 0) {
1138 if ((cn = dynamic_cast<Note*>(cne)) != 0) {
1140 } else if ((ch = dynamic_cast<Hit*>(cne)) != 0) {
1152 add_note (note, visible);
1157 if (empty_when_starting && (cne = find_canvas_note (note)) != 0) {
1165 /* remove note items that are no longer valid */
1167 if (empty_when_starting) {
1168 for (Events::iterator i = _events.begin(); i != _events.end(); ) {
1169 if (!(*i)->valid ()) {
1171 for (vector<GhostRegion*>::iterator j = ghosts.begin(); j != ghosts.end(); ++j) {
1172 MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*j);
1174 gr->remove_note (*i);
1179 i = _events.erase (i);
1187 _patch_changes.clear();
1191 display_patch_changes ();
1193 _marked_for_selection.clear ();
1194 _marked_for_velocity.clear ();
1196 /* we may have caused _events to contain things out of order (e.g. if a note
1197 moved earlier or later). we don't generally need them in time order, but
1198 make a note that a sort is required for those cases that require it.
1201 _sort_needed = true;
1205 MidiRegionView::display_patch_changes ()
1207 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1208 uint16_t chn_mask = mtv->midi_track()->get_playback_channel_mask();
1210 for (uint8_t i = 0; i < 16; ++i) {
1211 display_patch_changes_on_channel (i, chn_mask & (1 << i));
1215 /** @param active_channel true to display patch changes fully, false to display
1216 * them `greyed-out' (as on an inactive channel)
1219 MidiRegionView::display_patch_changes_on_channel (uint8_t channel, bool active_channel)
1221 for (MidiModel::PatchChanges::const_iterator i = _model->patch_changes().begin(); i != _model->patch_changes().end(); ++i) {
1223 if ((*i)->channel() != channel) {
1227 const string patch_name = instrument_info().get_patch_name ((*i)->bank(), (*i)->program(), channel);
1228 add_canvas_patch_change (*i, patch_name, active_channel);
1233 MidiRegionView::display_sysexes()
1235 bool have_periodic_system_messages = false;
1236 bool display_periodic_messages = true;
1238 if (!Config->get_never_display_periodic_midi()) {
1240 for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
1241 const boost::shared_ptr<const Evoral::MIDIEvent<Evoral::MusicalTime> > mev =
1242 boost::static_pointer_cast<const Evoral::MIDIEvent<Evoral::MusicalTime> > (*i);
1245 if (mev->is_spp() || mev->is_mtc_quarter() || mev->is_mtc_full()) {
1246 have_periodic_system_messages = true;
1252 if (have_periodic_system_messages) {
1253 double zoom = trackview.editor().get_current_zoom (); // frames per pixel
1255 /* get an approximate value for the number of samples per video frame */
1257 double video_frame = trackview.session()->frame_rate() * (1.0/30);
1259 /* if we are zoomed out beyond than the cutoff (i.e. more
1260 * frames per pixel than frames per 4 video frames), don't
1261 * show periodic sysex messages.
1264 if (zoom > (video_frame*4)) {
1265 display_periodic_messages = false;
1269 display_periodic_messages = false;
1272 for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
1274 const boost::shared_ptr<const Evoral::MIDIEvent<Evoral::MusicalTime> > mev =
1275 boost::static_pointer_cast<const Evoral::MIDIEvent<Evoral::MusicalTime> > (*i);
1277 Evoral::MusicalTime time = (*i)->time();
1280 if (mev->is_spp() || mev->is_mtc_quarter() || mev->is_mtc_full()) {
1281 if (!display_periodic_messages) {
1289 for (uint32_t b = 0; b < (*i)->size(); ++b) {
1290 str << int((*i)->buffer()[b]);
1291 if (b != (*i)->size() -1) {
1295 string text = str.str();
1297 const double x = trackview.editor().sample_to_pixel(source_beats_to_region_frames(time));
1299 double height = midi_stream_view()->contents_height();
1301 // CAIROCANVAS: no longer passing *i (the sysex event) to the
1302 // SysEx canvas object!!!
1304 boost::shared_ptr<SysEx> sysex = boost::shared_ptr<SysEx>(
1305 new SysEx (*this, _note_group, text, height, x, 1.0));
1307 // Show unless message is beyond the region bounds
1308 if (time - _region->start() >= _region->length() || time < _region->start()) {
1314 _sys_exes.push_back(sysex);
1318 MidiRegionView::~MidiRegionView ()
1320 in_destructor = true;
1322 trackview.editor().verbose_cursor()->hide ();
1324 note_delete_connection.disconnect ();
1326 delete _list_editor;
1328 RegionViewGoingAway (this); /* EMIT_SIGNAL */
1330 if (_active_notes) {
1334 _selection_cleared_connection.disconnect ();
1337 clear_events (false);
1340 delete _note_diff_command;
1341 delete _step_edit_cursor;
1342 delete _temporary_note_group;
1346 MidiRegionView::region_resized (const PropertyChange& what_changed)
1348 RegionView::region_resized(what_changed);
1350 if (what_changed.contains (ARDOUR::Properties::position)) {
1351 set_duration(_region->length(), 0);
1352 if (_enable_display) {
1359 MidiRegionView::reset_width_dependent_items (double pixel_width)
1361 RegionView::reset_width_dependent_items(pixel_width);
1363 if (_enable_display) {
1367 for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
1368 if ((*x)->canvas_item()->width() >= _pixel_width) {
1375 move_step_edit_cursor (_step_edit_cursor_position);
1376 set_step_edit_cursor_width (_step_edit_cursor_width);
1380 MidiRegionView::set_height (double height)
1382 static const double FUDGE = 2.0;
1383 const double old_height = _height;
1384 RegionView::set_height(height);
1385 _height = height - FUDGE;
1387 apply_note_range(midi_stream_view()->lowest_note(),
1388 midi_stream_view()->highest_note(),
1389 height != old_height + FUDGE);
1392 name_text->raise_to_top();
1395 for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
1396 (*x)->set_height (midi_stream_view()->contents_height());
1399 if (_step_edit_cursor) {
1400 _step_edit_cursor->set_y1 (midi_stream_view()->contents_height());
1405 /** Apply the current note range from the stream view
1406 * by repositioning/hiding notes as necessary
1409 MidiRegionView::apply_note_range (uint8_t min, uint8_t max, bool force)
1411 if (!_enable_display) {
1415 if (!force && _current_range_min == min && _current_range_max == max) {
1419 _current_range_min = min;
1420 _current_range_max = max;
1422 for (Events::const_iterator i = _events.begin(); i != _events.end(); ++i) {
1423 NoteBase* event = *i;
1424 boost::shared_ptr<NoteType> note (event->note());
1426 if (note->note() < _current_range_min ||
1427 note->note() > _current_range_max) {
1433 if (Note* cnote = dynamic_cast<Note*>(event)) {
1435 const double y0 = midi_stream_view()->note_to_y(note->note());
1436 const double y1 = y0 + floor(midi_stream_view()->note_height());
1441 } else if (Hit* chit = dynamic_cast<Hit*>(event)) {
1443 const double diamond_size = update_hit (chit);
1445 chit->set_height (diamond_size);
1451 MidiRegionView::add_ghost (TimeAxisView& tv)
1455 double unit_position = _region->position () / samples_per_pixel;
1456 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(&tv);
1457 MidiGhostRegion* ghost;
1459 if (mtv && mtv->midi_view()) {
1460 /* if ghost is inserted into midi track, use a dedicated midi ghost canvas group
1461 to allow having midi notes on top of note lines and waveforms.
1463 ghost = new MidiGhostRegion (*mtv->midi_view(), trackview, unit_position);
1465 ghost = new MidiGhostRegion (tv, trackview, unit_position);
1468 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1469 if ((note = dynamic_cast<Note*>(*i)) != 0) {
1470 ghost->add_note(note);
1474 ghost->set_height ();
1475 ghost->set_duration (_region->length() / samples_per_pixel);
1476 ghosts.push_back (ghost);
1478 GhostRegion::CatchDeletion.connect (*this, invalidator (*this), boost::bind (&RegionView::remove_ghost, this, _1), gui_context());
1484 /** Begin tracking note state for successive calls to add_event
1487 MidiRegionView::begin_write()
1489 if (_active_notes) {
1490 delete[] _active_notes;
1492 _active_notes = new Note*[128];
1493 for (unsigned i = 0; i < 128; ++i) {
1494 _active_notes[i] = 0;
1499 /** Destroy note state for add_event
1502 MidiRegionView::end_write()
1504 delete[] _active_notes;
1506 _marked_for_selection.clear();
1507 _marked_for_velocity.clear();
1511 /** Resolve an active MIDI note (while recording).
1514 MidiRegionView::resolve_note(uint8_t note, double end_time)
1516 if (midi_view()->note_mode() != Sustained) {
1520 if (_active_notes && _active_notes[note]) {
1522 /* XXX is end_time really region-centric? I think so, because
1523 this is a new region that we're recording, so source zero is
1524 the same as region zero
1526 const framepos_t end_time_frames = region_beats_to_region_frames(end_time);
1528 _active_notes[note]->set_x1 (trackview.editor().sample_to_pixel(end_time_frames));
1529 _active_notes[note]->set_outline_what (0xf);
1530 _active_notes[note] = 0;
1535 /** Extend active notes to rightmost edge of region (if length is changed)
1538 MidiRegionView::extend_active_notes()
1540 if (!_active_notes) {
1544 for (unsigned i=0; i < 128; ++i) {
1545 if (_active_notes[i]) {
1546 _active_notes[i]->set_x1 (trackview.editor().sample_to_pixel(_region->length()));
1553 MidiRegionView::play_midi_note(boost::shared_ptr<NoteType> note)
1555 if (_no_sound_notes || !Config->get_sound_midi_notes()) {
1559 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1561 if (!route_ui || !route_ui->midi_track()) {
1565 NotePlayer* np = new NotePlayer (route_ui->midi_track ());
1569 /* NotePlayer deletes itself */
1573 MidiRegionView::start_playing_midi_note(boost::shared_ptr<NoteType> note)
1575 if (_no_sound_notes || !Config->get_sound_midi_notes()) {
1579 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1581 if (!route_ui || !route_ui->midi_track()) {
1585 delete _note_player;
1586 _note_player = new NotePlayer (route_ui->midi_track ());
1587 _note_player->add (note);
1588 _note_player->on ();
1592 MidiRegionView::start_playing_midi_chord (vector<boost::shared_ptr<NoteType> > notes)
1594 if (_no_sound_notes || !Config->get_sound_midi_notes()) {
1598 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1600 if (!route_ui || !route_ui->midi_track()) {
1604 delete _note_player;
1605 _note_player = new NotePlayer (route_ui->midi_track());
1607 for (vector<boost::shared_ptr<NoteType> >::iterator n = notes.begin(); n != notes.end(); ++n) {
1608 _note_player->add (*n);
1611 _note_player->on ();
1616 MidiRegionView::note_in_region_range (const boost::shared_ptr<NoteType> note, bool& visible) const
1618 const framepos_t note_start_frames = source_beats_to_region_frames (note->time());
1619 bool outside = (note_start_frames < 0) || (note_start_frames > _region->last_frame());
1621 visible = (note->note() >= midi_stream_view()->lowest_note()) &&
1622 (note->note() <= midi_stream_view()->highest_note());
1627 /** Update a canvas note's size from its model note.
1628 * @param ev Canvas note to update.
1629 * @param update_ghost_regions true to update the note in any ghost regions that we have, otherwise false.
1632 MidiRegionView::update_note (Note* ev, bool update_ghost_regions)
1634 boost::shared_ptr<NoteType> note = ev->note();
1635 const double x = trackview.editor().sample_to_pixel (source_beats_to_region_frames (note->time()));
1636 const double y0 = midi_stream_view()->note_to_y(note->note());
1641 /* trim note display to not overlap the end of its region */
1643 if (note->length() > 0) {
1644 const framepos_t note_end_frames = min (source_beats_to_region_frames (note->end_time()), _region->length());
1645 ev->set_x1 (trackview.editor().sample_to_pixel (note_end_frames));
1647 ev->set_x1 (trackview.editor().sample_to_pixel (_region->length()));
1650 ev->set_y1 (y0 + floor(midi_stream_view()->note_height()));
1652 if (note->length() == 0) {
1653 if (_active_notes && note->note() < 128) {
1654 // If this note is already active there's a stuck note,
1655 // finish the old note rectangle
1656 if (_active_notes[note->note()]) {
1657 Note* const old_rect = _active_notes[note->note()];
1658 boost::shared_ptr<NoteType> old_note = old_rect->note();
1659 old_rect->set_x1 (x);
1660 old_rect->set_outline_what (0xF);
1662 _active_notes[note->note()] = ev;
1664 /* outline all but right edge */
1665 ev->set_outline_what (0x1 & 0x4 & 0x8);
1667 /* outline all edges */
1668 ev->set_outline_what (0xF);
1671 if (update_ghost_regions) {
1672 for (std::vector<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
1673 MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*i);
1675 gr->update_note (ev);
1682 MidiRegionView::update_hit (Hit* ev)
1684 boost::shared_ptr<NoteType> note = ev->note();
1686 const framepos_t note_start_frames = source_beats_to_region_frames(note->time());
1687 const double x = trackview.editor().sample_to_pixel(note_start_frames);
1688 const double diamond_size = midi_stream_view()->note_height() / 2.0;
1689 const double y = midi_stream_view()->note_to_y(note->note()) + ((diamond_size-2) / 4.0);
1691 ev->set_position (ArdourCanvas::Duple (x, y));
1693 return diamond_size;
1696 /** Add a MIDI note to the view (with length).
1698 * If in sustained mode, notes with length 0 will be considered active
1699 * notes, and resolve_note should be called when the corresponding note off
1700 * event arrives, to properly display the note.
1703 MidiRegionView::add_note(const boost::shared_ptr<NoteType> note, bool visible)
1705 NoteBase* event = 0;
1707 //ArdourCanvas::Group* const group = (ArdourCanvas::Group*) get_canvas_group();
1709 if (midi_view()->note_mode() == Sustained) {
1711 Note* ev_rect = new Note (*this, _note_group, note);
1713 update_note (ev_rect);
1717 MidiGhostRegion* gr;
1719 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
1720 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
1721 gr->add_note(ev_rect);
1725 } else if (midi_view()->note_mode() == Percussive) {
1727 const double diamond_size = midi_stream_view()->note_height() / 2.0;
1729 Hit* ev_diamond = new Hit (*this, _note_group, diamond_size, note);
1731 update_hit (ev_diamond);
1740 if (_marked_for_selection.find(note) != _marked_for_selection.end()) {
1741 note_selected(event, true);
1744 if (_marked_for_velocity.find(note) != _marked_for_velocity.end()) {
1745 event->show_velocity();
1748 event->on_channel_selection_change (get_selected_channels());
1749 _events.push_back(event);
1758 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1759 MidiStreamView* const view = mtv->midi_view();
1761 view->update_note_range (note->note());
1765 MidiRegionView::step_add_note (uint8_t channel, uint8_t number, uint8_t velocity,
1766 Evoral::MusicalTime pos, Evoral::MusicalTime len)
1768 boost::shared_ptr<NoteType> new_note (new NoteType (channel, pos, len, number, velocity));
1770 /* potentially extend region to hold new note */
1772 framepos_t end_frame = source_beats_to_absolute_frames (new_note->end_time());
1773 framepos_t region_end = _region->last_frame();
1775 if (end_frame > region_end) {
1776 _region->set_length (end_frame - _region->position());
1779 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1780 MidiStreamView* const view = mtv->midi_view();
1782 view->update_note_range(new_note->note());
1784 _marked_for_selection.clear ();
1787 start_note_diff_command (_("step add"));
1788 note_diff_add_note (new_note, true, false);
1791 // last_step_edit_note = new_note;
1795 MidiRegionView::step_sustain (Evoral::MusicalTime beats)
1797 change_note_lengths (false, false, beats, false, true);
1800 /** Add a new patch change flag to the canvas.
1801 * @param patch the patch change to add
1802 * @param the text to display in the flag
1803 * @param active_channel true to display the flag as on an active channel, false to grey it out for an inactive channel.
1806 MidiRegionView::add_canvas_patch_change (MidiModel::PatchChangePtr patch, const string& displaytext, bool /*active_channel*/)
1808 framecnt_t region_frames = source_beats_to_region_frames (patch->time());
1809 const double x = trackview.editor().sample_to_pixel (region_frames);
1811 double const height = midi_stream_view()->contents_height();
1813 // CAIROCANVAS: active_channel info removed from PatcChange constructor
1814 // so we need to do something more sophisticated to keep its color
1815 // appearance (MidiPatchChangeFill/MidiPatchChangeInactiveChannelFill)
1818 boost::shared_ptr<PatchChange> patch_change = boost::shared_ptr<PatchChange>(
1819 new PatchChange(*this, group,
1826 if (patch_change->item().width() < _pixel_width) {
1827 // Show unless patch change is beyond the region bounds
1828 if (region_frames < 0 || region_frames >= _region->length()) {
1829 patch_change->hide();
1831 patch_change->show();
1834 patch_change->hide ();
1837 _patch_changes.push_back (patch_change);
1840 MIDI::Name::PatchPrimaryKey
1841 MidiRegionView::patch_change_to_patch_key (MidiModel::PatchChangePtr p)
1843 return MIDI::Name::PatchPrimaryKey (p->program(), p->bank());
1846 /// Return true iff @p pc applies to the given time on the given channel.
1848 patch_applies (const ARDOUR::MidiModel::constPatchChangePtr pc, double time, uint8_t channel)
1850 return pc->time() <= time && pc->channel() == channel;
1854 MidiRegionView::get_patch_key_at (double time, uint8_t channel, MIDI::Name::PatchPrimaryKey& key) const
1856 // The earliest event not before time
1857 MidiModel::PatchChanges::iterator i = _model->patch_change_lower_bound (time);
1859 // Go backwards until we find the latest PC for this channel, or the start
1860 while (i != _model->patch_changes().begin() &&
1861 (i == _model->patch_changes().end() ||
1862 !patch_applies(*i, time, channel))) {
1866 if (i != _model->patch_changes().end() && patch_applies(*i, time, channel)) {
1867 key.bank_number = (*i)->bank();
1868 key.program_number = (*i)->program ();
1870 key.bank_number = key.program_number = 0;
1873 if (!key.is_sane()) {
1874 error << string_compose(_("insane MIDI patch key %1:%2"),
1875 key.bank_number, key.program_number) << endmsg;
1880 MidiRegionView::change_patch_change (PatchChange& pc, const MIDI::Name::PatchPrimaryKey& new_patch)
1882 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("alter patch change"));
1884 if (pc.patch()->program() != new_patch.program_number) {
1885 c->change_program (pc.patch (), new_patch.program_number);
1888 int const new_bank = new_patch.bank_number;
1889 if (pc.patch()->bank() != new_bank) {
1890 c->change_bank (pc.patch (), new_bank);
1893 _model->apply_command (*trackview.session(), c);
1895 _patch_changes.clear ();
1896 display_patch_changes ();
1900 MidiRegionView::change_patch_change (MidiModel::PatchChangePtr old_change, const Evoral::PatchChange<Evoral::MusicalTime> & new_change)
1902 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("alter patch change"));
1904 if (old_change->time() != new_change.time()) {
1905 c->change_time (old_change, new_change.time());
1908 if (old_change->channel() != new_change.channel()) {
1909 c->change_channel (old_change, new_change.channel());
1912 if (old_change->program() != new_change.program()) {
1913 c->change_program (old_change, new_change.program());
1916 if (old_change->bank() != new_change.bank()) {
1917 c->change_bank (old_change, new_change.bank());
1920 _model->apply_command (*trackview.session(), c);
1922 _patch_changes.clear ();
1923 display_patch_changes ();
1926 /** Add a patch change to the region.
1927 * @param t Time in frames relative to region position
1928 * @param patch Patch to add; time and channel are ignored (time is converted from t, and channel comes from
1929 * MidiTimeAxisView::get_channel_for_add())
1932 MidiRegionView::add_patch_change (framecnt_t t, Evoral::PatchChange<Evoral::MusicalTime> const & patch)
1934 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1936 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("add patch change"));
1937 c->add (MidiModel::PatchChangePtr (
1938 new Evoral::PatchChange<Evoral::MusicalTime> (
1939 absolute_frames_to_source_beats (_region->position() + t),
1940 mtv->get_channel_for_add(), patch.program(), patch.bank()
1945 _model->apply_command (*trackview.session(), c);
1947 _patch_changes.clear ();
1948 display_patch_changes ();
1952 MidiRegionView::move_patch_change (PatchChange& pc, Evoral::MusicalTime t)
1954 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("move patch change"));
1955 c->change_time (pc.patch (), t);
1956 _model->apply_command (*trackview.session(), c);
1958 _patch_changes.clear ();
1959 display_patch_changes ();
1963 MidiRegionView::delete_patch_change (PatchChange* pc)
1965 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("delete patch change"));
1966 c->remove (pc->patch ());
1967 _model->apply_command (*trackview.session(), c);
1969 _patch_changes.clear ();
1970 display_patch_changes ();
1974 MidiRegionView::previous_patch (PatchChange& patch)
1976 if (patch.patch()->program() < 127) {
1977 MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch());
1978 key.program_number++;
1979 change_patch_change (patch, key);
1984 MidiRegionView::next_patch (PatchChange& patch)
1986 if (patch.patch()->program() > 0) {
1987 MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch());
1988 key.program_number--;
1989 change_patch_change (patch, key);
1994 MidiRegionView::next_bank (PatchChange& patch)
1996 if (patch.patch()->program() < 127) {
1997 MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch());
1998 if (key.bank_number > 0) {
2000 change_patch_change (patch, key);
2006 MidiRegionView::previous_bank (PatchChange& patch)
2008 if (patch.patch()->program() > 0) {
2009 MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch());
2010 if (key.bank_number < 127) {
2012 change_patch_change (patch, key);
2018 MidiRegionView::maybe_remove_deleted_note_from_selection (NoteBase* cne)
2020 if (_selection.empty()) {
2024 _selection.erase (cne);
2028 MidiRegionView::delete_selection()
2030 if (_selection.empty()) {
2034 start_note_diff_command (_("delete selection"));
2036 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2037 if ((*i)->selected()) {
2038 _note_diff_command->remove((*i)->note());
2048 MidiRegionView::delete_note (boost::shared_ptr<NoteType> n)
2050 start_note_diff_command (_("delete note"));
2051 _note_diff_command->remove (n);
2054 trackview.editor().verbose_cursor()->hide ();
2058 MidiRegionView::clear_selection_except (NoteBase* ev, bool signal)
2060 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2062 Selection::iterator tmp = i;
2065 (*i)->set_selected (false);
2066 (*i)->hide_velocity ();
2067 _selection.erase (i);
2075 /* this does not change the status of this regionview w.r.t the editor
2080 SelectionCleared (this); /* EMIT SIGNAL */
2085 MidiRegionView::unique_select(NoteBase* ev)
2087 clear_selection_except (ev);
2089 /* don't bother with checking to see if we should remove this
2090 regionview from the editor selection, since we're about to add
2091 another note, and thus put/keep this regionview in the editor
2095 if (!ev->selected()) {
2096 add_to_selection (ev);
2101 MidiRegionView::select_all_notes ()
2105 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2106 add_to_selection (*i);
2111 MidiRegionView::select_range (framepos_t start, framepos_t end)
2115 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2116 framepos_t t = source_beats_to_absolute_frames((*i)->note()->time());
2117 if (t >= start && t <= end) {
2118 add_to_selection (*i);
2124 MidiRegionView::invert_selection ()
2126 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2127 if ((*i)->selected()) {
2128 remove_from_selection(*i);
2130 add_to_selection (*i);
2136 MidiRegionView::select_matching_notes (uint8_t notenum, uint16_t channel_mask, bool add, bool extend)
2138 bool have_selection = !_selection.empty();
2139 uint8_t low_note = 127;
2140 uint8_t high_note = 0;
2141 MidiModel::Notes& notes (_model->notes());
2142 _optimization_iterator = _events.begin();
2144 if (extend && !have_selection) {
2148 /* scan existing selection to get note range */
2150 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2151 if ((*i)->note()->note() < low_note) {
2152 low_note = (*i)->note()->note();
2154 if ((*i)->note()->note() > high_note) {
2155 high_note = (*i)->note()->note();
2162 if (!extend && (low_note == high_note) && (high_note == notenum)) {
2163 /* only note previously selected is the one we are
2164 * reselecting. treat this as cancelling the selection.
2171 low_note = min (low_note, notenum);
2172 high_note = max (high_note, notenum);
2175 _no_sound_notes = true;
2177 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
2179 boost::shared_ptr<NoteType> note (*n);
2181 bool select = false;
2183 if (((1 << note->channel()) & channel_mask) != 0) {
2185 if ((note->note() >= low_note && note->note() <= high_note)) {
2188 } else if (note->note() == notenum) {
2194 if ((cne = find_canvas_note (note)) != 0) {
2195 // extend is false because we've taken care of it,
2196 // since it extends by time range, not pitch.
2197 note_selected (cne, add, false);
2201 add = true; // we need to add all remaining matching notes, even if the passed in value was false (for "set")
2205 _no_sound_notes = false;
2209 MidiRegionView::toggle_matching_notes (uint8_t notenum, uint16_t channel_mask)
2211 MidiModel::Notes& notes (_model->notes());
2212 _optimization_iterator = _events.begin();
2214 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
2216 boost::shared_ptr<NoteType> note (*n);
2219 if (note->note() == notenum && (((0x0001 << note->channel()) & channel_mask) != 0)) {
2220 if ((cne = find_canvas_note (note)) != 0) {
2221 if (cne->selected()) {
2222 note_deselected (cne);
2224 note_selected (cne, true, false);
2232 MidiRegionView::note_selected (NoteBase* ev, bool add, bool extend)
2235 clear_selection_except (ev);
2236 if (!_selection.empty()) {
2237 PublicEditor& editor (trackview.editor());
2238 editor.get_selection().add (this);
2244 if (!ev->selected()) {
2245 add_to_selection (ev);
2249 /* find end of latest note selected, select all between that and the start of "ev" */
2251 Evoral::MusicalTime earliest = Evoral::MaxMusicalTime;
2252 Evoral::MusicalTime latest = 0;
2254 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2255 if ((*i)->note()->end_time() > latest) {
2256 latest = (*i)->note()->end_time();
2258 if ((*i)->note()->time() < earliest) {
2259 earliest = (*i)->note()->time();
2263 if (ev->note()->end_time() > latest) {
2264 latest = ev->note()->end_time();
2267 if (ev->note()->time() < earliest) {
2268 earliest = ev->note()->time();
2271 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2273 /* find notes entirely within OR spanning the earliest..latest range */
2275 if (((*i)->note()->time() >= earliest && (*i)->note()->end_time() <= latest) ||
2276 ((*i)->note()->time() <= earliest && (*i)->note()->end_time() >= latest)) {
2277 add_to_selection (*i);
2285 MidiRegionView::note_deselected(NoteBase* ev)
2287 remove_from_selection (ev);
2291 MidiRegionView::update_drag_selection(double x0, double x1, double y0, double y1, bool extend)
2293 // TODO: Make this faster by storing the last updated selection rect, and only
2294 // adjusting things that are in the area that appears/disappeared.
2295 // We probably need a tree to be able to find events in O(log(n)) time.
2297 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2298 if ((*i)->x0() < x1 && (*i)->x1() > x0 && (*i)->y0() < y1 && (*i)->y1() > y0) {
2299 // Rectangles intersect
2300 if (!(*i)->selected()) {
2301 add_to_selection (*i);
2303 } else if ((*i)->selected() && !extend) {
2304 // Rectangles do not intersect
2305 remove_from_selection (*i);
2311 MidiRegionView::update_vertical_drag_selection (double y1, double y2, bool extend)
2317 // TODO: Make this faster by storing the last updated selection rect, and only
2318 // adjusting things that are in the area that appears/disappeared.
2319 // We probably need a tree to be able to find events in O(log(n)) time.
2321 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2322 if (((*i)->y1() >= y1 && (*i)->y1() <= y2)) {
2323 // within y- (note-) range
2324 if (!(*i)->selected()) {
2325 add_to_selection (*i);
2327 } else if ((*i)->selected() && !extend) {
2328 remove_from_selection (*i);
2334 MidiRegionView::remove_from_selection (NoteBase* ev)
2336 Selection::iterator i = _selection.find (ev);
2338 if (i != _selection.end()) {
2339 _selection.erase (i);
2342 ev->set_selected (false);
2343 ev->hide_velocity ();
2345 if (_selection.empty()) {
2346 PublicEditor& editor (trackview.editor());
2347 editor.get_selection().remove (this);
2352 MidiRegionView::add_to_selection (NoteBase* ev)
2354 bool add_mrv_selection = false;
2356 if (_selection.empty()) {
2357 add_mrv_selection = true;
2360 if (_selection.insert (ev).second) {
2361 ev->set_selected (true);
2362 start_playing_midi_note ((ev)->note());
2365 if (add_mrv_selection) {
2366 PublicEditor& editor (trackview.editor());
2367 editor.get_selection().add (this);
2372 MidiRegionView::move_selection(double dx, double dy, double cumulative_dy)
2374 typedef vector<boost::shared_ptr<NoteType> > PossibleChord;
2375 PossibleChord to_play;
2376 Evoral::MusicalTime earliest = Evoral::MaxMusicalTime;
2378 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2379 if ((*i)->note()->time() < earliest) {
2380 earliest = (*i)->note()->time();
2384 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2385 if (Evoral::musical_time_equal ((*i)->note()->time(), earliest)) {
2386 to_play.push_back ((*i)->note());
2388 (*i)->move_event(dx, dy);
2391 if (dy && !_selection.empty() && !_no_sound_notes && Config->get_sound_midi_notes()) {
2393 if (to_play.size() > 1) {
2395 PossibleChord shifted;
2397 for (PossibleChord::iterator n = to_play.begin(); n != to_play.end(); ++n) {
2398 boost::shared_ptr<NoteType> moved_note (new NoteType (**n));
2399 moved_note->set_note (moved_note->note() + cumulative_dy);
2400 shifted.push_back (moved_note);
2403 start_playing_midi_chord (shifted);
2405 } else if (!to_play.empty()) {
2407 boost::shared_ptr<NoteType> moved_note (new NoteType (*to_play.front()));
2408 moved_note->set_note (moved_note->note() + cumulative_dy);
2409 start_playing_midi_note (moved_note);
2415 MidiRegionView::note_dropped(NoteBase *, frameoffset_t dt, int8_t dnote)
2417 uint8_t lowest_note_in_selection = 127;
2418 uint8_t highest_note_in_selection = 0;
2419 uint8_t highest_note_difference = 0;
2421 // find highest and lowest notes first
2423 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2424 uint8_t pitch = (*i)->note()->note();
2425 lowest_note_in_selection = std::min(lowest_note_in_selection, pitch);
2426 highest_note_in_selection = std::max(highest_note_in_selection, pitch);
2430 cerr << "dnote: " << (int) dnote << endl;
2431 cerr << "lowest note (streamview): " << int(midi_stream_view()->lowest_note())
2432 << " highest note (streamview): " << int(midi_stream_view()->highest_note()) << endl;
2433 cerr << "lowest note (selection): " << int(lowest_note_in_selection) << " highest note(selection): "
2434 << int(highest_note_in_selection) << endl;
2435 cerr << "selection size: " << _selection.size() << endl;
2436 cerr << "Highest note in selection: " << (int) highest_note_in_selection << endl;
2439 // Make sure the note pitch does not exceed the MIDI standard range
2440 if (highest_note_in_selection + dnote > 127) {
2441 highest_note_difference = highest_note_in_selection - 127;
2444 start_note_diff_command (_("move notes"));
2446 for (Selection::iterator i = _selection.begin(); i != _selection.end() ; ++i) {
2448 framepos_t new_frames = source_beats_to_absolute_frames ((*i)->note()->time()) + dt;
2449 Evoral::MusicalTime new_time = absolute_frames_to_source_beats (new_frames);
2455 note_diff_add_change (*i, MidiModel::NoteDiffCommand::StartTime, new_time);
2457 uint8_t original_pitch = (*i)->note()->note();
2458 uint8_t new_pitch = original_pitch + dnote - highest_note_difference;
2460 // keep notes in standard midi range
2461 clamp_to_0_127(new_pitch);
2463 lowest_note_in_selection = std::min(lowest_note_in_selection, new_pitch);
2464 highest_note_in_selection = std::max(highest_note_in_selection, new_pitch);
2466 note_diff_add_change (*i, MidiModel::NoteDiffCommand::NoteNumber, new_pitch);
2471 // care about notes being moved beyond the upper/lower bounds on the canvas
2472 if (lowest_note_in_selection < midi_stream_view()->lowest_note() ||
2473 highest_note_in_selection > midi_stream_view()->highest_note()) {
2474 midi_stream_view()->set_note_range(MidiStreamView::ContentsRange);
2478 /** @param x Pixel relative to the region position.
2479 * @return Snapped frame relative to the region position.
2482 MidiRegionView::snap_pixel_to_sample(double x)
2484 PublicEditor& editor (trackview.editor());
2485 return snap_frame_to_frame (editor.pixel_to_sample (x));
2488 /** @param x Pixel relative to the region position.
2489 * @return Snapped pixel relative to the region position.
2492 MidiRegionView::snap_to_pixel(double x)
2494 return (double) trackview.editor().sample_to_pixel(snap_pixel_to_sample(x));
2498 MidiRegionView::get_position_pixels()
2500 framepos_t region_frame = get_position();
2501 return trackview.editor().sample_to_pixel(region_frame);
2505 MidiRegionView::get_end_position_pixels()
2507 framepos_t frame = get_position() + get_duration ();
2508 return trackview.editor().sample_to_pixel(frame);
2512 MidiRegionView::source_beats_to_absolute_frames(double beats) const
2514 /* the time converter will return the frame corresponding to `beats'
2515 relative to the start of the source. The start of the source
2516 is an implied position given by region->position - region->start
2518 const framepos_t source_start = _region->position() - _region->start();
2519 return source_start + _source_relative_time_converter.to (beats);
2523 MidiRegionView::absolute_frames_to_source_beats(framepos_t frames) const
2525 /* the `frames' argument needs to be converted into a frame count
2526 relative to the start of the source before being passed in to the
2529 const framepos_t source_start = _region->position() - _region->start();
2530 return _source_relative_time_converter.from (frames - source_start);
2534 MidiRegionView::region_beats_to_region_frames(double beats) const
2536 return _region_relative_time_converter.to(beats);
2540 MidiRegionView::region_frames_to_region_beats(framepos_t frames) const
2542 return _region_relative_time_converter.from(frames);
2546 MidiRegionView::begin_resizing (bool /*at_front*/)
2548 _resize_data.clear();
2550 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2551 Note *note = dynamic_cast<Note*> (*i);
2553 // only insert CanvasNotes into the map
2555 NoteResizeData *resize_data = new NoteResizeData();
2556 resize_data->note = note;
2558 // create a new SimpleRect from the note which will be the resize preview
2559 ArdourCanvas::Rectangle *resize_rect = new ArdourCanvas::Rectangle (_note_group,
2560 ArdourCanvas::Rect (note->x0(), note->y0(), note->x0(), note->y1()));
2562 // calculate the colors: get the color settings
2563 uint32_t fill_color = UINT_RGBA_CHANGE_A(
2564 ARDOUR_UI::config()->get_canvasvar_MidiNoteSelected(),
2567 // make the resize preview notes more transparent and bright
2568 fill_color = UINT_INTERPOLATE(fill_color, 0xFFFFFF40, 0.5);
2570 // calculate color based on note velocity
2571 resize_rect->set_fill_color (UINT_INTERPOLATE(
2572 NoteBase::meter_style_fill_color(note->note()->velocity(), note->selected()),
2576 resize_rect->set_outline_color (NoteBase::calculate_outline (
2577 ARDOUR_UI::config()->get_canvasvar_MidiNoteSelected()));
2579 resize_data->resize_rect = resize_rect;
2580 _resize_data.push_back(resize_data);
2585 /** Update resizing notes while user drags.
2586 * @param primary `primary' note for the drag; ie the one that is used as the reference in non-relative mode.
2587 * @param at_front which end of the note (true == note on, false == note off)
2588 * @param delta_x change in mouse position since the start of the drag
2589 * @param relative true if relative resizing is taking place, false if absolute resizing. This only makes
2590 * a difference when multiple notes are being resized; in relative mode, each note's length is changed by the
2591 * amount of the drag. In non-relative mode, all selected notes are set to have the same start or end point
2592 * as the \a primary note.
2595 MidiRegionView::update_resizing (NoteBase* primary, bool at_front, double delta_x, bool relative)
2597 bool cursor_set = false;
2599 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2600 ArdourCanvas::Rectangle* resize_rect = (*i)->resize_rect;
2601 Note* canvas_note = (*i)->note;
2606 current_x = canvas_note->x0() + delta_x;
2608 current_x = primary->x0() + delta_x;
2612 current_x = canvas_note->x1() + delta_x;
2614 current_x = primary->x1() + delta_x;
2619 resize_rect->set_x0 (snap_to_pixel(current_x));
2620 resize_rect->set_x1 (canvas_note->x1());
2622 resize_rect->set_x1 (snap_to_pixel(current_x));
2623 resize_rect->set_x0 (canvas_note->x0());
2629 beats = snap_pixel_to_sample (current_x);
2630 beats = region_frames_to_region_beats (beats);
2635 if (beats < canvas_note->note()->end_time()) {
2636 len = canvas_note->note()->time() - beats;
2637 len += canvas_note->note()->length();
2642 if (beats >= canvas_note->note()->time()) {
2643 len = beats - canvas_note->note()->time();
2650 snprintf (buf, sizeof (buf), "%.3g beats", len);
2651 show_verbose_cursor (buf, 0, 0);
2660 /** Finish resizing notes when the user releases the mouse button.
2661 * Parameters the same as for \a update_resizing().
2664 MidiRegionView::commit_resizing (NoteBase* primary, bool at_front, double delta_x, bool relative)
2666 start_note_diff_command (_("resize notes"));
2668 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2669 Note* canvas_note = (*i)->note;
2670 ArdourCanvas::Rectangle* resize_rect = (*i)->resize_rect;
2672 /* Get the new x position for this resize, which is in pixels relative
2673 * to the region position.
2680 current_x = canvas_note->x0() + delta_x;
2682 current_x = primary->x0() + delta_x;
2686 current_x = canvas_note->x1() + delta_x;
2688 current_x = primary->x1() + delta_x;
2692 /* Convert that to a frame within the source */
2693 current_x = snap_pixel_to_sample (current_x) + _region->start ();
2695 /* and then to beats */
2696 current_x = region_frames_to_region_beats (current_x);
2698 if (at_front && current_x < canvas_note->note()->end_time()) {
2699 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::StartTime, current_x);
2701 double len = canvas_note->note()->time() - current_x;
2702 len += canvas_note->note()->length();
2705 /* XXX convert to beats */
2706 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
2711 double len = current_x - canvas_note->note()->time();
2714 /* XXX convert to beats */
2715 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
2723 _resize_data.clear();
2728 MidiRegionView::abort_resizing ()
2730 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2731 delete (*i)->resize_rect;
2735 _resize_data.clear ();
2739 MidiRegionView::change_note_velocity(NoteBase* event, int8_t velocity, bool relative)
2741 uint8_t new_velocity;
2744 new_velocity = event->note()->velocity() + velocity;
2745 clamp_to_0_127(new_velocity);
2747 new_velocity = velocity;
2750 event->set_selected (event->selected()); // change color
2752 note_diff_add_change (event, MidiModel::NoteDiffCommand::Velocity, new_velocity);
2756 MidiRegionView::change_note_note (NoteBase* event, int8_t note, bool relative)
2761 new_note = event->note()->note() + note;
2766 clamp_to_0_127 (new_note);
2767 note_diff_add_change (event, MidiModel::NoteDiffCommand::NoteNumber, new_note);
2771 MidiRegionView::trim_note (NoteBase* event, Evoral::MusicalTime front_delta, Evoral::MusicalTime end_delta)
2773 bool change_start = false;
2774 bool change_length = false;
2775 Evoral::MusicalTime new_start = 0;
2776 Evoral::MusicalTime new_length = 0;
2778 /* NOTE: the semantics of the two delta arguments are slightly subtle:
2780 front_delta: if positive - move the start of the note later in time (shortening it)
2781 if negative - move the start of the note earlier in time (lengthening it)
2783 end_delta: if positive - move the end of the note later in time (lengthening it)
2784 if negative - move the end of the note earlier in time (shortening it)
2788 if (front_delta < 0) {
2790 if (event->note()->time() < -front_delta) {
2793 new_start = event->note()->time() + front_delta; // moves earlier
2796 /* start moved toward zero, so move the end point out to where it used to be.
2797 Note that front_delta is negative, so this increases the length.
2800 new_length = event->note()->length() - front_delta;
2801 change_start = true;
2802 change_length = true;
2806 Evoral::MusicalTime new_pos = event->note()->time() + front_delta;
2808 if (new_pos < event->note()->end_time()) {
2809 new_start = event->note()->time() + front_delta;
2810 /* start moved toward the end, so move the end point back to where it used to be */
2811 new_length = event->note()->length() - front_delta;
2812 change_start = true;
2813 change_length = true;
2820 bool can_change = true;
2821 if (end_delta < 0) {
2822 if (event->note()->length() < -end_delta) {
2828 new_length = event->note()->length() + end_delta;
2829 change_length = true;
2834 note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_start);
2837 if (change_length) {
2838 note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, new_length);
2843 MidiRegionView::change_note_channel (NoteBase* event, int8_t chn, bool relative)
2845 uint8_t new_channel;
2849 if (event->note()->channel() < -chn) {
2852 new_channel = event->note()->channel() + chn;
2855 new_channel = event->note()->channel() + chn;
2858 new_channel = (uint8_t) chn;
2861 note_diff_add_change (event, MidiModel::NoteDiffCommand::Channel, new_channel);
2865 MidiRegionView::change_note_time (NoteBase* event, Evoral::MusicalTime delta, bool relative)
2867 Evoral::MusicalTime new_time;
2871 if (event->note()->time() < -delta) {
2874 new_time = event->note()->time() + delta;
2877 new_time = event->note()->time() + delta;
2883 note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_time);
2887 MidiRegionView::change_note_length (NoteBase* event, Evoral::MusicalTime t)
2889 note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, t);
2893 MidiRegionView::change_velocities (bool up, bool fine, bool allow_smush, bool all_together)
2898 if (_selection.empty()) {
2913 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2914 if ((*i)->note()->velocity() < -delta || (*i)->note()->velocity() + delta > 127) {
2920 start_note_diff_command (_("change velocities"));
2922 for (Selection::iterator i = _selection.begin(); i != _selection.end();) {
2923 Selection::iterator next = i;
2927 if (i == _selection.begin()) {
2928 change_note_velocity (*i, delta, true);
2929 value = (*i)->note()->velocity() + delta;
2931 change_note_velocity (*i, value, false);
2935 change_note_velocity (*i, delta, true);
2944 if (!_selection.empty()) {
2946 snprintf (buf, sizeof (buf), "Vel %d",
2947 (int) (*_selection.begin())->note()->velocity());
2948 show_verbose_cursor (buf, 10, 10);
2954 MidiRegionView::transpose (bool up, bool fine, bool allow_smush)
2956 if (_selection.empty()) {
2973 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2975 if ((int8_t) (*i)->note()->note() + delta <= 0) {
2979 if ((int8_t) (*i)->note()->note() + delta > 127) {
2986 start_note_diff_command (_("transpose"));
2988 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2989 Selection::iterator next = i;
2991 change_note_note (*i, delta, true);
2999 MidiRegionView::change_note_lengths (bool fine, bool shorter, Evoral::MusicalTime delta, bool start, bool end)
3005 /* grab the current grid distance */
3007 delta = trackview.editor().get_grid_type_as_beats (success, _region->position());
3009 /* XXX cannot get grid type as beats ... should always be possible ... FIX ME */
3010 error << string_compose (_("programming error: %1"), "Grid type not available as beats - TO BE FIXED") << endmsg;
3020 start_note_diff_command (_("change note lengths"));
3022 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3023 Selection::iterator next = i;
3026 /* note the negation of the delta for start */
3028 trim_note (*i, (start ? -delta : 0), (end ? delta : 0));
3037 MidiRegionView::nudge_notes (bool forward)
3039 if (_selection.empty()) {
3043 /* pick a note as the point along the timeline to get the nudge distance.
3044 its not necessarily the earliest note, so we may want to pull the notes out
3045 into a vector and sort before using the first one.
3048 framepos_t ref_point = source_beats_to_absolute_frames ((*(_selection.begin()))->note()->time());
3050 framecnt_t distance;
3052 if (trackview.editor().snap_mode() == Editing::SnapOff) {
3054 /* grid is off - use nudge distance */
3056 distance = trackview.editor().get_nudge_distance (ref_point, unused);
3062 framepos_t next_pos = ref_point;
3065 if (max_framepos - 1 < next_pos) {
3069 if (next_pos == 0) {
3075 trackview.editor().snap_to (next_pos, (forward ? 1 : -1), false);
3076 distance = ref_point - next_pos;
3079 if (distance == 0) {
3083 Evoral::MusicalTime delta = region_frames_to_region_beats (fabs ((double)distance));
3089 start_note_diff_command (_("nudge"));
3091 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3092 Selection::iterator next = i;
3094 change_note_time (*i, delta, true);
3102 MidiRegionView::change_channel(uint8_t channel)
3104 start_note_diff_command(_("change channel"));
3105 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3106 note_diff_add_change (*i, MidiModel::NoteDiffCommand::Channel, channel);
3114 MidiRegionView::note_entered(NoteBase* ev)
3116 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3118 pre_enter_cursor = editor->get_canvas_cursor ();
3120 if (_mouse_state == SelectTouchDragging) {
3121 note_selected (ev, true);
3124 show_verbose_cursor (ev->note ());
3128 MidiRegionView::note_left (NoteBase*)
3130 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3132 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3133 (*i)->hide_velocity ();
3136 editor->verbose_cursor()->hide ();
3138 if (pre_enter_cursor) {
3139 editor->set_canvas_cursor (pre_enter_cursor);
3140 pre_enter_cursor = 0;
3145 MidiRegionView::patch_entered (PatchChange* p)
3148 /* XXX should get patch name if we can */
3149 s << _("Bank ") << (p->patch()->bank() + MIDI_BP_ZERO) << '\n'
3150 << _("Program ") << ((int) p->patch()->program()) + MIDI_BP_ZERO << '\n'
3151 << _("Channel ") << ((int) p->patch()->channel() + 1);
3152 show_verbose_cursor (s.str(), 10, 20);
3153 p->item().grab_focus();
3157 MidiRegionView::patch_left (PatchChange *)
3159 trackview.editor().verbose_cursor()->hide ();
3160 /* focus will transfer back via the enter-notify event sent to this
3166 MidiRegionView::sysex_entered (SysEx* p)
3170 // need a way to extract text from p->_flag->_text
3172 // show_verbose_cursor (s.str(), 10, 20);
3173 p->item().grab_focus();
3177 MidiRegionView::sysex_left (SysEx *)
3179 trackview.editor().verbose_cursor()->hide ();
3180 /* focus will transfer back via the enter-notify event sent to this
3186 MidiRegionView::note_mouse_position (float x_fraction, float /*y_fraction*/, bool can_set_cursor)
3188 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3189 Editing::MouseMode mm = editor->current_mouse_mode();
3190 bool trimmable = (mm == MouseObject || mm == MouseTimeFX || mm == MouseDraw);
3192 if (trimmable && x_fraction > 0.0 && x_fraction < 0.2) {
3193 editor->set_canvas_cursor (editor->cursors()->left_side_trim);
3194 } else if (trimmable && x_fraction >= 0.8 && x_fraction < 1.0) {
3195 editor->set_canvas_cursor (editor->cursors()->right_side_trim);
3197 if (pre_enter_cursor && can_set_cursor) {
3198 editor->set_canvas_cursor (pre_enter_cursor);
3204 MidiRegionView::set_frame_color()
3208 TimeAxisViewItem::set_frame_color ();
3215 f = ARDOUR_UI::config()->get_canvasvar_SelectedFrameBase();
3216 } else if (high_enough_for_name) {
3217 f= ARDOUR_UI::config()->get_canvasvar_MidiFrameBase();
3222 if (!rect_visible) {
3223 f = UINT_RGBA_CHANGE_A (f, 0);
3226 frame->set_fill_color (f);
3230 MidiRegionView::midi_channel_mode_changed ()
3232 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3233 uint16_t mask = mtv->midi_track()->get_playback_channel_mask();
3234 ChannelMode mode = mtv->midi_track()->get_playback_channel_mode ();
3236 if (mode == ForceChannel) {
3237 mask = 0xFFFF; // Show all notes as active (below)
3240 // Update notes for selection
3241 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3242 (*i)->on_channel_selection_change (mask);
3245 _patch_changes.clear ();
3246 display_patch_changes ();
3250 MidiRegionView::instrument_settings_changed ()
3256 MidiRegionView::cut_copy_clear (Editing::CutCopyOp op)
3258 if (_selection.empty()) {
3262 PublicEditor& editor (trackview.editor());
3266 /* XXX what to do ? */
3270 editor.get_cut_buffer().add (selection_as_cut_buffer());
3278 start_note_diff_command();
3280 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3287 note_diff_remove_note (*i);
3297 MidiRegionView::selection_as_cut_buffer () const
3301 for (Selection::const_iterator i = _selection.begin(); i != _selection.end(); ++i) {
3302 NoteType* n = (*i)->note().get();
3303 notes.insert (boost::shared_ptr<NoteType> (new NoteType (*n)));
3306 MidiCutBuffer* cb = new MidiCutBuffer (trackview.session());
3312 /** This method handles undo */
3314 MidiRegionView::paste (framepos_t pos, float times, const MidiCutBuffer& mcb)
3320 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("MIDI paste @ %1 times %2\n", pos, times));
3322 trackview.session()->begin_reversible_command (_("paste"));
3324 start_note_diff_command (_("paste"));
3326 Evoral::MusicalTime beat_delta;
3327 Evoral::MusicalTime paste_pos_beats;
3328 Evoral::MusicalTime duration;
3329 Evoral::MusicalTime end_point = 0;
3331 duration = (*mcb.notes().rbegin())->end_time() - (*mcb.notes().begin())->time();
3332 paste_pos_beats = absolute_frames_to_source_beats (pos);
3333 beat_delta = (*mcb.notes().begin())->time() - paste_pos_beats;
3334 paste_pos_beats = 0;
3336 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",
3337 (*mcb.notes().begin())->time(),
3338 (*mcb.notes().rbegin())->end_time(),
3339 duration, pos, _region->position(),
3340 paste_pos_beats, beat_delta));
3344 for (int n = 0; n < (int) times; ++n) {
3346 for (Notes::const_iterator i = mcb.notes().begin(); i != mcb.notes().end(); ++i) {
3348 boost::shared_ptr<NoteType> copied_note (new NoteType (*((*i).get())));
3349 copied_note->set_time (paste_pos_beats + copied_note->time() - beat_delta);
3351 /* make all newly added notes selected */
3353 note_diff_add_note (copied_note, true);
3354 end_point = copied_note->end_time();
3357 paste_pos_beats += duration;
3360 /* if we pasted past the current end of the region, extend the region */
3362 framepos_t end_frame = source_beats_to_absolute_frames (end_point);
3363 framepos_t region_end = _region->position() + _region->length() - 1;
3365 if (end_frame > region_end) {
3367 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste extended region from %1 to %2\n", region_end, end_frame));
3369 _region->clear_changes ();
3370 _region->set_length (end_frame - _region->position());
3371 trackview.session()->add_command (new StatefulDiffCommand (_region));
3376 trackview.session()->commit_reversible_command ();
3379 struct EventNoteTimeEarlyFirstComparator {
3380 bool operator() (NoteBase* a, NoteBase* b) {
3381 return a->note()->time() < b->note()->time();
3386 MidiRegionView::time_sort_events ()
3388 if (!_sort_needed) {
3392 EventNoteTimeEarlyFirstComparator cmp;
3395 _sort_needed = false;
3399 MidiRegionView::goto_next_note (bool add_to_selection)
3401 bool use_next = false;
3403 if (_events.back()->selected()) {
3407 time_sort_events ();
3409 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3410 uint16_t const channel_mask = mtv->midi_track()->get_playback_channel_mask();
3412 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3413 if ((*i)->selected()) {
3416 } else if (use_next) {
3417 if (channel_mask & (1 << (*i)->note()->channel())) {
3418 if (!add_to_selection) {
3421 note_selected (*i, true, false);
3428 /* use the first one */
3430 if (!_events.empty() && (channel_mask & (1 << _events.front()->note()->channel ()))) {
3431 unique_select (_events.front());
3436 MidiRegionView::goto_previous_note (bool add_to_selection)
3438 bool use_next = false;
3440 if (_events.front()->selected()) {
3444 time_sort_events ();
3446 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3447 uint16_t const channel_mask = mtv->midi_track()->get_playback_channel_mask ();
3449 for (Events::reverse_iterator i = _events.rbegin(); i != _events.rend(); ++i) {
3450 if ((*i)->selected()) {
3453 } else if (use_next) {
3454 if (channel_mask & (1 << (*i)->note()->channel())) {
3455 if (!add_to_selection) {
3458 note_selected (*i, true, false);
3465 /* use the last one */
3467 if (!_events.empty() && (channel_mask & (1 << (*_events.rbegin())->note()->channel ()))) {
3468 unique_select (*(_events.rbegin()));
3473 MidiRegionView::selection_as_notelist (Notes& selected, bool allow_all_if_none_selected)
3475 bool had_selected = false;
3477 time_sort_events ();
3479 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3480 if ((*i)->selected()) {
3481 selected.insert ((*i)->note());
3482 had_selected = true;
3486 if (allow_all_if_none_selected && !had_selected) {
3487 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3488 selected.insert ((*i)->note());
3494 MidiRegionView::update_ghost_note (double x, double y)
3496 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3501 _note_group->canvas_to_item (x, y);
3503 PublicEditor& editor = trackview.editor ();
3505 framepos_t const unsnapped_frame = editor.pixel_to_sample (x);
3506 framecnt_t grid_frames;
3507 framepos_t const f = snap_frame_to_grid_underneath (unsnapped_frame, grid_frames);
3509 /* use region_frames... because we are converting a delta within the region
3513 double length = editor.get_grid_type_as_beats (success, unsnapped_frame);
3519 /* note that this sets the time of the ghost note in beats relative to
3520 the start of the source; that is how all note times are stored.
3522 _ghost_note->note()->set_time (absolute_frames_to_source_beats (f + _region->position ()));
3523 _ghost_note->note()->set_length (length);
3524 _ghost_note->note()->set_note (midi_stream_view()->y_to_note (y));
3525 _ghost_note->note()->set_channel (mtv->get_channel_for_add ());
3527 /* the ghost note does not appear in ghost regions, so pass false in here */
3528 update_note (_ghost_note, false);
3530 show_verbose_cursor (_ghost_note->note ());
3534 MidiRegionView::create_ghost_note (double x, double y)
3536 remove_ghost_note ();
3538 boost::shared_ptr<NoteType> g (new NoteType);
3539 _ghost_note = new Note (*this, _note_group, g);
3540 _ghost_note->set_ignore_events (true);
3541 _ghost_note->set_outline_color (0x000000aa);
3542 update_ghost_note (x, y);
3543 _ghost_note->show ();
3548 show_verbose_cursor (_ghost_note->note ());
3552 MidiRegionView::snap_changed ()
3558 create_ghost_note (_last_ghost_x, _last_ghost_y);
3562 MidiRegionView::drop_down_keys ()
3564 _mouse_state = None;
3568 MidiRegionView::maybe_select_by_position (GdkEventButton* ev, double /*x*/, double y)
3570 double note = midi_stream_view()->y_to_note(y);
3572 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3574 uint16_t chn_mask = mtv->midi_track()->get_playback_channel_mask();
3576 if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
3577 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchGreaterThanOrEqual, (uint8_t) floor (note), chn_mask);
3578 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
3579 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchLessThanOrEqual, (uint8_t) floor (note), chn_mask);
3584 bool add_mrv_selection = false;
3586 if (_selection.empty()) {
3587 add_mrv_selection = true;
3590 for (Events::iterator i = e.begin(); i != e.end(); ++i) {
3591 if (_selection.insert (*i).second) {
3592 (*i)->set_selected (true);
3596 if (add_mrv_selection) {
3597 PublicEditor& editor (trackview.editor());
3598 editor.get_selection().add (this);
3603 MidiRegionView::color_handler ()
3605 RegionView::color_handler ();
3607 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3608 (*i)->set_selected ((*i)->selected()); // will change color
3611 /* XXX probably more to do here */
3615 MidiRegionView::enable_display (bool yn)
3617 RegionView::enable_display (yn);
3624 MidiRegionView::show_step_edit_cursor (Evoral::MusicalTime pos)
3626 if (_step_edit_cursor == 0) {
3627 ArdourCanvas::Group* const group = (ArdourCanvas::Group*)get_canvas_group();
3629 _step_edit_cursor = new ArdourCanvas::Rectangle (group);
3630 _step_edit_cursor->set_y0 (0);
3631 _step_edit_cursor->set_y1 (midi_stream_view()->contents_height());
3632 _step_edit_cursor->set_fill_color (RGBA_TO_UINT (45,0,0,90));
3633 _step_edit_cursor->set_outline_color (RGBA_TO_UINT (85,0,0,90));
3636 move_step_edit_cursor (pos);
3637 _step_edit_cursor->show ();
3641 MidiRegionView::move_step_edit_cursor (Evoral::MusicalTime pos)
3643 _step_edit_cursor_position = pos;
3645 if (_step_edit_cursor) {
3646 double pixel = trackview.editor().sample_to_pixel (region_beats_to_region_frames (pos));
3647 _step_edit_cursor->set_x0 (pixel);
3648 set_step_edit_cursor_width (_step_edit_cursor_width);
3653 MidiRegionView::hide_step_edit_cursor ()
3655 if (_step_edit_cursor) {
3656 _step_edit_cursor->hide ();
3661 MidiRegionView::set_step_edit_cursor_width (Evoral::MusicalTime beats)
3663 _step_edit_cursor_width = beats;
3665 if (_step_edit_cursor) {
3666 _step_edit_cursor->set_x1 (_step_edit_cursor->x0() + trackview.editor().sample_to_pixel (region_beats_to_region_frames (beats)));
3670 /** Called when a diskstream on our track has received some data. Update the view, if applicable.
3671 * @param w Source that the data will end up in.
3674 MidiRegionView::data_recorded (boost::weak_ptr<MidiSource> w)
3676 if (!_active_notes) {
3677 /* we aren't actively being recorded to */
3681 boost::shared_ptr<MidiSource> src = w.lock ();
3682 if (!src || src != midi_region()->midi_source()) {
3683 /* recorded data was not destined for our source */
3687 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (&trackview);
3689 boost::shared_ptr<MidiBuffer> buf = mtv->midi_track()->get_gui_feed_buffer ();
3691 BeatsFramesConverter converter (trackview.session()->tempo_map(), mtv->midi_track()->get_capture_start_frame (0));
3693 framepos_t back = max_framepos;
3695 for (MidiBuffer::iterator i = buf->begin(); i != buf->end(); ++i) {
3696 Evoral::MIDIEvent<MidiBuffer::TimeType> const ev (*i, false);
3698 if (ev.is_channel_event()) {
3699 if (get_channel_mode() == FilterChannels) {
3700 if (((uint16_t(1) << ev.channel()) & get_selected_channels()) == 0) {
3706 /* ev.time() is in session frames, so (ev.time() - converter.origin_b()) is
3707 frames from the start of the source, and so time_beats is in terms of the
3711 Evoral::MusicalTime const time_beats = converter.from (ev.time () - converter.origin_b ());
3713 if (ev.type() == MIDI_CMD_NOTE_ON) {
3714 boost::shared_ptr<NoteType> note (
3715 new NoteType (ev.channel(), time_beats, 0, ev.note(), ev.velocity()));
3717 add_note (note, true);
3719 /* fix up our note range */
3720 if (ev.note() < _current_range_min) {
3721 midi_stream_view()->apply_note_range (ev.note(), _current_range_max, true);
3722 } else if (ev.note() > _current_range_max) {
3723 midi_stream_view()->apply_note_range (_current_range_min, ev.note(), true);
3726 } else if (ev.type() == MIDI_CMD_NOTE_OFF) {
3727 resolve_note (ev.note (), time_beats);
3733 midi_stream_view()->check_record_layers (region(), back);
3737 MidiRegionView::trim_front_starting ()
3739 /* Reparent the note group to the region view's parent, so that it doesn't change
3740 when the region view is trimmed.
3742 _temporary_note_group = new ArdourCanvas::Group (group->parent ());
3743 _temporary_note_group->move (group->position ());
3744 _note_group->reparent (_temporary_note_group);
3748 MidiRegionView::trim_front_ending ()
3750 _note_group->reparent (group);
3751 delete _temporary_note_group;
3752 _temporary_note_group = 0;
3754 if (_region->start() < 0) {
3755 /* Trim drag made start time -ve; fix this */
3756 midi_region()->fix_negative_start ();
3761 MidiRegionView::edit_patch_change (PatchChange* pc)
3763 PatchChangeDialog d (&_source_relative_time_converter, trackview.session(), *pc->patch (), instrument_info(), Gtk::Stock::APPLY, true);
3765 int response = d.run();
3768 case Gtk::RESPONSE_ACCEPT:
3770 case Gtk::RESPONSE_REJECT:
3771 delete_patch_change (pc);
3777 change_patch_change (pc->patch(), d.patch ());
3781 MidiRegionView::delete_sysex (SysEx* /*sysex*/)
3784 // sysyex object doesn't have a pointer to a sysex event
3785 // MidiModel::SysExDiffCommand* c = _model->new_sysex_diff_command (_("delete sysex"));
3786 // c->remove (sysex->sysex());
3787 // _model->apply_command (*trackview.session(), c);
3789 //_sys_exes.clear ();
3790 // display_sysexes();
3794 MidiRegionView::show_verbose_cursor (boost::shared_ptr<NoteType> n) const
3796 using namespace MIDI::Name;
3800 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3802 boost::shared_ptr<MasterDeviceNames> device_names(mtv->get_device_names());
3804 MIDI::Name::PatchPrimaryKey patch_key;
3805 get_patch_key_at(n->time(), n->channel(), patch_key);
3806 name = device_names->note_name(mtv->gui_property(X_("midnam-custom-device-mode")),
3808 patch_key.bank_number,
3809 patch_key.program_number,
3815 snprintf (buf, sizeof (buf), "%d %s\nCh %d Vel %d",
3817 name.empty() ? Evoral::midi_note_name (n->note()).c_str() : name.c_str(),
3818 (int) n->channel() + 1,
3819 (int) n->velocity());
3821 show_verbose_cursor(buf, 10, 20);
3825 MidiRegionView::show_verbose_cursor (string const & text, double xoffset, double yoffset) const
3829 trackview.editor().verbose_cursor()->set_text (text);
3830 trackview.editor().get_pointer_position (wx, wy);
3835 /* Flip the cursor above the mouse pointer if it would overlap the bottom of the canvas */
3837 boost::optional<ArdourCanvas::Rect> bbo = trackview.editor().verbose_cursor()->item().bounding_box();
3841 ArdourCanvas::Rect bb = bbo.get();
3843 if ((wy + bb.y1 - bb.y0) > trackview.editor().visible_canvas_height()) {
3844 wy -= (bb.y1 - bb.y0) + 2 * yoffset;
3847 trackview.editor().verbose_cursor()->set_position (wx, wy);
3848 trackview.editor().verbose_cursor()->show ();
3851 /** @param p A session framepos.
3852 * @param grid_frames Filled in with the number of frames that a grid interval is at p.
3853 * @return p snapped to the grid subdivision underneath it.
3856 MidiRegionView::snap_frame_to_grid_underneath (framepos_t p, framecnt_t& grid_frames) const
3858 PublicEditor& editor = trackview.editor ();
3861 Evoral::MusicalTime grid_beats = editor.get_grid_type_as_beats (success, p);
3867 grid_frames = region_beats_to_region_frames (grid_beats);
3869 /* Hack so that we always snap to the note that we are over, instead of snapping
3870 to the next one if we're more than halfway through the one we're over.
3872 if (editor.snap_mode() == SnapNormal && p >= grid_frames / 2) {
3873 p -= grid_frames / 2;
3876 return snap_frame_to_frame (p);
3879 /** Called when the selection has been cleared in any MidiRegionView.
3880 * @param rv MidiRegionView that the selection was cleared in.
3883 MidiRegionView::selection_cleared (MidiRegionView* rv)
3889 /* Clear our selection in sympathy; but don't signal the fact */
3890 clear_selection (false);
3894 MidiRegionView::note_button_release ()
3896 delete _note_player;
3901 MidiRegionView::get_channel_mode () const
3903 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (&trackview);
3904 return rtav->midi_track()->get_playback_channel_mode();
3908 MidiRegionView::get_selected_channels () const
3910 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (&trackview);
3911 return rtav->midi_track()->get_playback_channel_mask();