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/operations.h"
39 #include "ardour/session.h"
41 #include "evoral/Parameter.hpp"
42 #include "evoral/MIDIParameters.hpp"
43 #include "evoral/MIDIEvent.hpp"
44 #include "evoral/Control.hpp"
45 #include "evoral/midi_util.h"
47 #include "canvas/debug.h"
48 #include "canvas/text.h"
50 #include "automation_region_view.h"
51 #include "automation_time_axis.h"
54 #include "editor_drag.h"
55 #include "ghostregion.h"
56 #include "gui_thread.h"
58 #include "midi_channel_dialog.h"
59 #include "midi_cut_buffer.h"
60 #include "midi_list_editor.h"
61 #include "midi_region_view.h"
62 #include "midi_streamview.h"
63 #include "midi_time_axis.h"
64 #include "midi_util.h"
65 #include "midi_velocity_dialog.h"
66 #include "mouse_cursors.h"
67 #include "note_player.h"
68 #include "public_editor.h"
69 #include "route_time_axis.h"
70 #include "rgb_macros.h"
71 #include "selection.h"
72 #include "streamview.h"
73 #include "patch_change_dialog.h"
74 #include "verbose_cursor.h"
75 #include "ardour_ui.h"
78 #include "patch_change.h"
83 using namespace ARDOUR;
85 using namespace Editing;
86 using Gtkmm2ext::Keyboard;
88 PBD::Signal1<void, MidiRegionView *> MidiRegionView::SelectionCleared;
90 #define MIDI_BP_ZERO ((Config->get_first_midi_bank_is_zero())?0:1)
92 MidiRegionView::MidiRegionView (ArdourCanvas::Container *parent, RouteTimeAxisView &tv,
93 boost::shared_ptr<MidiRegion> r, double spu, uint32_t basic_color)
94 : RegionView (parent, tv, r, spu, basic_color)
95 , _current_range_min(0)
96 , _current_range_max(0)
98 , _note_group (new ArdourCanvas::Container (group))
99 , _note_diff_command (0)
101 , _step_edit_cursor (0)
102 , _step_edit_cursor_width (1.0)
103 , _step_edit_cursor_position (0.0)
104 , _channel_selection_scoped_note (0)
105 , _temporary_note_group (0)
108 , _sort_needed (true)
109 , _optimization_iterator (_events.end())
111 , _no_sound_notes (false)
114 , pre_enter_cursor (0)
115 , pre_press_cursor (0)
116 , pre_note_enter_cursor (0)
119 CANVAS_DEBUG_NAME (_note_group, string_compose ("note group for %1", get_item_name()));
120 _note_group->raise_to_top();
121 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
123 Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&MidiRegionView::parameter_changed, this, _1), gui_context());
124 connect_to_diskstream ();
126 SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), boost::bind (&MidiRegionView::selection_cleared, this, _1), gui_context ());
129 MidiRegionView::MidiRegionView (ArdourCanvas::Container *parent, RouteTimeAxisView &tv,
130 boost::shared_ptr<MidiRegion> r, double spu, uint32_t basic_color,
131 TimeAxisViewItem::Visibility visibility)
132 : RegionView (parent, tv, r, spu, basic_color, false, visibility)
133 , _current_range_min(0)
134 , _current_range_max(0)
136 , _note_group (new ArdourCanvas::Container (parent))
137 , _note_diff_command (0)
139 , _step_edit_cursor (0)
140 , _step_edit_cursor_width (1.0)
141 , _step_edit_cursor_position (0.0)
142 , _channel_selection_scoped_note (0)
143 , _temporary_note_group (0)
146 , _sort_needed (true)
147 , _optimization_iterator (_events.end())
149 , _no_sound_notes (false)
152 , pre_enter_cursor (0)
153 , pre_press_cursor (0)
154 , pre_note_enter_cursor (0)
157 CANVAS_DEBUG_NAME (_note_group, string_compose ("note group for %1", get_item_name()));
158 _note_group->raise_to_top();
160 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
162 connect_to_diskstream ();
164 SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), boost::bind (&MidiRegionView::selection_cleared, this, _1), gui_context ());
168 MidiRegionView::parameter_changed (std::string const & p)
170 if (p == "display-first-midi-bank-as-zero") {
171 if (_enable_display) {
177 MidiRegionView::MidiRegionView (const MidiRegionView& other)
178 : sigc::trackable(other)
180 , _current_range_min(0)
181 , _current_range_max(0)
183 , _note_group (new ArdourCanvas::Container (get_canvas_group()))
184 , _note_diff_command (0)
186 , _step_edit_cursor (0)
187 , _step_edit_cursor_width (1.0)
188 , _step_edit_cursor_position (0.0)
189 , _channel_selection_scoped_note (0)
190 , _temporary_note_group (0)
193 , _sort_needed (true)
194 , _optimization_iterator (_events.end())
196 , _no_sound_notes (false)
199 , pre_enter_cursor (0)
200 , pre_press_cursor (0)
201 , pre_note_enter_cursor (0)
207 MidiRegionView::MidiRegionView (const MidiRegionView& other, boost::shared_ptr<MidiRegion> region)
208 : RegionView (other, boost::shared_ptr<Region> (region))
209 , _current_range_min(0)
210 , _current_range_max(0)
212 , _note_group (new ArdourCanvas::Container (get_canvas_group()))
213 , _note_diff_command (0)
215 , _step_edit_cursor (0)
216 , _step_edit_cursor_width (1.0)
217 , _step_edit_cursor_position (0.0)
218 , _channel_selection_scoped_note (0)
219 , _temporary_note_group (0)
222 , _sort_needed (true)
223 , _optimization_iterator (_events.end())
225 , _no_sound_notes (false)
228 , pre_enter_cursor (0)
229 , pre_press_cursor (0)
230 , pre_note_enter_cursor (0)
237 MidiRegionView::init (bool wfd)
239 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
241 NoteBase::NoteBaseDeleted.connect (note_delete_connection, MISSING_INVALIDATOR,
242 boost::bind (&MidiRegionView::maybe_remove_deleted_note_from_selection, this, _1),
246 midi_region()->midi_source(0)->load_model();
249 _model = midi_region()->midi_source(0)->model();
250 _enable_display = false;
252 RegionView::init (false);
254 set_height (trackview.current_height());
257 region_sync_changed ();
258 region_resized (ARDOUR::bounds_change);
263 _enable_display = true;
266 display_model (_model);
270 reset_width_dependent_items (_pixel_width);
272 group->raise_to_top();
274 midi_view()->midi_track()->PlaybackChannelModeChanged.connect (_channel_mode_changed_connection, invalidator (*this),
275 boost::bind (&MidiRegionView::midi_channel_mode_changed, this),
278 instrument_info().Changed.connect (_instrument_changed_connection, invalidator (*this),
279 boost::bind (&MidiRegionView::instrument_settings_changed, this), gui_context());
281 trackview.editor().SnapChanged.connect(snap_changed_connection, invalidator(*this),
282 boost::bind (&MidiRegionView::snap_changed, this),
285 Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&MidiRegionView::parameter_changed, this, _1), gui_context());
286 connect_to_diskstream ();
288 SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), boost::bind (&MidiRegionView::selection_cleared, this, _1), gui_context ());
292 MidiRegionView::instrument_info () const
294 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
295 return route_ui->route()->instrument_info();
298 const boost::shared_ptr<ARDOUR::MidiRegion>
299 MidiRegionView::midi_region() const
301 return boost::dynamic_pointer_cast<ARDOUR::MidiRegion>(_region);
305 MidiRegionView::connect_to_diskstream ()
307 midi_view()->midi_track()->DataRecorded.connect(
308 *this, invalidator(*this),
309 boost::bind (&MidiRegionView::data_recorded, this, _1),
314 MidiRegionView::canvas_group_event(GdkEvent* ev)
323 case GDK_ENTER_NOTIFY:
324 case GDK_LEAVE_NOTIFY:
325 _last_event_x = ev->crossing.x;
326 _last_event_y = ev->crossing.y;
328 case GDK_MOTION_NOTIFY:
329 _last_event_x = ev->motion.x;
330 _last_event_y = ev->motion.y;
336 if (ev->type == GDK_2BUTTON_PRESS) {
337 // cannot use double-click to exit internal mode if single-click is being used
338 MouseMode m = trackview.editor().current_mouse_mode();
340 if ((m != MouseObject || !Keyboard::modifier_state_contains (ev->button.state, Keyboard::insert_note_modifier())) && (m != MouseDraw)) {
341 return trackview.editor().toggle_internal_editing_from_double_click (ev);
345 if ((!trackview.editor().internal_editing() && trackview.editor().current_mouse_mode() != MouseGain) ||
346 (trackview.editor().current_mouse_mode() == MouseTimeFX)) {
347 // handle non-internal-edit/non-draw modes elsewhere
348 return RegionView::canvas_group_event (ev);
353 if (scroll (&ev->scroll)) {
359 return key_press (&ev->key);
361 case GDK_KEY_RELEASE:
362 return key_release (&ev->key);
364 case GDK_BUTTON_PRESS:
365 return button_press (&ev->button);
367 case GDK_BUTTON_RELEASE:
368 r = button_release (&ev->button);
373 case GDK_ENTER_NOTIFY:
374 // set entered_regionview (among other things)
375 trackview.editor().canvas_region_view_event (ev, group, this);
376 return enter_notify (&ev->crossing);
378 case GDK_LEAVE_NOTIFY:
379 // reset entered_regionview (among other things)
380 trackview.editor().canvas_region_view_event (ev, group, this);
381 return leave_notify (&ev->crossing);
383 case GDK_MOTION_NOTIFY:
384 return motion (&ev->motion);
390 return trackview.editor().canvas_region_view_event (ev, group, this);
394 MidiRegionView::enter_notify (GdkEventCrossing* ev)
396 trackview.editor().MouseModeChanged.connect (
397 _mouse_mode_connection, invalidator (*this), boost::bind (&MidiRegionView::mouse_mode_changed, this), gui_context ()
400 if (trackview.editor().current_mouse_mode() == MouseDraw && _mouse_state != AddDragging) {
401 create_ghost_note (ev->x, ev->y);
404 if (!trackview.editor().internal_editing()) {
405 Keyboard::magic_widget_drop_focus();
407 Keyboard::magic_widget_grab_focus();
411 // if current operation is non-operational in a midi region, change the cursor to so indicate
412 if (trackview.editor().current_mouse_mode() == MouseGain) {
413 Editor* editor = dynamic_cast<Editor *> (&trackview.editor());
414 pre_enter_cursor = editor->get_canvas_cursor();
415 editor->set_canvas_cursor(editor->cursors()->timebar);
422 MidiRegionView::leave_notify (GdkEventCrossing*)
424 _mouse_mode_connection.disconnect ();
426 trackview.editor().verbose_cursor()->hide ();
427 remove_ghost_note ();
429 if (trackview.editor().internal_editing()) {
430 Keyboard::magic_widget_drop_focus();
433 if (pre_enter_cursor) {
434 Editor* editor = dynamic_cast<Editor *> (&trackview.editor());
435 editor->set_canvas_cursor(pre_enter_cursor);
436 pre_enter_cursor = 0;
443 MidiRegionView::mouse_mode_changed ()
445 if (trackview.editor().current_mouse_mode() == MouseDraw && trackview.editor().internal_editing()) {
446 create_ghost_note (_last_event_x, _last_event_y);
448 remove_ghost_note ();
449 trackview.editor().verbose_cursor()->hide ();
452 if (!trackview.editor().internal_editing()) {
453 Keyboard::magic_widget_drop_focus();
455 Keyboard::magic_widget_grab_focus();
461 MidiRegionView::button_press (GdkEventButton* ev)
463 if (ev->button != 1) {
467 Editor* editor = dynamic_cast<Editor *> (&trackview.editor());
468 MouseMode m = editor->current_mouse_mode();
470 if (m == MouseObject && Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())) {
471 pre_press_cursor = editor->get_canvas_cursor ();
472 editor->set_canvas_cursor (editor->cursors()->midi_pencil);
475 if (_mouse_state != SelectTouchDragging) {
477 _pressed_button = ev->button;
478 _mouse_state = Pressed;
483 _pressed_button = ev->button;
489 MidiRegionView::button_release (GdkEventButton* ev)
491 double event_x, event_y;
493 if (ev->button != 1) {
500 group->canvas_to_item (event_x, event_y);
503 PublicEditor& editor = trackview.editor ();
505 if (pre_press_cursor) {
506 dynamic_cast<Editor*>(&editor)->set_canvas_cursor (pre_press_cursor, false);
507 pre_press_cursor = 0;
510 switch (_mouse_state) {
511 case Pressed: // Clicked
513 switch (editor.current_mouse_mode()) {
515 /* no motion occured - simple click */
524 if (Keyboard::is_insert_note_event(ev)) {
526 double event_x, event_y;
530 group->canvas_to_item (event_x, event_y);
533 Evoral::MusicalTime beats = editor.get_grid_type_as_beats (success, editor.pixel_to_sample (event_x));
539 /* Shorten the length by 1 tick so that we can add a new note at the next
540 grid snap without it overlapping this one.
542 beats -= 1.0 / Timecode::BBT_Time::ticks_per_beat;
544 create_note_at (editor.pixel_to_sample (event_x), event_y, beats, true);
552 Evoral::MusicalTime beats = editor.get_grid_type_as_beats (success, editor.pixel_to_sample (event_x));
558 /* Shorten the length by 1 tick so that we can add a new note at the next
559 grid snap without it overlapping this one.
561 beats -= 1.0 / Timecode::BBT_Time::ticks_per_beat;
563 create_note_at (editor.pixel_to_sample (event_x), event_y, beats, true);
574 case SelectRectDragging:
576 editor.drags()->end_grab ((GdkEvent *) ev);
578 create_ghost_note (ev->x, ev->y);
590 MidiRegionView::motion (GdkEventMotion* ev)
592 PublicEditor& editor = trackview.editor ();
594 if (!_ghost_note && editor.current_mouse_mode() == MouseObject &&
595 Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier()) &&
596 _mouse_state != AddDragging) {
598 create_ghost_note (ev->x, ev->y);
600 } else if (_ghost_note && editor.current_mouse_mode() == MouseObject &&
601 Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())) {
603 update_ghost_note (ev->x, ev->y);
605 } else if (_ghost_note && editor.current_mouse_mode() == MouseObject) {
607 remove_ghost_note ();
608 editor.verbose_cursor()->hide ();
610 } else if (_ghost_note && editor.current_mouse_mode() == MouseDraw) {
612 update_ghost_note (ev->x, ev->y);
615 /* any motion immediately hides velocity text that may have been visible */
617 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
618 (*i)->hide_velocity ();
621 switch (_mouse_state) {
624 if (_pressed_button == 1) {
626 MouseMode m = editor.current_mouse_mode();
628 if (m == MouseDraw || (m == MouseObject && Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier()))) {
629 editor.drags()->set (new NoteCreateDrag (dynamic_cast<Editor *> (&editor), group, this), (GdkEvent *) ev);
630 _mouse_state = AddDragging;
631 remove_ghost_note ();
632 editor.verbose_cursor()->hide ();
634 } else if (m == MouseObject) {
635 editor.drags()->set (new MidiRubberbandSelectDrag (dynamic_cast<Editor *> (&editor), this), (GdkEvent *) ev);
637 _mouse_state = SelectRectDragging;
639 } else if (m == MouseRange) {
640 editor.drags()->set (new MidiVerticalSelectDrag (dynamic_cast<Editor *> (&editor), this), (GdkEvent *) ev);
641 _mouse_state = SelectVerticalDragging;
648 case SelectRectDragging:
649 case SelectVerticalDragging:
651 editor.drags()->motion_handler ((GdkEvent *) ev, false);
654 case SelectTouchDragging:
662 /* we may be dragging some non-note object (eg. patch-change, sysex)
665 return editor.drags()->motion_handler ((GdkEvent *) ev, false);
670 MidiRegionView::scroll (GdkEventScroll* ev)
672 if (_selection.empty()) {
676 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
677 /* XXX: bit of a hack; allow PrimaryModifier scroll through so that
678 it still works for zoom.
683 trackview.editor().verbose_cursor()->hide ();
685 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
686 bool together = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
688 if (ev->direction == GDK_SCROLL_UP) {
689 change_velocities (true, fine, false, together);
690 } else if (ev->direction == GDK_SCROLL_DOWN) {
691 change_velocities (false, fine, false, together);
693 /* left, right: we don't use them */
701 MidiRegionView::key_press (GdkEventKey* ev)
703 /* since GTK bindings are generally activated on press, and since
704 detectable auto-repeat is the name of the game and only sends
705 repeated presses, carry out key actions at key press, not release.
708 bool unmodified = Keyboard::no_modifier_keys_pressed (ev);
710 if (unmodified && (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R)) {
711 _mouse_state = SelectTouchDragging;
714 } else if (ev->keyval == GDK_Escape && unmodified) {
718 } else if (unmodified && (ev->keyval == GDK_comma || ev->keyval == GDK_period)) {
720 bool start = (ev->keyval == GDK_comma);
721 bool end = (ev->keyval == GDK_period);
722 bool shorter = Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier);
723 bool fine = Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
725 change_note_lengths (fine, shorter, 0.0, start, end);
729 } else if ((ev->keyval == GDK_BackSpace || ev->keyval == GDK_Delete) && unmodified) {
731 if (_selection.empty()) {
738 } else if (ev->keyval == GDK_Tab) {
740 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
741 goto_previous_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
743 goto_next_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
747 } else if (ev->keyval == GDK_ISO_Left_Tab) {
749 /* Shift-TAB generates ISO Left Tab, for some reason */
751 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
752 goto_previous_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
754 goto_next_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
760 } else if (ev->keyval == GDK_Up) {
762 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
763 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
764 bool together = Keyboard::modifier_state_contains (ev->state, Keyboard::Level4Modifier);
766 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
767 change_velocities (true, fine, allow_smush, together);
769 transpose (true, fine, allow_smush);
773 } else if (ev->keyval == GDK_Down) {
775 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
776 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
777 bool together = Keyboard::modifier_state_contains (ev->state, Keyboard::Level4Modifier);
779 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
780 change_velocities (false, fine, allow_smush, together);
782 transpose (false, fine, allow_smush);
786 } else if (ev->keyval == GDK_Left && unmodified) {
791 } else if (ev->keyval == GDK_Right && unmodified) {
796 } else if (ev->keyval == GDK_c && unmodified) {
800 } else if (ev->keyval == GDK_v && unmodified) {
809 MidiRegionView::key_release (GdkEventKey* ev)
811 if ((_mouse_state == SelectTouchDragging) && (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R)) {
819 MidiRegionView::channel_edit ()
821 if (_selection.empty()) {
825 /* pick a note somewhat at random (since Selection is a set<>) to
826 * provide the "current" channel for the dialog.
829 uint8_t current_channel = (*_selection.begin())->note()->channel ();
830 MidiChannelDialog channel_dialog (current_channel);
831 int ret = channel_dialog.run ();
834 case Gtk::RESPONSE_OK:
840 uint8_t new_channel = channel_dialog.active_channel ();
842 start_note_diff_command (_("channel edit"));
844 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
845 Selection::iterator next = i;
847 change_note_channel (*i, new_channel);
855 MidiRegionView::velocity_edit ()
857 if (_selection.empty()) {
861 /* pick a note somewhat at random (since Selection is a set<>) to
862 * provide the "current" velocity for the dialog.
865 uint8_t current_velocity = (*_selection.begin())->note()->velocity ();
866 MidiVelocityDialog velocity_dialog (current_velocity);
867 int ret = velocity_dialog.run ();
870 case Gtk::RESPONSE_OK:
876 uint8_t new_velocity = velocity_dialog.velocity ();
878 start_note_diff_command (_("velocity edit"));
880 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
881 Selection::iterator next = i;
883 change_note_velocity (*i, new_velocity, false);
891 MidiRegionView::show_list_editor ()
894 _list_editor = new MidiListEditor (trackview.session(), midi_region(), midi_view()->midi_track());
896 _list_editor->present ();
899 /** Add a note to the model, and the view, at a canvas (click) coordinate.
900 * \param t time in frames relative to the position of the region
901 * \param y vertical position in pixels
902 * \param length duration of the note in beats
903 * \param snap_t true to snap t to the grid, otherwise false.
906 MidiRegionView::create_note_at (framepos_t t, double y, double length, bool snap_t)
908 if (length < 2 * DBL_EPSILON) {
912 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
913 MidiStreamView* const view = mtv->midi_view();
915 const double note = view->y_to_note(y);
917 // Start of note in frames relative to region start
919 framecnt_t grid_frames;
920 t = snap_frame_to_grid_underneath (t, grid_frames);
923 const boost::shared_ptr<NoteType> new_note (
924 new NoteType (mtv->get_channel_for_add (),
925 region_frames_to_region_beats(t + _region->start()),
927 (uint8_t)note, 0x40));
929 if (_model->contains (new_note)) {
933 view->update_note_range(new_note->note());
935 MidiModel::NoteDiffCommand* cmd = _model->new_note_diff_command(_("add note"));
937 _model->apply_command(*trackview.session(), cmd);
939 play_midi_note (new_note);
943 MidiRegionView::clear_events (bool with_selection_signal)
945 clear_selection (with_selection_signal);
948 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
949 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
954 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
959 _patch_changes.clear();
961 _optimization_iterator = _events.end();
965 MidiRegionView::display_model(boost::shared_ptr<MidiModel> model)
969 content_connection.disconnect ();
970 _model->ContentsChanged.connect (content_connection, invalidator (*this), boost::bind (&MidiRegionView::redisplay_model, this), gui_context());
974 if (_enable_display) {
980 MidiRegionView::start_note_diff_command (string name)
982 if (!_note_diff_command) {
983 _note_diff_command = _model->new_note_diff_command (name);
988 MidiRegionView::note_diff_add_note (const boost::shared_ptr<NoteType> note, bool selected, bool show_velocity)
990 if (_note_diff_command) {
991 _note_diff_command->add (note);
994 _marked_for_selection.insert(note);
997 _marked_for_velocity.insert(note);
1002 MidiRegionView::note_diff_remove_note (NoteBase* ev)
1004 if (_note_diff_command && ev->note()) {
1005 _note_diff_command->remove(ev->note());
1010 MidiRegionView::note_diff_add_change (NoteBase* ev,
1011 MidiModel::NoteDiffCommand::Property property,
1014 if (_note_diff_command) {
1015 _note_diff_command->change (ev->note(), property, val);
1020 MidiRegionView::note_diff_add_change (NoteBase* ev,
1021 MidiModel::NoteDiffCommand::Property property,
1022 Evoral::MusicalTime val)
1024 if (_note_diff_command) {
1025 _note_diff_command->change (ev->note(), property, val);
1030 MidiRegionView::apply_diff (bool as_subcommand)
1034 if (!_note_diff_command) {
1038 if ((add_or_remove = _note_diff_command->adds_or_removes())) {
1039 // Mark all selected notes for selection when model reloads
1040 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1041 _marked_for_selection.insert((*i)->note());
1045 if (as_subcommand) {
1046 _model->apply_command_as_subcommand (*trackview.session(), _note_diff_command);
1048 _model->apply_command (*trackview.session(), _note_diff_command);
1051 _note_diff_command = 0;
1052 midi_view()->midi_track()->playlist_modified();
1054 if (add_or_remove) {
1055 _marked_for_selection.clear();
1058 _marked_for_velocity.clear();
1062 MidiRegionView::abort_command()
1064 delete _note_diff_command;
1065 _note_diff_command = 0;
1070 MidiRegionView::find_canvas_note (boost::shared_ptr<NoteType> note)
1072 if (_optimization_iterator != _events.end()) {
1073 ++_optimization_iterator;
1076 if (_optimization_iterator != _events.end() && (*_optimization_iterator)->note() == note) {
1077 return *_optimization_iterator;
1080 for (_optimization_iterator = _events.begin(); _optimization_iterator != _events.end(); ++_optimization_iterator) {
1081 if ((*_optimization_iterator)->note() == note) {
1082 return *_optimization_iterator;
1090 MidiRegionView::get_events (Events& e, Evoral::Sequence<Evoral::MusicalTime>::NoteOperator op, uint8_t val, int chan_mask)
1092 MidiModel::Notes notes;
1093 _model->get_notes (notes, op, val, chan_mask);
1095 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1096 NoteBase* cne = find_canvas_note (*n);
1104 MidiRegionView::redisplay_model()
1106 // Don't redisplay the model if we're currently recording and displaying that
1107 if (_active_notes) {
1115 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1116 (*i)->invalidate ();
1119 MidiModel::ReadLock lock(_model->read_lock());
1121 MidiModel::Notes& notes (_model->notes());
1122 _optimization_iterator = _events.begin();
1124 bool empty_when_starting = _events.empty();
1126 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1128 boost::shared_ptr<NoteType> note (*n);
1132 if (note_in_region_range (note, visible)) {
1134 if (!empty_when_starting && (cne = find_canvas_note (note)) != 0) {
1141 if ((cn = dynamic_cast<Note*>(cne)) != 0) {
1143 } else if ((ch = dynamic_cast<Hit*>(cne)) != 0) {
1155 add_note (note, visible);
1160 if (!empty_when_starting && (cne = find_canvas_note (note)) != 0) {
1168 /* remove note items that are no longer valid */
1170 if (!empty_when_starting) {
1171 for (Events::iterator i = _events.begin(); i != _events.end(); ) {
1172 if (!(*i)->valid ()) {
1174 for (vector<GhostRegion*>::iterator j = ghosts.begin(); j != ghosts.end(); ++j) {
1175 MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*j);
1177 gr->remove_note (*i);
1182 i = _events.erase (i);
1190 _patch_changes.clear();
1194 display_patch_changes ();
1196 _marked_for_selection.clear ();
1197 _marked_for_velocity.clear ();
1199 /* we may have caused _events to contain things out of order (e.g. if a note
1200 moved earlier or later). we don't generally need them in time order, but
1201 make a note that a sort is required for those cases that require it.
1204 _sort_needed = true;
1208 MidiRegionView::display_patch_changes ()
1210 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1211 uint16_t chn_mask = mtv->midi_track()->get_playback_channel_mask();
1213 for (uint8_t i = 0; i < 16; ++i) {
1214 display_patch_changes_on_channel (i, chn_mask & (1 << i));
1218 /** @param active_channel true to display patch changes fully, false to display
1219 * them `greyed-out' (as on an inactive channel)
1222 MidiRegionView::display_patch_changes_on_channel (uint8_t channel, bool active_channel)
1224 for (MidiModel::PatchChanges::const_iterator i = _model->patch_changes().begin(); i != _model->patch_changes().end(); ++i) {
1226 if ((*i)->channel() != channel) {
1230 const string patch_name = instrument_info().get_patch_name ((*i)->bank(), (*i)->program(), channel);
1231 add_canvas_patch_change (*i, patch_name, active_channel);
1236 MidiRegionView::display_sysexes()
1238 bool have_periodic_system_messages = false;
1239 bool display_periodic_messages = true;
1241 if (!Config->get_never_display_periodic_midi()) {
1243 for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
1244 const boost::shared_ptr<const Evoral::MIDIEvent<Evoral::MusicalTime> > mev =
1245 boost::static_pointer_cast<const Evoral::MIDIEvent<Evoral::MusicalTime> > (*i);
1248 if (mev->is_spp() || mev->is_mtc_quarter() || mev->is_mtc_full()) {
1249 have_periodic_system_messages = true;
1255 if (have_periodic_system_messages) {
1256 double zoom = trackview.editor().get_current_zoom (); // frames per pixel
1258 /* get an approximate value for the number of samples per video frame */
1260 double video_frame = trackview.session()->frame_rate() * (1.0/30);
1262 /* if we are zoomed out beyond than the cutoff (i.e. more
1263 * frames per pixel than frames per 4 video frames), don't
1264 * show periodic sysex messages.
1267 if (zoom > (video_frame*4)) {
1268 display_periodic_messages = false;
1272 display_periodic_messages = false;
1275 for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
1277 const boost::shared_ptr<const Evoral::MIDIEvent<Evoral::MusicalTime> > mev =
1278 boost::static_pointer_cast<const Evoral::MIDIEvent<Evoral::MusicalTime> > (*i);
1280 Evoral::MusicalTime time = (*i)->time();
1283 if (mev->is_spp() || mev->is_mtc_quarter() || mev->is_mtc_full()) {
1284 if (!display_periodic_messages) {
1292 for (uint32_t b = 0; b < (*i)->size(); ++b) {
1293 str << int((*i)->buffer()[b]);
1294 if (b != (*i)->size() -1) {
1298 string text = str.str();
1300 const double x = trackview.editor().sample_to_pixel(source_beats_to_region_frames(time));
1302 double height = midi_stream_view()->contents_height();
1304 // CAIROCANVAS: no longer passing *i (the sysex event) to the
1305 // SysEx canvas object!!!
1307 boost::shared_ptr<SysEx> sysex = boost::shared_ptr<SysEx>(
1308 new SysEx (*this, _note_group, text, height, x, 1.0));
1310 // Show unless message is beyond the region bounds
1311 if (time - _region->start() >= _region->length() || time < _region->start()) {
1317 _sys_exes.push_back(sysex);
1321 MidiRegionView::~MidiRegionView ()
1323 in_destructor = true;
1325 trackview.editor().verbose_cursor()->hide ();
1327 note_delete_connection.disconnect ();
1329 delete _list_editor;
1331 RegionViewGoingAway (this); /* EMIT_SIGNAL */
1333 if (_active_notes) {
1337 _selection_cleared_connection.disconnect ();
1340 clear_events (false);
1343 delete _note_diff_command;
1344 delete _step_edit_cursor;
1345 delete _temporary_note_group;
1349 MidiRegionView::region_resized (const PropertyChange& what_changed)
1351 RegionView::region_resized(what_changed);
1353 if (what_changed.contains (ARDOUR::Properties::position)) {
1354 set_duration(_region->length(), 0);
1355 if (_enable_display) {
1362 MidiRegionView::reset_width_dependent_items (double pixel_width)
1364 RegionView::reset_width_dependent_items(pixel_width);
1366 if (_enable_display) {
1370 for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
1371 if ((*x)->canvas_item()->width() >= _pixel_width) {
1378 move_step_edit_cursor (_step_edit_cursor_position);
1379 set_step_edit_cursor_width (_step_edit_cursor_width);
1383 MidiRegionView::set_height (double height)
1385 double old_height = _height;
1386 RegionView::set_height(height);
1388 apply_note_range (midi_stream_view()->lowest_note(),
1389 midi_stream_view()->highest_note(),
1390 height != old_height);
1393 name_text->raise_to_top();
1396 for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
1397 (*x)->set_height (midi_stream_view()->contents_height());
1400 if (_step_edit_cursor) {
1401 _step_edit_cursor->set_y1 (midi_stream_view()->contents_height());
1406 /** Apply the current note range from the stream view
1407 * by repositioning/hiding notes as necessary
1410 MidiRegionView::apply_note_range (uint8_t min, uint8_t max, bool force)
1412 if (!_enable_display) {
1416 if (!force && _current_range_min == min && _current_range_max == max) {
1420 _current_range_min = min;
1421 _current_range_max = max;
1423 for (Events::const_iterator i = _events.begin(); i != _events.end(); ++i) {
1424 NoteBase* event = *i;
1425 boost::shared_ptr<NoteType> note (event->note());
1427 if (note->note() < _current_range_min ||
1428 note->note() > _current_range_max) {
1434 if (Note* cnote = dynamic_cast<Note*>(event)) {
1436 const double y0 = 1. + floor (midi_stream_view()->note_to_y(note->note()));
1437 const double y1 = y0 + std::max(1., floor(midi_stream_view()->note_height()) - 1.);
1442 } else if (Hit* chit = dynamic_cast<Hit*>(event)) {
1449 MidiRegionView::add_ghost (TimeAxisView& tv)
1453 double unit_position = _region->position () / samples_per_pixel;
1454 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(&tv);
1455 MidiGhostRegion* ghost;
1457 if (mtv && mtv->midi_view()) {
1458 /* if ghost is inserted into midi track, use a dedicated midi ghost canvas group
1459 to allow having midi notes on top of note lines and waveforms.
1461 ghost = new MidiGhostRegion (*mtv->midi_view(), trackview, unit_position);
1463 ghost = new MidiGhostRegion (tv, trackview, unit_position);
1466 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1467 if ((note = dynamic_cast<Note*>(*i)) != 0) {
1468 ghost->add_note(note);
1472 ghost->set_height ();
1473 ghost->set_duration (_region->length() / samples_per_pixel);
1474 ghosts.push_back (ghost);
1476 GhostRegion::CatchDeletion.connect (*this, invalidator (*this), boost::bind (&RegionView::remove_ghost, this, _1), gui_context());
1482 /** Begin tracking note state for successive calls to add_event
1485 MidiRegionView::begin_write()
1487 if (_active_notes) {
1488 delete[] _active_notes;
1490 _active_notes = new Note*[128];
1491 for (unsigned i = 0; i < 128; ++i) {
1492 _active_notes[i] = 0;
1497 /** Destroy note state for add_event
1500 MidiRegionView::end_write()
1502 delete[] _active_notes;
1504 _marked_for_selection.clear();
1505 _marked_for_velocity.clear();
1509 /** Resolve an active MIDI note (while recording).
1512 MidiRegionView::resolve_note(uint8_t note, double end_time)
1514 if (midi_view()->note_mode() != Sustained) {
1518 if (_active_notes && _active_notes[note]) {
1520 /* XXX is end_time really region-centric? I think so, because
1521 this is a new region that we're recording, so source zero is
1522 the same as region zero
1524 const framepos_t end_time_frames = region_beats_to_region_frames(end_time);
1526 _active_notes[note]->set_x1 (trackview.editor().sample_to_pixel(end_time_frames));
1527 _active_notes[note]->set_outline_all ();
1528 _active_notes[note] = 0;
1534 /** Extend active notes to rightmost edge of region (if length is changed)
1537 MidiRegionView::extend_active_notes()
1539 if (!_active_notes) {
1543 for (unsigned i=0; i < 128; ++i) {
1544 if (_active_notes[i]) {
1545 _active_notes[i]->set_x1 (trackview.editor().sample_to_pixel(_region->length()));
1552 MidiRegionView::play_midi_note(boost::shared_ptr<NoteType> note)
1554 if (_no_sound_notes || !Config->get_sound_midi_notes()) {
1558 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1560 if (!route_ui || !route_ui->midi_track()) {
1564 NotePlayer* np = new NotePlayer (route_ui->midi_track ());
1568 /* NotePlayer deletes itself */
1572 MidiRegionView::start_playing_midi_note(boost::shared_ptr<NoteType> note)
1574 if (_no_sound_notes || !Config->get_sound_midi_notes()) {
1578 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1580 if (!route_ui || !route_ui->midi_track()) {
1584 delete _note_player;
1585 _note_player = new NotePlayer (route_ui->midi_track ());
1586 _note_player->add (note);
1587 _note_player->on ();
1591 MidiRegionView::start_playing_midi_chord (vector<boost::shared_ptr<NoteType> > notes)
1593 if (_no_sound_notes || !Config->get_sound_midi_notes()) {
1597 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1599 if (!route_ui || !route_ui->midi_track()) {
1603 delete _note_player;
1604 _note_player = new NotePlayer (route_ui->midi_track());
1606 for (vector<boost::shared_ptr<NoteType> >::iterator n = notes.begin(); n != notes.end(); ++n) {
1607 _note_player->add (*n);
1610 _note_player->on ();
1615 MidiRegionView::note_in_region_range (const boost::shared_ptr<NoteType> note, bool& visible) const
1617 const framepos_t note_start_frames = source_beats_to_region_frames (note->time());
1618 bool outside = (note_start_frames < 0) || (note_start_frames > _region->last_frame());
1620 visible = (note->note() >= midi_stream_view()->lowest_note()) &&
1621 (note->note() <= midi_stream_view()->highest_note());
1626 /** Update a canvas note's size from its model note.
1627 * @param ev Canvas note to update.
1628 * @param update_ghost_regions true to update the note in any ghost regions that we have, otherwise false.
1631 MidiRegionView::update_note (Note* ev, bool update_ghost_regions)
1633 boost::shared_ptr<NoteType> note = ev->note();
1634 const double x = trackview.editor().sample_to_pixel (source_beats_to_region_frames (note->time()));
1635 const double y0 = 1 + floor(midi_stream_view()->note_to_y(note->note()));
1640 /* trim note display to not overlap the end of its region */
1642 if (note->length() > 0) {
1643 const framepos_t note_end_frames = min (source_beats_to_region_frames (note->end_time()), _region->length());
1644 ev->set_x1 (trackview.editor().sample_to_pixel (note_end_frames));
1646 ev->set_x1 (trackview.editor().sample_to_pixel (_region->length()));
1649 ev->set_y1 (y0 + std::max(1., floor(midi_stream_view()->note_height()) - 1));
1651 if (note->length() == 0) {
1652 if (_active_notes && note->note() < 128) {
1653 // If this note is already active there's a stuck note,
1654 // finish the old note rectangle
1655 if (_active_notes[note->note()]) {
1656 Note* const old_rect = _active_notes[note->note()];
1657 boost::shared_ptr<NoteType> old_note = old_rect->note();
1658 old_rect->set_x1 (x);
1659 old_rect->set_outline_all ();
1661 _active_notes[note->note()] = ev;
1663 /* outline all but right edge */
1664 ev->set_outline_what (ArdourCanvas::Rectangle::What (
1665 ArdourCanvas::Rectangle::TOP|
1666 ArdourCanvas::Rectangle::LEFT|
1667 ArdourCanvas::Rectangle::BOTTOM));
1669 /* outline all edges */
1670 ev->set_outline_all ();
1673 if (update_ghost_regions) {
1674 for (std::vector<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
1675 MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*i);
1677 gr->update_note (ev);
1684 MidiRegionView::update_hit (Hit* ev)
1686 boost::shared_ptr<NoteType> note = ev->note();
1688 const framepos_t note_start_frames = source_beats_to_region_frames(note->time());
1689 const double x = trackview.editor().sample_to_pixel(note_start_frames);
1690 const double diamond_size = std::max(1., floor(midi_stream_view()->note_height()) - 2.);
1691 const double y = 1.5 + floor(midi_stream_view()->note_to_y(note->note())) + diamond_size * .5;
1693 ev->set_position (ArdourCanvas::Duple (x, y));
1694 ev->set_height (diamond_size);
1697 /** Add a MIDI note to the view (with length).
1699 * If in sustained mode, notes with length 0 will be considered active
1700 * notes, and resolve_note should be called when the corresponding note off
1701 * event arrives, to properly display the note.
1704 MidiRegionView::add_note(const boost::shared_ptr<NoteType> note, bool visible)
1706 NoteBase* event = 0;
1708 if (midi_view()->note_mode() == Sustained) {
1710 Note* ev_rect = new Note (*this, _note_group, note);
1712 update_note (ev_rect);
1716 MidiGhostRegion* gr;
1718 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
1719 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
1720 gr->add_note(ev_rect);
1724 } else if (midi_view()->note_mode() == Percussive) {
1726 const double diamond_size = std::max(1., floor(midi_stream_view()->note_height()) - 2.);
1728 Hit* ev_diamond = new Hit (*this, _note_group, diamond_size, note);
1730 update_hit (ev_diamond);
1739 if (_marked_for_selection.find(note) != _marked_for_selection.end()) {
1740 note_selected(event, true);
1743 if (_marked_for_velocity.find(note) != _marked_for_velocity.end()) {
1744 event->show_velocity();
1747 event->on_channel_selection_change (get_selected_channels());
1748 _events.push_back(event);
1757 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1758 MidiStreamView* const view = mtv->midi_view();
1760 view->update_note_range (note->note());
1764 MidiRegionView::step_add_note (uint8_t channel, uint8_t number, uint8_t velocity,
1765 Evoral::MusicalTime pos, Evoral::MusicalTime len)
1767 boost::shared_ptr<NoteType> new_note (new NoteType (channel, pos, len, number, velocity));
1769 /* potentially extend region to hold new note */
1771 framepos_t end_frame = source_beats_to_absolute_frames (new_note->end_time());
1772 framepos_t region_end = _region->last_frame();
1774 if (end_frame > region_end) {
1775 _region->set_length (end_frame - _region->position());
1778 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1779 MidiStreamView* const view = mtv->midi_view();
1781 view->update_note_range(new_note->note());
1783 _marked_for_selection.clear ();
1786 start_note_diff_command (_("step add"));
1787 note_diff_add_note (new_note, true, false);
1790 // last_step_edit_note = new_note;
1794 MidiRegionView::step_sustain (Evoral::MusicalTime beats)
1796 change_note_lengths (false, false, beats, false, true);
1799 /** Add a new patch change flag to the canvas.
1800 * @param patch the patch change to add
1801 * @param the text to display in the flag
1802 * @param active_channel true to display the flag as on an active channel, false to grey it out for an inactive channel.
1805 MidiRegionView::add_canvas_patch_change (MidiModel::PatchChangePtr patch, const string& displaytext, bool /*active_channel*/)
1807 framecnt_t region_frames = source_beats_to_region_frames (patch->time());
1808 const double x = trackview.editor().sample_to_pixel (region_frames);
1810 double const height = midi_stream_view()->contents_height();
1812 // CAIROCANVAS: active_channel info removed from PatcChange constructor
1813 // so we need to do something more sophisticated to keep its color
1814 // appearance (MidiPatchChangeFill/MidiPatchChangeInactiveChannelFill)
1817 boost::shared_ptr<PatchChange> patch_change = boost::shared_ptr<PatchChange>(
1818 new PatchChange(*this, group,
1825 if (patch_change->item().width() < _pixel_width) {
1826 // Show unless patch change is beyond the region bounds
1827 if (region_frames < 0 || region_frames >= _region->length()) {
1828 patch_change->hide();
1830 patch_change->show();
1833 patch_change->hide ();
1836 _patch_changes.push_back (patch_change);
1839 MIDI::Name::PatchPrimaryKey
1840 MidiRegionView::patch_change_to_patch_key (MidiModel::PatchChangePtr p)
1842 return MIDI::Name::PatchPrimaryKey (p->program(), p->bank());
1845 /// Return true iff @p pc applies to the given time on the given channel.
1847 patch_applies (const ARDOUR::MidiModel::constPatchChangePtr pc, double time, uint8_t channel)
1849 return pc->time() <= time && pc->channel() == channel;
1853 MidiRegionView::get_patch_key_at (double time, uint8_t channel, MIDI::Name::PatchPrimaryKey& key) const
1855 // The earliest event not before time
1856 MidiModel::PatchChanges::iterator i = _model->patch_change_lower_bound (time);
1858 // Go backwards until we find the latest PC for this channel, or the start
1859 while (i != _model->patch_changes().begin() &&
1860 (i == _model->patch_changes().end() ||
1861 !patch_applies(*i, time, channel))) {
1865 if (i != _model->patch_changes().end() && patch_applies(*i, time, channel)) {
1866 key.bank_number = (*i)->bank();
1867 key.program_number = (*i)->program ();
1869 key.bank_number = key.program_number = 0;
1872 if (!key.is_sane()) {
1873 error << string_compose(_("insane MIDI patch key %1:%2"),
1874 key.bank_number, key.program_number) << endmsg;
1879 MidiRegionView::change_patch_change (PatchChange& pc, const MIDI::Name::PatchPrimaryKey& new_patch)
1881 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("alter patch change"));
1883 if (pc.patch()->program() != new_patch.program_number) {
1884 c->change_program (pc.patch (), new_patch.program_number);
1887 int const new_bank = new_patch.bank_number;
1888 if (pc.patch()->bank() != new_bank) {
1889 c->change_bank (pc.patch (), new_bank);
1892 _model->apply_command (*trackview.session(), c);
1894 _patch_changes.clear ();
1895 display_patch_changes ();
1899 MidiRegionView::change_patch_change (MidiModel::PatchChangePtr old_change, const Evoral::PatchChange<Evoral::MusicalTime> & new_change)
1901 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("alter patch change"));
1903 if (old_change->time() != new_change.time()) {
1904 c->change_time (old_change, new_change.time());
1907 if (old_change->channel() != new_change.channel()) {
1908 c->change_channel (old_change, new_change.channel());
1911 if (old_change->program() != new_change.program()) {
1912 c->change_program (old_change, new_change.program());
1915 if (old_change->bank() != new_change.bank()) {
1916 c->change_bank (old_change, new_change.bank());
1919 _model->apply_command (*trackview.session(), c);
1921 _patch_changes.clear ();
1922 display_patch_changes ();
1925 /** Add a patch change to the region.
1926 * @param t Time in frames relative to region position
1927 * @param patch Patch to add; time and channel are ignored (time is converted from t, and channel comes from
1928 * MidiTimeAxisView::get_channel_for_add())
1931 MidiRegionView::add_patch_change (framecnt_t t, Evoral::PatchChange<Evoral::MusicalTime> const & patch)
1933 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1935 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("add patch change"));
1936 c->add (MidiModel::PatchChangePtr (
1937 new Evoral::PatchChange<Evoral::MusicalTime> (
1938 absolute_frames_to_source_beats (_region->position() + t),
1939 mtv->get_channel_for_add(), patch.program(), patch.bank()
1944 _model->apply_command (*trackview.session(), c);
1946 _patch_changes.clear ();
1947 display_patch_changes ();
1951 MidiRegionView::move_patch_change (PatchChange& pc, Evoral::MusicalTime t)
1953 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("move patch change"));
1954 c->change_time (pc.patch (), t);
1955 _model->apply_command (*trackview.session(), c);
1957 _patch_changes.clear ();
1958 display_patch_changes ();
1962 MidiRegionView::delete_patch_change (PatchChange* pc)
1964 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("delete patch change"));
1965 c->remove (pc->patch ());
1966 _model->apply_command (*trackview.session(), c);
1968 _patch_changes.clear ();
1969 display_patch_changes ();
1973 MidiRegionView::previous_patch (PatchChange& patch)
1975 if (patch.patch()->program() < 127) {
1976 MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch());
1977 key.program_number++;
1978 change_patch_change (patch, key);
1983 MidiRegionView::next_patch (PatchChange& patch)
1985 if (patch.patch()->program() > 0) {
1986 MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch());
1987 key.program_number--;
1988 change_patch_change (patch, key);
1993 MidiRegionView::next_bank (PatchChange& patch)
1995 if (patch.patch()->program() < 127) {
1996 MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch());
1997 if (key.bank_number > 0) {
1999 change_patch_change (patch, key);
2005 MidiRegionView::previous_bank (PatchChange& patch)
2007 if (patch.patch()->program() > 0) {
2008 MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch());
2009 if (key.bank_number < 127) {
2011 change_patch_change (patch, key);
2017 MidiRegionView::maybe_remove_deleted_note_from_selection (NoteBase* cne)
2019 if (_selection.empty()) {
2023 _selection.erase (cne);
2027 MidiRegionView::delete_selection()
2029 if (_selection.empty()) {
2033 start_note_diff_command (_("delete selection"));
2035 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2036 if ((*i)->selected()) {
2037 _note_diff_command->remove((*i)->note());
2047 MidiRegionView::delete_note (boost::shared_ptr<NoteType> n)
2049 start_note_diff_command (_("delete note"));
2050 _note_diff_command->remove (n);
2053 trackview.editor().verbose_cursor()->hide ();
2057 MidiRegionView::clear_selection_except (NoteBase* ev, bool signal)
2059 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2061 Selection::iterator tmp = i;
2064 (*i)->set_selected (false);
2065 (*i)->hide_velocity ();
2066 _selection.erase (i);
2074 /* this does not change the status of this regionview w.r.t the editor
2079 SelectionCleared (this); /* EMIT SIGNAL */
2084 MidiRegionView::unique_select(NoteBase* ev)
2086 clear_selection_except (ev);
2088 /* don't bother with checking to see if we should remove this
2089 regionview from the editor selection, since we're about to add
2090 another note, and thus put/keep this regionview in the editor
2094 if (!ev->selected()) {
2095 add_to_selection (ev);
2100 MidiRegionView::select_all_notes ()
2104 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2105 add_to_selection (*i);
2110 MidiRegionView::select_range (framepos_t start, framepos_t end)
2114 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2115 framepos_t t = source_beats_to_absolute_frames((*i)->note()->time());
2116 if (t >= start && t <= end) {
2117 add_to_selection (*i);
2123 MidiRegionView::invert_selection ()
2125 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2126 if ((*i)->selected()) {
2127 remove_from_selection(*i);
2129 add_to_selection (*i);
2135 MidiRegionView::select_matching_notes (uint8_t notenum, uint16_t channel_mask, bool add, bool extend)
2137 bool have_selection = !_selection.empty();
2138 uint8_t low_note = 127;
2139 uint8_t high_note = 0;
2140 MidiModel::Notes& notes (_model->notes());
2141 _optimization_iterator = _events.begin();
2143 if (extend && !have_selection) {
2147 /* scan existing selection to get note range */
2149 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2150 if ((*i)->note()->note() < low_note) {
2151 low_note = (*i)->note()->note();
2153 if ((*i)->note()->note() > high_note) {
2154 high_note = (*i)->note()->note();
2161 if (!extend && (low_note == high_note) && (high_note == notenum)) {
2162 /* only note previously selected is the one we are
2163 * reselecting. treat this as cancelling the selection.
2170 low_note = min (low_note, notenum);
2171 high_note = max (high_note, notenum);
2174 _no_sound_notes = true;
2176 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
2178 boost::shared_ptr<NoteType> note (*n);
2180 bool select = false;
2182 if (((1 << note->channel()) & channel_mask) != 0) {
2184 if ((note->note() >= low_note && note->note() <= high_note)) {
2187 } else if (note->note() == notenum) {
2193 if ((cne = find_canvas_note (note)) != 0) {
2194 // extend is false because we've taken care of it,
2195 // since it extends by time range, not pitch.
2196 note_selected (cne, add, false);
2200 add = true; // we need to add all remaining matching notes, even if the passed in value was false (for "set")
2204 _no_sound_notes = false;
2208 MidiRegionView::toggle_matching_notes (uint8_t notenum, uint16_t channel_mask)
2210 MidiModel::Notes& notes (_model->notes());
2211 _optimization_iterator = _events.begin();
2213 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
2215 boost::shared_ptr<NoteType> note (*n);
2218 if (note->note() == notenum && (((0x0001 << note->channel()) & channel_mask) != 0)) {
2219 if ((cne = find_canvas_note (note)) != 0) {
2220 if (cne->selected()) {
2221 note_deselected (cne);
2223 note_selected (cne, true, false);
2231 MidiRegionView::note_selected (NoteBase* ev, bool add, bool extend)
2234 clear_selection_except (ev);
2235 if (!_selection.empty()) {
2236 PublicEditor& editor (trackview.editor());
2237 editor.get_selection().add (this);
2243 if (!ev->selected()) {
2244 add_to_selection (ev);
2248 /* find end of latest note selected, select all between that and the start of "ev" */
2250 Evoral::MusicalTime earliest = Evoral::MaxMusicalTime;
2251 Evoral::MusicalTime latest = 0;
2253 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2254 if ((*i)->note()->end_time() > latest) {
2255 latest = (*i)->note()->end_time();
2257 if ((*i)->note()->time() < earliest) {
2258 earliest = (*i)->note()->time();
2262 if (ev->note()->end_time() > latest) {
2263 latest = ev->note()->end_time();
2266 if (ev->note()->time() < earliest) {
2267 earliest = ev->note()->time();
2270 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2272 /* find notes entirely within OR spanning the earliest..latest range */
2274 if (((*i)->note()->time() >= earliest && (*i)->note()->end_time() <= latest) ||
2275 ((*i)->note()->time() <= earliest && (*i)->note()->end_time() >= latest)) {
2276 add_to_selection (*i);
2284 MidiRegionView::note_deselected(NoteBase* ev)
2286 remove_from_selection (ev);
2290 MidiRegionView::update_drag_selection(double x0, double x1, double y0, double y1, bool extend)
2292 // TODO: Make this faster by storing the last updated selection rect, and only
2293 // adjusting things that are in the area that appears/disappeared.
2294 // We probably need a tree to be able to find events in O(log(n)) time.
2296 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2297 if ((*i)->x0() < x1 && (*i)->x1() > x0 && (*i)->y0() < y1 && (*i)->y1() > y0) {
2298 // Rectangles intersect
2299 if (!(*i)->selected()) {
2300 add_to_selection (*i);
2302 } else if ((*i)->selected() && !extend) {
2303 // Rectangles do not intersect
2304 remove_from_selection (*i);
2310 MidiRegionView::update_vertical_drag_selection (double y1, double y2, bool extend)
2316 // TODO: Make this faster by storing the last updated selection rect, and only
2317 // adjusting things that are in the area that appears/disappeared.
2318 // We probably need a tree to be able to find events in O(log(n)) time.
2320 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2321 if (((*i)->y1() >= y1 && (*i)->y1() <= y2)) {
2322 // within y- (note-) range
2323 if (!(*i)->selected()) {
2324 add_to_selection (*i);
2326 } else if ((*i)->selected() && !extend) {
2327 remove_from_selection (*i);
2333 MidiRegionView::remove_from_selection (NoteBase* ev)
2335 Selection::iterator i = _selection.find (ev);
2337 if (i != _selection.end()) {
2338 _selection.erase (i);
2341 ev->set_selected (false);
2342 ev->hide_velocity ();
2344 if (_selection.empty()) {
2345 PublicEditor& editor (trackview.editor());
2346 editor.get_selection().remove (this);
2351 MidiRegionView::add_to_selection (NoteBase* ev)
2353 bool add_mrv_selection = false;
2355 if (_selection.empty()) {
2356 add_mrv_selection = true;
2359 if (_selection.insert (ev).second) {
2360 ev->set_selected (true);
2361 start_playing_midi_note ((ev)->note());
2364 if (add_mrv_selection) {
2365 PublicEditor& editor (trackview.editor());
2366 editor.get_selection().add (this);
2371 MidiRegionView::move_selection(double dx, double dy, double cumulative_dy)
2373 typedef vector<boost::shared_ptr<NoteType> > PossibleChord;
2374 PossibleChord to_play;
2375 Evoral::MusicalTime earliest = Evoral::MaxMusicalTime;
2377 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2378 if ((*i)->note()->time() < earliest) {
2379 earliest = (*i)->note()->time();
2383 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2384 if (Evoral::musical_time_equal ((*i)->note()->time(), earliest)) {
2385 to_play.push_back ((*i)->note());
2387 (*i)->move_event(dx, dy);
2390 if (dy && !_selection.empty() && !_no_sound_notes && Config->get_sound_midi_notes()) {
2392 if (to_play.size() > 1) {
2394 PossibleChord shifted;
2396 for (PossibleChord::iterator n = to_play.begin(); n != to_play.end(); ++n) {
2397 boost::shared_ptr<NoteType> moved_note (new NoteType (**n));
2398 moved_note->set_note (moved_note->note() + cumulative_dy);
2399 shifted.push_back (moved_note);
2402 start_playing_midi_chord (shifted);
2404 } else if (!to_play.empty()) {
2406 boost::shared_ptr<NoteType> moved_note (new NoteType (*to_play.front()));
2407 moved_note->set_note (moved_note->note() + cumulative_dy);
2408 start_playing_midi_note (moved_note);
2414 MidiRegionView::note_dropped(NoteBase *, frameoffset_t dt, int8_t dnote)
2416 uint8_t lowest_note_in_selection = 127;
2417 uint8_t highest_note_in_selection = 0;
2418 uint8_t highest_note_difference = 0;
2420 // find highest and lowest notes first
2422 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2423 uint8_t pitch = (*i)->note()->note();
2424 lowest_note_in_selection = std::min(lowest_note_in_selection, pitch);
2425 highest_note_in_selection = std::max(highest_note_in_selection, pitch);
2429 cerr << "dnote: " << (int) dnote << endl;
2430 cerr << "lowest note (streamview): " << int(midi_stream_view()->lowest_note())
2431 << " highest note (streamview): " << int(midi_stream_view()->highest_note()) << endl;
2432 cerr << "lowest note (selection): " << int(lowest_note_in_selection) << " highest note(selection): "
2433 << int(highest_note_in_selection) << endl;
2434 cerr << "selection size: " << _selection.size() << endl;
2435 cerr << "Highest note in selection: " << (int) highest_note_in_selection << endl;
2438 // Make sure the note pitch does not exceed the MIDI standard range
2439 if (highest_note_in_selection + dnote > 127) {
2440 highest_note_difference = highest_note_in_selection - 127;
2443 start_note_diff_command (_("move notes"));
2445 for (Selection::iterator i = _selection.begin(); i != _selection.end() ; ++i) {
2447 framepos_t new_frames = source_beats_to_absolute_frames ((*i)->note()->time()) + dt;
2448 Evoral::MusicalTime new_time = absolute_frames_to_source_beats (new_frames);
2454 note_diff_add_change (*i, MidiModel::NoteDiffCommand::StartTime, new_time);
2456 uint8_t original_pitch = (*i)->note()->note();
2457 uint8_t new_pitch = original_pitch + dnote - highest_note_difference;
2459 // keep notes in standard midi range
2460 clamp_to_0_127(new_pitch);
2462 lowest_note_in_selection = std::min(lowest_note_in_selection, new_pitch);
2463 highest_note_in_selection = std::max(highest_note_in_selection, new_pitch);
2465 note_diff_add_change (*i, MidiModel::NoteDiffCommand::NoteNumber, new_pitch);
2470 // care about notes being moved beyond the upper/lower bounds on the canvas
2471 if (lowest_note_in_selection < midi_stream_view()->lowest_note() ||
2472 highest_note_in_selection > midi_stream_view()->highest_note()) {
2473 midi_stream_view()->set_note_range(MidiStreamView::ContentsRange);
2477 /** @param x Pixel relative to the region position.
2478 * @return Snapped frame relative to the region position.
2481 MidiRegionView::snap_pixel_to_sample(double x)
2483 PublicEditor& editor (trackview.editor());
2484 return snap_frame_to_frame (editor.pixel_to_sample (x));
2487 /** @param x Pixel relative to the region position.
2488 * @return Snapped pixel relative to the region position.
2491 MidiRegionView::snap_to_pixel(double x)
2493 return (double) trackview.editor().sample_to_pixel(snap_pixel_to_sample(x));
2497 MidiRegionView::get_position_pixels()
2499 framepos_t region_frame = get_position();
2500 return trackview.editor().sample_to_pixel(region_frame);
2504 MidiRegionView::get_end_position_pixels()
2506 framepos_t frame = get_position() + get_duration ();
2507 return trackview.editor().sample_to_pixel(frame);
2511 MidiRegionView::source_beats_to_absolute_frames(double beats) const
2513 /* the time converter will return the frame corresponding to `beats'
2514 relative to the start of the source. The start of the source
2515 is an implied position given by region->position - region->start
2517 const framepos_t source_start = _region->position() - _region->start();
2518 return source_start + _source_relative_time_converter.to (beats);
2522 MidiRegionView::absolute_frames_to_source_beats(framepos_t frames) const
2524 /* the `frames' argument needs to be converted into a frame count
2525 relative to the start of the source before being passed in to the
2528 const framepos_t source_start = _region->position() - _region->start();
2529 return _source_relative_time_converter.from (frames - source_start);
2533 MidiRegionView::region_beats_to_region_frames(double beats) const
2535 return _region_relative_time_converter.to(beats);
2539 MidiRegionView::region_frames_to_region_beats(framepos_t frames) const
2541 return _region_relative_time_converter.from(frames);
2545 MidiRegionView::begin_resizing (bool /*at_front*/)
2547 _resize_data.clear();
2549 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2550 Note *note = dynamic_cast<Note*> (*i);
2552 // only insert CanvasNotes into the map
2554 NoteResizeData *resize_data = new NoteResizeData();
2555 resize_data->note = note;
2557 // create a new SimpleRect from the note which will be the resize preview
2558 ArdourCanvas::Rectangle *resize_rect = new ArdourCanvas::Rectangle (_note_group,
2559 ArdourCanvas::Rect (note->x0(), note->y0(), note->x0(), note->y1()));
2561 // calculate the colors: get the color settings
2562 uint32_t fill_color = UINT_RGBA_CHANGE_A(
2563 ARDOUR_UI::config()->get_MidiNoteSelected(),
2566 // make the resize preview notes more transparent and bright
2567 fill_color = UINT_INTERPOLATE(fill_color, 0xFFFFFF40, 0.5);
2569 // calculate color based on note velocity
2570 resize_rect->set_fill_color (UINT_INTERPOLATE(
2571 NoteBase::meter_style_fill_color(note->note()->velocity(), note->selected()),
2575 resize_rect->set_outline_color (NoteBase::calculate_outline (
2576 ARDOUR_UI::config()->get_MidiNoteSelected()));
2578 resize_data->resize_rect = resize_rect;
2579 _resize_data.push_back(resize_data);
2584 /** Update resizing notes while user drags.
2585 * @param primary `primary' note for the drag; ie the one that is used as the reference in non-relative mode.
2586 * @param at_front which end of the note (true == note on, false == note off)
2587 * @param delta_x change in mouse position since the start of the drag
2588 * @param relative true if relative resizing is taking place, false if absolute resizing. This only makes
2589 * a difference when multiple notes are being resized; in relative mode, each note's length is changed by the
2590 * amount of the drag. In non-relative mode, all selected notes are set to have the same start or end point
2591 * as the \a primary note.
2594 MidiRegionView::update_resizing (NoteBase* primary, bool at_front, double delta_x, bool relative)
2596 bool cursor_set = false;
2598 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2599 ArdourCanvas::Rectangle* resize_rect = (*i)->resize_rect;
2600 Note* canvas_note = (*i)->note;
2605 current_x = canvas_note->x0() + delta_x;
2607 current_x = primary->x0() + delta_x;
2611 current_x = canvas_note->x1() + delta_x;
2613 current_x = primary->x1() + delta_x;
2617 if (current_x < 0) {
2618 // This works even with snapping because RegionView::snap_frame_to_frame()
2619 // snaps forward if the snapped sample is before the beginning of the region
2622 if (current_x > trackview.editor().sample_to_pixel(_region->length())) {
2623 current_x = trackview.editor().sample_to_pixel(_region->length());
2627 resize_rect->set_x0 (snap_to_pixel(current_x));
2628 resize_rect->set_x1 (canvas_note->x1());
2630 resize_rect->set_x1 (snap_to_pixel(current_x));
2631 resize_rect->set_x0 (canvas_note->x0());
2637 beats = snap_pixel_to_sample (current_x);
2638 beats = region_frames_to_region_beats (beats);
2643 if (beats < canvas_note->note()->end_time()) {
2644 len = canvas_note->note()->time() - beats;
2645 len += canvas_note->note()->length();
2650 if (beats >= canvas_note->note()->time()) {
2651 len = beats - canvas_note->note()->time();
2658 snprintf (buf, sizeof (buf), "%.3g beats", len);
2659 show_verbose_cursor (buf, 0, 0);
2668 /** Finish resizing notes when the user releases the mouse button.
2669 * Parameters the same as for \a update_resizing().
2672 MidiRegionView::commit_resizing (NoteBase* primary, bool at_front, double delta_x, bool relative)
2674 start_note_diff_command (_("resize notes"));
2676 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2677 Note* canvas_note = (*i)->note;
2678 ArdourCanvas::Rectangle* resize_rect = (*i)->resize_rect;
2680 /* Get the new x position for this resize, which is in pixels relative
2681 * to the region position.
2688 current_x = canvas_note->x0() + delta_x;
2690 current_x = primary->x0() + delta_x;
2694 current_x = canvas_note->x1() + delta_x;
2696 current_x = primary->x1() + delta_x;
2700 if (current_x < 0) {
2703 if (current_x > trackview.editor().sample_to_pixel(_region->length())) {
2704 current_x = trackview.editor().sample_to_pixel(_region->length());
2707 /* Convert that to a frame within the source */
2708 current_x = snap_pixel_to_sample (current_x) + _region->start ();
2710 /* and then to beats */
2711 current_x = region_frames_to_region_beats (current_x);
2713 if (at_front && current_x < canvas_note->note()->end_time()) {
2714 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::StartTime, current_x);
2716 double len = canvas_note->note()->time() - current_x;
2717 len += canvas_note->note()->length();
2720 /* XXX convert to beats */
2721 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
2726 double len = current_x - canvas_note->note()->time();
2729 /* XXX convert to beats */
2730 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
2738 _resize_data.clear();
2743 MidiRegionView::abort_resizing ()
2745 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2746 delete (*i)->resize_rect;
2750 _resize_data.clear ();
2754 MidiRegionView::change_note_velocity(NoteBase* event, int8_t velocity, bool relative)
2756 uint8_t new_velocity;
2759 new_velocity = event->note()->velocity() + velocity;
2760 clamp_to_0_127(new_velocity);
2762 new_velocity = velocity;
2765 event->set_selected (event->selected()); // change color
2767 note_diff_add_change (event, MidiModel::NoteDiffCommand::Velocity, new_velocity);
2771 MidiRegionView::change_note_note (NoteBase* event, int8_t note, bool relative)
2776 new_note = event->note()->note() + note;
2781 clamp_to_0_127 (new_note);
2782 note_diff_add_change (event, MidiModel::NoteDiffCommand::NoteNumber, new_note);
2786 MidiRegionView::trim_note (NoteBase* event, Evoral::MusicalTime front_delta, Evoral::MusicalTime end_delta)
2788 bool change_start = false;
2789 bool change_length = false;
2790 Evoral::MusicalTime new_start = 0;
2791 Evoral::MusicalTime new_length = 0;
2793 /* NOTE: the semantics of the two delta arguments are slightly subtle:
2795 front_delta: if positive - move the start of the note later in time (shortening it)
2796 if negative - move the start of the note earlier in time (lengthening it)
2798 end_delta: if positive - move the end of the note later in time (lengthening it)
2799 if negative - move the end of the note earlier in time (shortening it)
2803 if (front_delta < 0) {
2805 if (event->note()->time() < -front_delta) {
2808 new_start = event->note()->time() + front_delta; // moves earlier
2811 /* start moved toward zero, so move the end point out to where it used to be.
2812 Note that front_delta is negative, so this increases the length.
2815 new_length = event->note()->length() - front_delta;
2816 change_start = true;
2817 change_length = true;
2821 Evoral::MusicalTime new_pos = event->note()->time() + front_delta;
2823 if (new_pos < event->note()->end_time()) {
2824 new_start = event->note()->time() + front_delta;
2825 /* start moved toward the end, so move the end point back to where it used to be */
2826 new_length = event->note()->length() - front_delta;
2827 change_start = true;
2828 change_length = true;
2835 bool can_change = true;
2836 if (end_delta < 0) {
2837 if (event->note()->length() < -end_delta) {
2843 new_length = event->note()->length() + end_delta;
2844 change_length = true;
2849 note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_start);
2852 if (change_length) {
2853 note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, new_length);
2858 MidiRegionView::change_note_channel (NoteBase* event, int8_t chn, bool relative)
2860 uint8_t new_channel;
2864 if (event->note()->channel() < -chn) {
2867 new_channel = event->note()->channel() + chn;
2870 new_channel = event->note()->channel() + chn;
2873 new_channel = (uint8_t) chn;
2876 note_diff_add_change (event, MidiModel::NoteDiffCommand::Channel, new_channel);
2880 MidiRegionView::change_note_time (NoteBase* event, Evoral::MusicalTime delta, bool relative)
2882 Evoral::MusicalTime new_time;
2886 if (event->note()->time() < -delta) {
2889 new_time = event->note()->time() + delta;
2892 new_time = event->note()->time() + delta;
2898 note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_time);
2902 MidiRegionView::change_note_length (NoteBase* event, Evoral::MusicalTime t)
2904 note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, t);
2908 MidiRegionView::change_velocities (bool up, bool fine, bool allow_smush, bool all_together)
2913 if (_selection.empty()) {
2928 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2929 if ((*i)->note()->velocity() < -delta || (*i)->note()->velocity() + delta > 127) {
2935 start_note_diff_command (_("change velocities"));
2937 for (Selection::iterator i = _selection.begin(); i != _selection.end();) {
2938 Selection::iterator next = i;
2942 if (i == _selection.begin()) {
2943 change_note_velocity (*i, delta, true);
2944 value = (*i)->note()->velocity() + delta;
2946 change_note_velocity (*i, value, false);
2950 change_note_velocity (*i, delta, true);
2959 if (!_selection.empty()) {
2961 snprintf (buf, sizeof (buf), "Vel %d",
2962 (int) (*_selection.begin())->note()->velocity());
2963 show_verbose_cursor (buf, 10, 10);
2969 MidiRegionView::transpose (bool up, bool fine, bool allow_smush)
2971 if (_selection.empty()) {
2988 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2990 if ((int8_t) (*i)->note()->note() + delta <= 0) {
2994 if ((int8_t) (*i)->note()->note() + delta > 127) {
3001 start_note_diff_command (_("transpose"));
3003 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3004 Selection::iterator next = i;
3006 change_note_note (*i, delta, true);
3014 MidiRegionView::change_note_lengths (bool fine, bool shorter, Evoral::MusicalTime delta, bool start, bool end)
3020 /* grab the current grid distance */
3022 delta = trackview.editor().get_grid_type_as_beats (success, _region->position());
3024 /* XXX cannot get grid type as beats ... should always be possible ... FIX ME */
3025 error << string_compose (_("programming error: %1"), "Grid type not available as beats - TO BE FIXED") << endmsg;
3035 start_note_diff_command (_("change note lengths"));
3037 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3038 Selection::iterator next = i;
3041 /* note the negation of the delta for start */
3043 trim_note (*i, (start ? -delta : 0), (end ? delta : 0));
3052 MidiRegionView::nudge_notes (bool forward)
3054 if (_selection.empty()) {
3058 /* pick a note as the point along the timeline to get the nudge distance.
3059 its not necessarily the earliest note, so we may want to pull the notes out
3060 into a vector and sort before using the first one.
3063 framepos_t ref_point = source_beats_to_absolute_frames ((*(_selection.begin()))->note()->time());
3065 framecnt_t distance;
3067 if (trackview.editor().snap_mode() == Editing::SnapOff) {
3069 /* grid is off - use nudge distance */
3071 distance = trackview.editor().get_nudge_distance (ref_point, unused);
3077 framepos_t next_pos = ref_point;
3080 if (max_framepos - 1 < next_pos) {
3084 if (next_pos == 0) {
3090 trackview.editor().snap_to (next_pos, (forward ? RoundUpAlways : RoundDownAlways), false);
3091 distance = ref_point - next_pos;
3094 if (distance == 0) {
3098 Evoral::MusicalTime delta = region_frames_to_region_beats (fabs ((double)distance));
3104 start_note_diff_command (_("nudge"));
3106 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3107 Selection::iterator next = i;
3109 change_note_time (*i, delta, true);
3117 MidiRegionView::change_channel(uint8_t channel)
3119 start_note_diff_command(_("change channel"));
3120 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3121 note_diff_add_change (*i, MidiModel::NoteDiffCommand::Channel, channel);
3129 MidiRegionView::note_entered(NoteBase* ev)
3131 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3133 pre_note_enter_cursor = editor->get_canvas_cursor ();
3135 if (_mouse_state == SelectTouchDragging) {
3136 note_selected (ev, true);
3139 show_verbose_cursor (ev->note ());
3143 MidiRegionView::note_left (NoteBase*)
3145 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3147 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3148 (*i)->hide_velocity ();
3151 editor->verbose_cursor()->hide ();
3153 if (pre_note_enter_cursor) {
3154 editor->set_canvas_cursor (pre_note_enter_cursor);
3155 pre_note_enter_cursor = 0;
3160 MidiRegionView::patch_entered (PatchChange* p)
3163 /* XXX should get patch name if we can */
3164 s << _("Bank ") << (p->patch()->bank() + MIDI_BP_ZERO) << '\n'
3165 << _("Program ") << ((int) p->patch()->program()) + MIDI_BP_ZERO << '\n'
3166 << _("Channel ") << ((int) p->patch()->channel() + 1);
3167 show_verbose_cursor (s.str(), 10, 20);
3168 p->item().grab_focus();
3172 MidiRegionView::patch_left (PatchChange *)
3174 trackview.editor().verbose_cursor()->hide ();
3175 /* focus will transfer back via the enter-notify event sent to this
3181 MidiRegionView::sysex_entered (SysEx* p)
3185 // need a way to extract text from p->_flag->_text
3187 // show_verbose_cursor (s.str(), 10, 20);
3188 p->item().grab_focus();
3192 MidiRegionView::sysex_left (SysEx *)
3194 trackview.editor().verbose_cursor()->hide ();
3195 /* focus will transfer back via the enter-notify event sent to this
3201 MidiRegionView::note_mouse_position (float x_fraction, float /*y_fraction*/, bool can_set_cursor)
3203 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3204 Editing::MouseMode mm = editor->current_mouse_mode();
3205 bool trimmable = (mm == MouseObject || mm == MouseTimeFX || mm == MouseDraw);
3207 if (can_set_cursor) {
3208 if (trimmable && x_fraction > 0.0 && x_fraction < 0.2) {
3209 editor->set_canvas_cursor (editor->cursors()->left_side_trim);
3210 } else if (trimmable && x_fraction >= 0.8 && x_fraction < 1.0) {
3211 editor->set_canvas_cursor (editor->cursors()->right_side_trim);
3212 } else if (pre_note_enter_cursor) {
3213 editor->set_canvas_cursor (pre_note_enter_cursor);
3219 MidiRegionView::set_frame_color()
3223 TimeAxisViewItem::set_frame_color ();
3230 f = ARDOUR_UI::config()->get_SelectedFrameBase();
3231 } else if (high_enough_for_name) {
3232 f= ARDOUR_UI::config()->get_MidiFrameBase();
3237 if (!rect_visible) {
3238 f = UINT_RGBA_CHANGE_A (f, 80);
3241 frame->set_fill_color (f);
3245 MidiRegionView::midi_channel_mode_changed ()
3247 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3248 uint16_t mask = mtv->midi_track()->get_playback_channel_mask();
3249 ChannelMode mode = mtv->midi_track()->get_playback_channel_mode ();
3251 if (mode == ForceChannel) {
3252 mask = 0xFFFF; // Show all notes as active (below)
3255 // Update notes for selection
3256 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3257 (*i)->on_channel_selection_change (mask);
3260 _patch_changes.clear ();
3261 display_patch_changes ();
3265 MidiRegionView::instrument_settings_changed ()
3271 MidiRegionView::cut_copy_clear (Editing::CutCopyOp op)
3273 if (_selection.empty()) {
3277 PublicEditor& editor (trackview.editor());
3281 /* XXX what to do ? */
3285 editor.get_cut_buffer().add (selection_as_cut_buffer());
3293 start_note_diff_command();
3295 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3302 note_diff_remove_note (*i);
3312 MidiRegionView::selection_as_cut_buffer () const
3316 for (Selection::const_iterator i = _selection.begin(); i != _selection.end(); ++i) {
3317 NoteType* n = (*i)->note().get();
3318 notes.insert (boost::shared_ptr<NoteType> (new NoteType (*n)));
3321 MidiCutBuffer* cb = new MidiCutBuffer (trackview.session());
3327 /** This method handles undo */
3329 MidiRegionView::paste (framepos_t pos, unsigned paste_count, float times, const MidiCutBuffer& mcb)
3335 PublicEditor& editor = trackview.editor ();
3337 trackview.session()->begin_reversible_command (Operations::paste);
3339 start_note_diff_command (_("paste"));
3341 /* get snap duration, default to 1 beat if not snapped to anything musical */
3342 bool success = true;
3343 double snap_beats = editor.get_grid_type_as_beats(success, pos);
3348 const Evoral::MusicalTime first_time = (*mcb.notes().begin())->time();
3349 const Evoral::MusicalTime last_time = (*mcb.notes().rbegin())->end_time();
3350 const Evoral::MusicalTime duration = last_time - first_time;
3351 const Evoral::MusicalTime snap_duration = ceil(duration / snap_beats) * snap_beats;
3352 const Evoral::MusicalTime paste_offset = paste_count * snap_duration;
3353 const Evoral::MusicalTime pos_beats = absolute_frames_to_source_beats(pos) + paste_offset;
3354 Evoral::MusicalTime end_point = 0;
3356 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste data spans from %1 to %2 (%3) ; paste pos beats = %4 (based on %5 - %6)\n",
3359 duration, pos, _region->position(),
3364 for (int n = 0; n < (int) times; ++n) {
3366 for (Notes::const_iterator i = mcb.notes().begin(); i != mcb.notes().end(); ++i) {
3368 boost::shared_ptr<NoteType> copied_note (new NoteType (*((*i).get())));
3369 copied_note->set_time (pos_beats + copied_note->time() - first_time);
3371 /* make all newly added notes selected */
3373 note_diff_add_note (copied_note, true);
3374 end_point = copied_note->end_time();
3378 /* if we pasted past the current end of the region, extend the region */
3380 framepos_t end_frame = source_beats_to_absolute_frames (end_point);
3381 framepos_t region_end = _region->position() + _region->length() - 1;
3383 if (end_frame > region_end) {
3385 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste extended region from %1 to %2\n", region_end, end_frame));
3387 _region->clear_changes ();
3388 _region->set_length (end_frame - _region->position());
3389 trackview.session()->add_command (new StatefulDiffCommand (_region));
3394 trackview.session()->commit_reversible_command ();
3397 struct EventNoteTimeEarlyFirstComparator {
3398 bool operator() (NoteBase* a, NoteBase* b) {
3399 return a->note()->time() < b->note()->time();
3404 MidiRegionView::time_sort_events ()
3406 if (!_sort_needed) {
3410 EventNoteTimeEarlyFirstComparator cmp;
3413 _sort_needed = false;
3417 MidiRegionView::goto_next_note (bool add_to_selection)
3419 bool use_next = false;
3421 if (_events.back()->selected()) {
3425 time_sort_events ();
3427 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3428 uint16_t const channel_mask = mtv->midi_track()->get_playback_channel_mask();
3430 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3431 if ((*i)->selected()) {
3434 } else if (use_next) {
3435 if (channel_mask & (1 << (*i)->note()->channel())) {
3436 if (!add_to_selection) {
3439 note_selected (*i, true, false);
3446 /* use the first one */
3448 if (!_events.empty() && (channel_mask & (1 << _events.front()->note()->channel ()))) {
3449 unique_select (_events.front());
3454 MidiRegionView::goto_previous_note (bool add_to_selection)
3456 bool use_next = false;
3458 if (_events.front()->selected()) {
3462 time_sort_events ();
3464 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3465 uint16_t const channel_mask = mtv->midi_track()->get_playback_channel_mask ();
3467 for (Events::reverse_iterator i = _events.rbegin(); i != _events.rend(); ++i) {
3468 if ((*i)->selected()) {
3471 } else if (use_next) {
3472 if (channel_mask & (1 << (*i)->note()->channel())) {
3473 if (!add_to_selection) {
3476 note_selected (*i, true, false);
3483 /* use the last one */
3485 if (!_events.empty() && (channel_mask & (1 << (*_events.rbegin())->note()->channel ()))) {
3486 unique_select (*(_events.rbegin()));
3491 MidiRegionView::selection_as_notelist (Notes& selected, bool allow_all_if_none_selected)
3493 bool had_selected = false;
3495 time_sort_events ();
3497 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3498 if ((*i)->selected()) {
3499 selected.insert ((*i)->note());
3500 had_selected = true;
3504 if (allow_all_if_none_selected && !had_selected) {
3505 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3506 selected.insert ((*i)->note());
3512 MidiRegionView::update_ghost_note (double x, double y)
3514 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3519 _note_group->canvas_to_item (x, y);
3521 PublicEditor& editor = trackview.editor ();
3523 framepos_t const unsnapped_frame = editor.pixel_to_sample (x);
3524 framecnt_t grid_frames;
3525 framepos_t const f = snap_frame_to_grid_underneath (unsnapped_frame, grid_frames);
3527 /* use region_frames... because we are converting a delta within the region
3531 double length = editor.get_grid_type_as_beats (success, unsnapped_frame);
3537 /* note that this sets the time of the ghost note in beats relative to
3538 the start of the source; that is how all note times are stored.
3540 _ghost_note->note()->set_time (
3541 std::max(0.0, absolute_frames_to_source_beats (f + _region->position ())));
3542 _ghost_note->note()->set_length (length);
3543 _ghost_note->note()->set_note (midi_stream_view()->y_to_note (y));
3544 _ghost_note->note()->set_channel (mtv->get_channel_for_add ());
3546 /* the ghost note does not appear in ghost regions, so pass false in here */
3547 update_note (_ghost_note, false);
3549 show_verbose_cursor (_ghost_note->note ());
3553 MidiRegionView::create_ghost_note (double x, double y)
3555 remove_ghost_note ();
3557 boost::shared_ptr<NoteType> g (new NoteType);
3558 _ghost_note = new Note (*this, _note_group, g);
3559 _ghost_note->set_ignore_events (true);
3560 _ghost_note->set_outline_color (0x000000aa);
3561 if (x < 0) { x = 0; }
3562 update_ghost_note (x, y);
3563 _ghost_note->show ();
3568 show_verbose_cursor (_ghost_note->note ());
3572 MidiRegionView::remove_ghost_note ()
3579 MidiRegionView::snap_changed ()
3585 create_ghost_note (_last_ghost_x, _last_ghost_y);
3589 MidiRegionView::drop_down_keys ()
3591 _mouse_state = None;
3595 MidiRegionView::maybe_select_by_position (GdkEventButton* ev, double /*x*/, double y)
3597 double note = midi_stream_view()->y_to_note(y);
3599 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3601 uint16_t chn_mask = mtv->midi_track()->get_playback_channel_mask();
3603 if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
3604 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchGreaterThanOrEqual, (uint8_t) floor (note), chn_mask);
3605 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
3606 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchLessThanOrEqual, (uint8_t) floor (note), chn_mask);
3611 bool add_mrv_selection = false;
3613 if (_selection.empty()) {
3614 add_mrv_selection = true;
3617 for (Events::iterator i = e.begin(); i != e.end(); ++i) {
3618 if (_selection.insert (*i).second) {
3619 (*i)->set_selected (true);
3623 if (add_mrv_selection) {
3624 PublicEditor& editor (trackview.editor());
3625 editor.get_selection().add (this);
3630 MidiRegionView::color_handler ()
3632 RegionView::color_handler ();
3634 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3635 (*i)->set_selected ((*i)->selected()); // will change color
3638 /* XXX probably more to do here */
3642 MidiRegionView::enable_display (bool yn)
3644 RegionView::enable_display (yn);
3651 MidiRegionView::show_step_edit_cursor (Evoral::MusicalTime pos)
3653 if (_step_edit_cursor == 0) {
3654 ArdourCanvas::Item* const group = get_canvas_group();
3656 _step_edit_cursor = new ArdourCanvas::Rectangle (group);
3657 _step_edit_cursor->set_y0 (0);
3658 _step_edit_cursor->set_y1 (midi_stream_view()->contents_height());
3659 _step_edit_cursor->set_fill_color (RGBA_TO_UINT (45,0,0,90));
3660 _step_edit_cursor->set_outline_color (RGBA_TO_UINT (85,0,0,90));
3663 move_step_edit_cursor (pos);
3664 _step_edit_cursor->show ();
3668 MidiRegionView::move_step_edit_cursor (Evoral::MusicalTime pos)
3670 _step_edit_cursor_position = pos;
3672 if (_step_edit_cursor) {
3673 double pixel = trackview.editor().sample_to_pixel (region_beats_to_region_frames (pos));
3674 _step_edit_cursor->set_x0 (pixel);
3675 set_step_edit_cursor_width (_step_edit_cursor_width);
3680 MidiRegionView::hide_step_edit_cursor ()
3682 if (_step_edit_cursor) {
3683 _step_edit_cursor->hide ();
3688 MidiRegionView::set_step_edit_cursor_width (Evoral::MusicalTime beats)
3690 _step_edit_cursor_width = beats;
3692 if (_step_edit_cursor) {
3693 _step_edit_cursor->set_x1 (_step_edit_cursor->x0() + trackview.editor().sample_to_pixel (region_beats_to_region_frames (beats)));
3697 /** Called when a diskstream on our track has received some data. Update the view, if applicable.
3698 * @param w Source that the data will end up in.
3701 MidiRegionView::data_recorded (boost::weak_ptr<MidiSource> w)
3703 if (!_active_notes) {
3704 /* we aren't actively being recorded to */
3708 boost::shared_ptr<MidiSource> src = w.lock ();
3709 if (!src || src != midi_region()->midi_source()) {
3710 /* recorded data was not destined for our source */
3714 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (&trackview);
3716 boost::shared_ptr<MidiBuffer> buf = mtv->midi_track()->get_gui_feed_buffer ();
3718 BeatsFramesConverter converter (trackview.session()->tempo_map(), mtv->midi_track()->get_capture_start_frame (0));
3720 framepos_t back = max_framepos;
3722 for (MidiBuffer::iterator i = buf->begin(); i != buf->end(); ++i) {
3723 Evoral::MIDIEvent<MidiBuffer::TimeType> const ev (*i, false);
3725 if (ev.is_channel_event()) {
3726 if (get_channel_mode() == FilterChannels) {
3727 if (((uint16_t(1) << ev.channel()) & get_selected_channels()) == 0) {
3733 /* ev.time() is in session frames, so (ev.time() - converter.origin_b()) is
3734 frames from the start of the source, and so time_beats is in terms of the
3738 Evoral::MusicalTime const time_beats = converter.from (ev.time () - converter.origin_b ());
3740 if (ev.type() == MIDI_CMD_NOTE_ON) {
3741 boost::shared_ptr<NoteType> note (
3742 new NoteType (ev.channel(), time_beats, 0, ev.note(), ev.velocity()));
3744 add_note (note, true);
3746 /* fix up our note range */
3747 if (ev.note() < _current_range_min) {
3748 midi_stream_view()->apply_note_range (ev.note(), _current_range_max, true);
3749 } else if (ev.note() > _current_range_max) {
3750 midi_stream_view()->apply_note_range (_current_range_min, ev.note(), true);
3753 } else if (ev.type() == MIDI_CMD_NOTE_OFF) {
3754 resolve_note (ev.note (), time_beats);
3760 midi_stream_view()->check_record_layers (region(), back);
3764 MidiRegionView::trim_front_starting ()
3766 /* Reparent the note group to the region view's parent, so that it doesn't change
3767 when the region view is trimmed.
3769 _temporary_note_group = new ArdourCanvas::Container (group->parent ());
3770 _temporary_note_group->move (group->position ());
3771 _note_group->reparent (_temporary_note_group);
3775 MidiRegionView::trim_front_ending ()
3777 _note_group->reparent (group);
3778 delete _temporary_note_group;
3779 _temporary_note_group = 0;
3781 if (_region->start() < 0) {
3782 /* Trim drag made start time -ve; fix this */
3783 midi_region()->fix_negative_start ();
3788 MidiRegionView::edit_patch_change (PatchChange* pc)
3790 PatchChangeDialog d (&_source_relative_time_converter, trackview.session(), *pc->patch (), instrument_info(), Gtk::Stock::APPLY, true);
3792 int response = d.run();
3795 case Gtk::RESPONSE_ACCEPT:
3797 case Gtk::RESPONSE_REJECT:
3798 delete_patch_change (pc);
3804 change_patch_change (pc->patch(), d.patch ());
3808 MidiRegionView::delete_sysex (SysEx* /*sysex*/)
3811 // sysyex object doesn't have a pointer to a sysex event
3812 // MidiModel::SysExDiffCommand* c = _model->new_sysex_diff_command (_("delete sysex"));
3813 // c->remove (sysex->sysex());
3814 // _model->apply_command (*trackview.session(), c);
3816 //_sys_exes.clear ();
3817 // display_sysexes();
3821 MidiRegionView::show_verbose_cursor (boost::shared_ptr<NoteType> n) const
3823 using namespace MIDI::Name;
3827 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3829 boost::shared_ptr<MasterDeviceNames> device_names(mtv->get_device_names());
3831 MIDI::Name::PatchPrimaryKey patch_key;
3832 get_patch_key_at(n->time(), n->channel(), patch_key);
3833 name = device_names->note_name(mtv->gui_property(X_("midnam-custom-device-mode")),
3835 patch_key.bank_number,
3836 patch_key.program_number,
3842 snprintf (buf, sizeof (buf), "%d %s\nCh %d Vel %d",
3844 name.empty() ? Evoral::midi_note_name (n->note()).c_str() : name.c_str(),
3845 (int) n->channel() + 1,
3846 (int) n->velocity());
3848 show_verbose_cursor(buf, 10, 20);
3852 MidiRegionView::show_verbose_cursor (string const & text, double xoffset, double yoffset) const
3854 trackview.editor().verbose_cursor()->set (text);
3855 trackview.editor().verbose_cursor()->show ();
3856 trackview.editor().verbose_cursor()->set_offset (ArdourCanvas::Duple (xoffset, yoffset));
3859 /** @param p A session framepos.
3860 * @param grid_frames Filled in with the number of frames that a grid interval is at p.
3861 * @return p snapped to the grid subdivision underneath it.
3864 MidiRegionView::snap_frame_to_grid_underneath (framepos_t p, framecnt_t& grid_frames) const
3866 PublicEditor& editor = trackview.editor ();
3869 Evoral::MusicalTime grid_beats = editor.get_grid_type_as_beats (success, p);
3875 grid_frames = region_beats_to_region_frames (grid_beats);
3877 /* Hack so that we always snap to the note that we are over, instead of snapping
3878 to the next one if we're more than halfway through the one we're over.
3880 if (editor.snap_mode() == SnapNormal && p >= grid_frames / 2) {
3881 p -= grid_frames / 2;
3884 return snap_frame_to_frame (p);
3887 /** Called when the selection has been cleared in any MidiRegionView.
3888 * @param rv MidiRegionView that the selection was cleared in.
3891 MidiRegionView::selection_cleared (MidiRegionView* rv)
3897 /* Clear our selection in sympathy; but don't signal the fact */
3898 clear_selection (false);
3902 MidiRegionView::note_button_release ()
3904 delete _note_player;
3909 MidiRegionView::get_channel_mode () const
3911 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (&trackview);
3912 return rtav->midi_track()->get_playback_channel_mode();
3916 MidiRegionView::get_selected_channels () const
3918 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (&trackview);
3919 return rtav->midi_track()->get_playback_channel_mask();