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 "midi++/midnam_patch.h"
32 #include "pbd/memento_command.h"
33 #include "pbd/stateful_diff_command.h"
35 #include "ardour/midi_model.h"
36 #include "ardour/midi_region.h"
37 #include "ardour/midi_source.h"
38 #include "ardour/midi_track.h"
39 #include "ardour/operations.h"
40 #include "ardour/session.h"
42 #include "evoral/Parameter.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"
52 #include "control_point.h"
55 #include "editor_drag.h"
56 #include "ghostregion.h"
57 #include "gui_thread.h"
58 #include "item_counts.h"
60 #include "midi_channel_dialog.h"
61 #include "midi_cut_buffer.h"
62 #include "midi_list_editor.h"
63 #include "midi_region_view.h"
64 #include "midi_streamview.h"
65 #include "midi_time_axis.h"
66 #include "midi_util.h"
67 #include "midi_velocity_dialog.h"
68 #include "mouse_cursors.h"
69 #include "note_player.h"
70 #include "paste_context.h"
71 #include "public_editor.h"
72 #include "route_time_axis.h"
73 #include "rgb_macros.h"
74 #include "selection.h"
75 #include "streamview.h"
76 #include "patch_change_dialog.h"
77 #include "verbose_cursor.h"
78 #include "ardour_ui.h"
81 #include "patch_change.h"
86 using namespace ARDOUR;
88 using namespace Editing;
90 using Gtkmm2ext::Keyboard;
92 PBD::Signal1<void, MidiRegionView *> MidiRegionView::SelectionCleared;
94 #define MIDI_BP_ZERO ((Config->get_first_midi_bank_is_zero())?0:1)
96 MidiRegionView::MidiRegionView (ArdourCanvas::Container* parent,
97 RouteTimeAxisView& tv,
98 boost::shared_ptr<MidiRegion> r,
100 uint32_t basic_color)
101 : RegionView (parent, tv, r, spu, basic_color)
102 , _current_range_min(0)
103 , _current_range_max(0)
104 , _region_relative_time_converter(r->session().tempo_map(), r->position())
105 , _source_relative_time_converter(r->session().tempo_map(), r->position() - r->start())
107 , _note_group (new ArdourCanvas::Container (group))
108 , _note_diff_command (0)
110 , _step_edit_cursor (0)
111 , _step_edit_cursor_width (1.0)
112 , _step_edit_cursor_position (0.0)
113 , _channel_selection_scoped_note (0)
114 , _temporary_note_group (0)
117 , _sort_needed (true)
118 , _optimization_iterator (_events.end())
120 , _no_sound_notes (false)
123 , _grabbed_keyboard (false)
126 CANVAS_DEBUG_NAME (_note_group, string_compose ("note group for %1", get_item_name()));
127 _note_group->raise_to_top();
128 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
130 Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&MidiRegionView::parameter_changed, this, _1), gui_context());
131 connect_to_diskstream ();
133 SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), boost::bind (&MidiRegionView::selection_cleared, this, _1), gui_context ());
135 PublicEditor& editor (trackview.editor());
136 editor.get_selection().ClearMidiNoteSelection.connect (_clear_midi_selection_connection, invalidator (*this), boost::bind (&MidiRegionView::clear_midi_selection, this), gui_context ());
139 MidiRegionView::MidiRegionView (ArdourCanvas::Container* parent,
140 RouteTimeAxisView& tv,
141 boost::shared_ptr<MidiRegion> r,
143 uint32_t basic_color,
145 TimeAxisViewItem::Visibility visibility)
146 : RegionView (parent, tv, r, spu, basic_color, recording, visibility)
147 , _current_range_min(0)
148 , _current_range_max(0)
149 , _region_relative_time_converter(r->session().tempo_map(), r->position())
150 , _source_relative_time_converter(r->session().tempo_map(), r->position() - r->start())
152 , _note_group (new ArdourCanvas::Container (parent))
153 , _note_diff_command (0)
155 , _step_edit_cursor (0)
156 , _step_edit_cursor_width (1.0)
157 , _step_edit_cursor_position (0.0)
158 , _channel_selection_scoped_note (0)
159 , _temporary_note_group (0)
162 , _sort_needed (true)
163 , _optimization_iterator (_events.end())
165 , _no_sound_notes (false)
168 , _grabbed_keyboard (false)
171 CANVAS_DEBUG_NAME (_note_group, string_compose ("note group for %1", get_item_name()));
172 _note_group->raise_to_top();
174 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
176 connect_to_diskstream ();
178 SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), boost::bind (&MidiRegionView::selection_cleared, this, _1), gui_context ());
180 PublicEditor& editor (trackview.editor());
181 editor.get_selection().ClearMidiNoteSelection.connect (_clear_midi_selection_connection, invalidator (*this), boost::bind (&MidiRegionView::clear_midi_selection, this), gui_context ());
185 MidiRegionView::parameter_changed (std::string const & p)
187 if (p == "display-first-midi-bank-as-zero") {
188 if (_enable_display) {
194 MidiRegionView::MidiRegionView (const MidiRegionView& other)
195 : sigc::trackable(other)
197 , _current_range_min(0)
198 , _current_range_max(0)
199 , _region_relative_time_converter(other.region_relative_time_converter())
200 , _source_relative_time_converter(other.source_relative_time_converter())
202 , _note_group (new ArdourCanvas::Container (get_canvas_group()))
203 , _note_diff_command (0)
205 , _step_edit_cursor (0)
206 , _step_edit_cursor_width (1.0)
207 , _step_edit_cursor_position (0.0)
208 , _channel_selection_scoped_note (0)
209 , _temporary_note_group (0)
212 , _sort_needed (true)
213 , _optimization_iterator (_events.end())
215 , _no_sound_notes (false)
218 , _grabbed_keyboard (false)
224 MidiRegionView::MidiRegionView (const MidiRegionView& other, boost::shared_ptr<MidiRegion> region)
225 : RegionView (other, boost::shared_ptr<Region> (region))
226 , _current_range_min(0)
227 , _current_range_max(0)
228 , _region_relative_time_converter(other.region_relative_time_converter())
229 , _source_relative_time_converter(other.source_relative_time_converter())
231 , _note_group (new ArdourCanvas::Container (get_canvas_group()))
232 , _note_diff_command (0)
234 , _step_edit_cursor (0)
235 , _step_edit_cursor_width (1.0)
236 , _step_edit_cursor_position (0.0)
237 , _channel_selection_scoped_note (0)
238 , _temporary_note_group (0)
241 , _sort_needed (true)
242 , _optimization_iterator (_events.end())
244 , _no_sound_notes (false)
247 , _grabbed_keyboard (false)
254 MidiRegionView::init (bool wfd)
256 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
258 NoteBase::NoteBaseDeleted.connect (note_delete_connection, MISSING_INVALIDATOR,
259 boost::bind (&MidiRegionView::maybe_remove_deleted_note_from_selection, this, _1),
263 Glib::Threads::Mutex::Lock lm(midi_region()->midi_source(0)->mutex());
264 midi_region()->midi_source(0)->load_model(lm);
267 _model = midi_region()->midi_source(0)->model();
268 _enable_display = false;
269 fill_color_name = "midi frame base";
271 RegionView::init (false);
273 set_height (trackview.current_height());
276 region_sync_changed ();
277 region_resized (ARDOUR::bounds_change);
282 _enable_display = true;
285 display_model (_model);
289 reset_width_dependent_items (_pixel_width);
291 group->raise_to_top();
293 midi_view()->midi_track()->PlaybackChannelModeChanged.connect (_channel_mode_changed_connection, invalidator (*this),
294 boost::bind (&MidiRegionView::midi_channel_mode_changed, this),
297 instrument_info().Changed.connect (_instrument_changed_connection, invalidator (*this),
298 boost::bind (&MidiRegionView::instrument_settings_changed, this), gui_context());
300 trackview.editor().SnapChanged.connect(snap_changed_connection, invalidator(*this),
301 boost::bind (&MidiRegionView::snap_changed, this),
304 trackview.editor().MouseModeChanged.connect(_mouse_mode_connection, invalidator (*this),
305 boost::bind (&MidiRegionView::mouse_mode_changed, this),
308 Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&MidiRegionView::parameter_changed, this, _1), gui_context());
309 connect_to_diskstream ();
311 SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), boost::bind (&MidiRegionView::selection_cleared, this, _1), gui_context ());
313 PublicEditor& editor (trackview.editor());
314 editor.get_selection().ClearMidiNoteSelection.connect (_clear_midi_selection_connection, invalidator (*this), boost::bind (&MidiRegionView::clear_midi_selection, this), gui_context ());
318 MidiRegionView::instrument_info () const
320 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
321 return route_ui->route()->instrument_info();
324 const boost::shared_ptr<ARDOUR::MidiRegion>
325 MidiRegionView::midi_region() const
327 return boost::dynamic_pointer_cast<ARDOUR::MidiRegion>(_region);
331 MidiRegionView::connect_to_diskstream ()
333 midi_view()->midi_track()->DataRecorded.connect(
334 *this, invalidator(*this),
335 boost::bind (&MidiRegionView::data_recorded, this, _1),
340 MidiRegionView::canvas_group_event(GdkEvent* ev)
342 if (in_destructor || _recregion) {
346 if (!trackview.editor().internal_editing()) {
347 // not in internal edit mode, so just act like a normal region
348 return RegionView::canvas_group_event (ev);
354 case GDK_ENTER_NOTIFY:
355 _last_event_x = ev->crossing.x;
356 _last_event_y = ev->crossing.y;
357 enter_notify(&ev->crossing);
358 // set entered_regionview (among other things)
359 return RegionView::canvas_group_event (ev);
361 case GDK_LEAVE_NOTIFY:
362 _last_event_x = ev->crossing.x;
363 _last_event_y = ev->crossing.y;
364 leave_notify(&ev->crossing);
365 // reset entered_regionview (among other things)
366 return RegionView::canvas_group_event (ev);
369 if (scroll (&ev->scroll)) {
375 return key_press (&ev->key);
377 case GDK_KEY_RELEASE:
378 return key_release (&ev->key);
380 case GDK_BUTTON_PRESS:
381 return button_press (&ev->button);
383 case GDK_BUTTON_RELEASE:
384 r = button_release (&ev->button);
385 _note_player.reset();
388 case GDK_MOTION_NOTIFY:
389 _last_event_x = ev->motion.x;
390 _last_event_y = ev->motion.y;
391 return motion (&ev->motion);
397 return RegionView::canvas_group_event (ev);
401 MidiRegionView::enter_notify (GdkEventCrossing* ev)
410 MidiRegionView::leave_notify (GdkEventCrossing*)
419 MidiRegionView::mouse_mode_changed ()
421 // Adjust frame colour (become more transparent for internal tools)
425 if (trackview.editor().internal_editing()) {
426 // Switched in to internal editing mode while entered
429 // Switched out of internal editing mode while entered
436 MidiRegionView::enter_internal()
438 if (trackview.editor().current_mouse_mode() == MouseDraw && _mouse_state != AddDragging) {
439 // Show ghost note under pencil
440 create_ghost_note(_last_event_x, _last_event_y);
443 if (!_selection.empty()) {
444 // Grab keyboard for moving selected notes with arrow keys
445 Keyboard::magic_widget_grab_focus();
446 _grabbed_keyboard = true;
451 MidiRegionView::leave_internal()
453 trackview.editor().verbose_cursor()->hide ();
454 remove_ghost_note ();
456 if (_grabbed_keyboard) {
457 Keyboard::magic_widget_drop_focus();
458 _grabbed_keyboard = false;
463 MidiRegionView::button_press (GdkEventButton* ev)
465 if (ev->button != 1) {
469 Editor* editor = dynamic_cast<Editor *> (&trackview.editor());
470 MouseMode m = editor->current_mouse_mode();
472 if (m == MouseContent && Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())) {
473 _press_cursor_ctx = CursorContext::create(*editor, editor->cursors()->midi_pencil);
476 if (_mouse_state != SelectTouchDragging) {
478 _pressed_button = ev->button;
479 _mouse_state = Pressed;
484 _pressed_button = ev->button;
490 MidiRegionView::button_release (GdkEventButton* ev)
492 double event_x, event_y;
494 if (ev->button != 1) {
501 group->canvas_to_item (event_x, event_y);
504 PublicEditor& editor = trackview.editor ();
506 _press_cursor_ctx.reset();
508 switch (_mouse_state) {
509 case Pressed: // Clicked
511 switch (editor.current_mouse_mode()) {
513 /* no motion occured - simple click */
522 if (Keyboard::is_insert_note_event(ev)) {
524 double event_x, event_y;
528 group->canvas_to_item (event_x, event_y);
530 Evoral::MusicalTime beats = get_grid_beats(editor.pixel_to_sample(event_x));
532 /* Shorten the length by 1 tick so that we can add a new note at the next
533 grid snap without it overlapping this one.
535 beats -= Evoral::MusicalTime::tick();
537 create_note_at (editor.pixel_to_sample (event_x), event_y, beats, true);
544 Evoral::MusicalTime beats = get_grid_beats(editor.pixel_to_sample(event_x));
546 /* Shorten the length by 1 tick so that we can add a new note at the next
547 grid snap without it overlapping this one.
549 beats -= Evoral::MusicalTime::tick();
551 create_note_at (editor.pixel_to_sample (event_x), event_y, beats, true);
562 case SelectRectDragging:
564 editor.drags()->end_grab ((GdkEvent *) ev);
566 create_ghost_note (ev->x, ev->y);
578 MidiRegionView::motion (GdkEventMotion* ev)
580 PublicEditor& editor = trackview.editor ();
582 if (!_ghost_note && editor.current_mouse_mode() == MouseContent &&
583 Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier()) &&
584 _mouse_state != AddDragging) {
586 create_ghost_note (ev->x, ev->y);
588 } else if (_ghost_note && editor.current_mouse_mode() == MouseContent &&
589 Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())) {
591 update_ghost_note (ev->x, ev->y);
593 } else if (_ghost_note && editor.current_mouse_mode() == MouseContent) {
595 remove_ghost_note ();
596 editor.verbose_cursor()->hide ();
598 } else if (_ghost_note && editor.current_mouse_mode() == MouseDraw) {
600 update_ghost_note (ev->x, ev->y);
603 /* any motion immediately hides velocity text that may have been visible */
605 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
606 (*i)->hide_velocity ();
609 switch (_mouse_state) {
612 if (_pressed_button == 1) {
614 MouseMode m = editor.current_mouse_mode();
616 if (m == MouseDraw || (m == MouseContent && Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier()))) {
617 editor.drags()->set (new NoteCreateDrag (dynamic_cast<Editor *> (&editor), group, this), (GdkEvent *) ev);
618 _mouse_state = AddDragging;
619 remove_ghost_note ();
620 editor.verbose_cursor()->hide ();
622 } else if (m == MouseContent) {
623 editor.drags()->set (new MidiRubberbandSelectDrag (dynamic_cast<Editor *> (&editor), this), (GdkEvent *) ev);
624 if (!Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
627 _mouse_state = SelectRectDragging;
629 } else if (m == MouseRange) {
630 editor.drags()->set (new MidiVerticalSelectDrag (dynamic_cast<Editor *> (&editor), this), (GdkEvent *) ev);
631 _mouse_state = SelectVerticalDragging;
638 case SelectRectDragging:
639 case SelectVerticalDragging:
641 editor.drags()->motion_handler ((GdkEvent *) ev, false);
644 case SelectTouchDragging:
652 /* we may be dragging some non-note object (eg. patch-change, sysex)
655 return editor.drags()->motion_handler ((GdkEvent *) ev, false);
660 MidiRegionView::scroll (GdkEventScroll* ev)
662 if (_selection.empty()) {
666 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
667 /* XXX: bit of a hack; allow PrimaryModifier scroll through so that
668 it still works for zoom.
673 trackview.editor().verbose_cursor()->hide ();
675 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
676 bool together = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
678 if (ev->direction == GDK_SCROLL_UP) {
679 change_velocities (true, fine, false, together);
680 } else if (ev->direction == GDK_SCROLL_DOWN) {
681 change_velocities (false, fine, false, together);
683 /* left, right: we don't use them */
691 MidiRegionView::key_press (GdkEventKey* ev)
693 /* since GTK bindings are generally activated on press, and since
694 detectable auto-repeat is the name of the game and only sends
695 repeated presses, carry out key actions at key press, not release.
698 bool unmodified = Keyboard::no_modifier_keys_pressed (ev);
700 if (unmodified && (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R)) {
701 _mouse_state = SelectTouchDragging;
704 } else if (ev->keyval == GDK_Escape && unmodified) {
708 } else if (unmodified && (ev->keyval == GDK_comma || ev->keyval == GDK_period)) {
710 bool start = (ev->keyval == GDK_comma);
711 bool end = (ev->keyval == GDK_period);
712 bool shorter = Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier);
713 bool fine = Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
715 change_note_lengths (fine, shorter, Evoral::MusicalTime(), start, end);
719 } else if ((ev->keyval == GDK_BackSpace || ev->keyval == GDK_Delete) && unmodified) {
721 if (_selection.empty()) {
728 } else if (ev->keyval == GDK_Tab) {
730 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
731 goto_previous_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
733 goto_next_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
737 } else if (ev->keyval == GDK_ISO_Left_Tab) {
739 /* Shift-TAB generates ISO Left Tab, for some reason */
741 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
742 goto_previous_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
744 goto_next_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
750 } else if (ev->keyval == GDK_Up) {
752 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
753 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
754 bool together = Keyboard::modifier_state_contains (ev->state, Keyboard::Level4Modifier);
756 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
757 change_velocities (true, fine, allow_smush, together);
759 transpose (true, fine, allow_smush);
763 } else if (ev->keyval == GDK_Down) {
765 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
766 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
767 bool together = Keyboard::modifier_state_contains (ev->state, Keyboard::Level4Modifier);
769 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
770 change_velocities (false, fine, allow_smush, together);
772 transpose (false, fine, allow_smush);
776 } else if (ev->keyval == GDK_Left) {
778 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
779 nudge_notes (false, fine);
782 } else if (ev->keyval == GDK_Right) {
784 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
785 nudge_notes (true, fine);
788 } else if (ev->keyval == GDK_c && unmodified) {
792 } else if (ev->keyval == GDK_v && unmodified) {
801 MidiRegionView::key_release (GdkEventKey* ev)
803 if ((_mouse_state == SelectTouchDragging) && (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R)) {
811 MidiRegionView::channel_edit ()
813 if (_selection.empty()) {
817 /* pick a note somewhat at random (since Selection is a set<>) to
818 * provide the "current" channel for the dialog.
821 uint8_t current_channel = (*_selection.begin())->note()->channel ();
822 MidiChannelDialog channel_dialog (current_channel);
823 int ret = channel_dialog.run ();
826 case Gtk::RESPONSE_OK:
832 uint8_t new_channel = channel_dialog.active_channel ();
834 start_note_diff_command (_("channel edit"));
836 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
837 Selection::iterator next = i;
839 change_note_channel (*i, new_channel);
847 MidiRegionView::velocity_edit ()
849 if (_selection.empty()) {
853 /* pick a note somewhat at random (since Selection is a set<>) to
854 * provide the "current" velocity for the dialog.
857 uint8_t current_velocity = (*_selection.begin())->note()->velocity ();
858 MidiVelocityDialog velocity_dialog (current_velocity);
859 int ret = velocity_dialog.run ();
862 case Gtk::RESPONSE_OK:
868 uint8_t new_velocity = velocity_dialog.velocity ();
870 start_note_diff_command (_("velocity edit"));
872 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
873 Selection::iterator next = i;
875 change_note_velocity (*i, new_velocity, false);
883 MidiRegionView::show_list_editor ()
886 _list_editor = new MidiListEditor (trackview.session(), midi_region(), midi_view()->midi_track());
888 _list_editor->present ();
891 /** Add a note to the model, and the view, at a canvas (click) coordinate.
892 * \param t time in frames relative to the position of the region
893 * \param y vertical position in pixels
894 * \param length duration of the note in beats
895 * \param snap_t true to snap t to the grid, otherwise false.
898 MidiRegionView::create_note_at (framepos_t t, double y, Evoral::MusicalTime length, bool snap_t)
900 if (length < 2 * DBL_EPSILON) {
904 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
905 MidiStreamView* const view = mtv->midi_view();
907 const double note = view->y_to_note(y);
909 // Start of note in frames relative to region start
911 framecnt_t grid_frames;
912 t = snap_frame_to_grid_underneath (t, grid_frames);
915 const boost::shared_ptr<NoteType> new_note (
916 new NoteType (mtv->get_channel_for_add (),
917 region_frames_to_region_beats(t + _region->start()),
919 (uint8_t)note, 0x40));
921 if (_model->contains (new_note)) {
925 view->update_note_range(new_note->note());
927 MidiModel::NoteDiffCommand* cmd = _model->new_note_diff_command(_("add note"));
929 _model->apply_command(*trackview.session(), cmd);
931 play_midi_note (new_note);
935 MidiRegionView::clear_events (bool with_selection_signal)
937 clear_selection (with_selection_signal);
940 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
941 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
946 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
951 _patch_changes.clear();
953 _optimization_iterator = _events.end();
957 MidiRegionView::display_model(boost::shared_ptr<MidiModel> model)
961 content_connection.disconnect ();
962 _model->ContentsChanged.connect (content_connection, invalidator (*this), boost::bind (&MidiRegionView::redisplay_model, this), gui_context());
966 if (_enable_display) {
972 MidiRegionView::start_note_diff_command (string name)
974 if (!_note_diff_command) {
975 _note_diff_command = _model->new_note_diff_command (name);
980 MidiRegionView::note_diff_add_note (const boost::shared_ptr<NoteType> note, bool selected, bool show_velocity)
982 if (_note_diff_command) {
983 _note_diff_command->add (note);
986 _marked_for_selection.insert(note);
989 _marked_for_velocity.insert(note);
994 MidiRegionView::note_diff_remove_note (NoteBase* ev)
996 if (_note_diff_command && ev->note()) {
997 _note_diff_command->remove(ev->note());
1002 MidiRegionView::note_diff_add_change (NoteBase* ev,
1003 MidiModel::NoteDiffCommand::Property property,
1006 if (_note_diff_command) {
1007 _note_diff_command->change (ev->note(), property, val);
1012 MidiRegionView::note_diff_add_change (NoteBase* ev,
1013 MidiModel::NoteDiffCommand::Property property,
1014 Evoral::MusicalTime val)
1016 if (_note_diff_command) {
1017 _note_diff_command->change (ev->note(), property, val);
1022 MidiRegionView::apply_diff (bool as_subcommand)
1026 if (!_note_diff_command) {
1030 if ((add_or_remove = _note_diff_command->adds_or_removes())) {
1031 // Mark all selected notes for selection when model reloads
1032 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1033 _marked_for_selection.insert((*i)->note());
1037 if (as_subcommand) {
1038 _model->apply_command_as_subcommand (*trackview.session(), _note_diff_command);
1040 _model->apply_command (*trackview.session(), _note_diff_command);
1043 _note_diff_command = 0;
1044 midi_view()->midi_track()->playlist_modified();
1046 if (add_or_remove) {
1047 _marked_for_selection.clear();
1050 _marked_for_velocity.clear();
1054 MidiRegionView::abort_command()
1056 delete _note_diff_command;
1057 _note_diff_command = 0;
1062 MidiRegionView::find_canvas_note (boost::shared_ptr<NoteType> note)
1064 if (_optimization_iterator != _events.end()) {
1065 ++_optimization_iterator;
1068 if (_optimization_iterator != _events.end() && (*_optimization_iterator)->note() == note) {
1069 return *_optimization_iterator;
1072 for (_optimization_iterator = _events.begin(); _optimization_iterator != _events.end(); ++_optimization_iterator) {
1073 if ((*_optimization_iterator)->note() == note) {
1074 return *_optimization_iterator;
1082 MidiRegionView::get_events (Events& e, Evoral::Sequence<Evoral::MusicalTime>::NoteOperator op, uint8_t val, int chan_mask)
1084 MidiModel::Notes notes;
1085 _model->get_notes (notes, op, val, chan_mask);
1087 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1088 NoteBase* cne = find_canvas_note (*n);
1096 MidiRegionView::redisplay_model()
1098 // Don't redisplay the model if we're currently recording and displaying that
1099 if (_active_notes) {
1107 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1108 (*i)->invalidate ();
1111 MidiModel::ReadLock lock(_model->read_lock());
1113 MidiModel::Notes& notes (_model->notes());
1114 _optimization_iterator = _events.begin();
1116 bool empty_when_starting = _events.empty();
1118 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1120 boost::shared_ptr<NoteType> note (*n);
1124 if (note_in_region_range (note, visible)) {
1126 if (!empty_when_starting && (cne = find_canvas_note (note)) != 0) {
1133 if ((cn = dynamic_cast<Note*>(cne)) != 0) {
1135 } else if ((ch = dynamic_cast<Hit*>(cne)) != 0) {
1147 add_note (note, visible);
1152 if (!empty_when_starting && (cne = find_canvas_note (note)) != 0) {
1160 /* remove note items that are no longer valid */
1162 if (!empty_when_starting) {
1163 for (Events::iterator i = _events.begin(); i != _events.end(); ) {
1164 if (!(*i)->valid ()) {
1166 for (vector<GhostRegion*>::iterator j = ghosts.begin(); j != ghosts.end(); ++j) {
1167 MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*j);
1169 gr->remove_note (*i);
1174 i = _events.erase (i);
1182 _patch_changes.clear();
1186 display_patch_changes ();
1188 _marked_for_selection.clear ();
1189 _marked_for_velocity.clear ();
1191 /* we may have caused _events to contain things out of order (e.g. if a note
1192 moved earlier or later). we don't generally need them in time order, but
1193 make a note that a sort is required for those cases that require it.
1196 _sort_needed = true;
1200 MidiRegionView::display_patch_changes ()
1202 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1203 uint16_t chn_mask = mtv->midi_track()->get_playback_channel_mask();
1205 for (uint8_t i = 0; i < 16; ++i) {
1206 display_patch_changes_on_channel (i, chn_mask & (1 << i));
1210 /** @param active_channel true to display patch changes fully, false to display
1211 * them `greyed-out' (as on an inactive channel)
1214 MidiRegionView::display_patch_changes_on_channel (uint8_t channel, bool active_channel)
1216 for (MidiModel::PatchChanges::const_iterator i = _model->patch_changes().begin(); i != _model->patch_changes().end(); ++i) {
1218 if ((*i)->channel() != channel) {
1222 const string patch_name = instrument_info().get_patch_name ((*i)->bank(), (*i)->program(), channel);
1223 add_canvas_patch_change (*i, patch_name, active_channel);
1228 MidiRegionView::display_sysexes()
1230 bool have_periodic_system_messages = false;
1231 bool display_periodic_messages = true;
1233 if (!ARDOUR_UI::config()->get_never_display_periodic_midi()) {
1235 for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
1236 const boost::shared_ptr<const Evoral::MIDIEvent<Evoral::MusicalTime> > mev =
1237 boost::static_pointer_cast<const Evoral::MIDIEvent<Evoral::MusicalTime> > (*i);
1240 if (mev->is_spp() || mev->is_mtc_quarter() || mev->is_mtc_full()) {
1241 have_periodic_system_messages = true;
1247 if (have_periodic_system_messages) {
1248 double zoom = trackview.editor().get_current_zoom (); // frames per pixel
1250 /* get an approximate value for the number of samples per video frame */
1252 double video_frame = trackview.session()->frame_rate() * (1.0/30);
1254 /* if we are zoomed out beyond than the cutoff (i.e. more
1255 * frames per pixel than frames per 4 video frames), don't
1256 * show periodic sysex messages.
1259 if (zoom > (video_frame*4)) {
1260 display_periodic_messages = false;
1264 display_periodic_messages = false;
1267 for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
1269 const boost::shared_ptr<const Evoral::MIDIEvent<Evoral::MusicalTime> > mev =
1270 boost::static_pointer_cast<const Evoral::MIDIEvent<Evoral::MusicalTime> > (*i);
1272 Evoral::MusicalTime time = (*i)->time();
1275 if (mev->is_spp() || mev->is_mtc_quarter() || mev->is_mtc_full()) {
1276 if (!display_periodic_messages) {
1284 for (uint32_t b = 0; b < (*i)->size(); ++b) {
1285 str << int((*i)->buffer()[b]);
1286 if (b != (*i)->size() -1) {
1290 string text = str.str();
1292 const double x = trackview.editor().sample_to_pixel(source_beats_to_region_frames(time));
1294 double height = midi_stream_view()->contents_height();
1296 // CAIROCANVAS: no longer passing *i (the sysex event) to the
1297 // SysEx canvas object!!!
1299 boost::shared_ptr<SysEx> sysex = boost::shared_ptr<SysEx>(
1300 new SysEx (*this, _note_group, text, height, x, 1.0));
1302 // Show unless message is beyond the region bounds
1303 if (time - _region->start() >= _region->length() || time < _region->start()) {
1309 _sys_exes.push_back(sysex);
1313 MidiRegionView::~MidiRegionView ()
1315 in_destructor = true;
1317 trackview.editor().verbose_cursor()->hide ();
1319 note_delete_connection.disconnect ();
1321 delete _list_editor;
1323 RegionViewGoingAway (this); /* EMIT_SIGNAL */
1325 if (_active_notes) {
1329 _selection_cleared_connection.disconnect ();
1332 clear_events (false);
1335 delete _note_diff_command;
1336 delete _step_edit_cursor;
1337 delete _temporary_note_group;
1341 MidiRegionView::region_resized (const PropertyChange& what_changed)
1343 RegionView::region_resized(what_changed);
1345 if (what_changed.contains (ARDOUR::Properties::position)) {
1346 _region_relative_time_converter.set_origin_b(_region->position());
1347 set_duration(_region->length(), 0);
1348 if (_enable_display) {
1353 if (what_changed.contains (ARDOUR::Properties::start) ||
1354 what_changed.contains (ARDOUR::Properties::position)) {
1355 _source_relative_time_converter.set_origin_b (_region->position() - _region->start());
1360 MidiRegionView::reset_width_dependent_items (double pixel_width)
1362 RegionView::reset_width_dependent_items(pixel_width);
1364 if (_enable_display) {
1368 for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
1369 if ((*x)->canvas_item()->width() >= _pixel_width) {
1376 move_step_edit_cursor (_step_edit_cursor_position);
1377 set_step_edit_cursor_width (_step_edit_cursor_width);
1381 MidiRegionView::set_height (double height)
1383 double old_height = _height;
1384 RegionView::set_height(height);
1386 apply_note_range (midi_stream_view()->lowest_note(),
1387 midi_stream_view()->highest_note(),
1388 height != old_height);
1391 name_text->raise_to_top();
1394 for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
1395 (*x)->set_height (midi_stream_view()->contents_height());
1398 if (_step_edit_cursor) {
1399 _step_edit_cursor->set_y1 (midi_stream_view()->contents_height());
1404 /** Apply the current note range from the stream view
1405 * by repositioning/hiding notes as necessary
1408 MidiRegionView::apply_note_range (uint8_t min, uint8_t max, bool force)
1410 if (!_enable_display) {
1414 if (!force && _current_range_min == min && _current_range_max == max) {
1418 _current_range_min = min;
1419 _current_range_max = max;
1421 for (Events::const_iterator i = _events.begin(); i != _events.end(); ++i) {
1422 NoteBase* event = *i;
1423 boost::shared_ptr<NoteType> note (event->note());
1425 if (note->note() < _current_range_min ||
1426 note->note() > _current_range_max) {
1432 if (Note* cnote = dynamic_cast<Note*>(event)) {
1434 const double y0 = 1. + floor (midi_stream_view()->note_to_y(note->note()));
1435 const double y1 = y0 + std::max(1., floor(midi_stream_view()->note_height()) - 1.);
1440 } else if (Hit* chit = dynamic_cast<Hit*>(event)) {
1447 MidiRegionView::add_ghost (TimeAxisView& tv)
1451 double unit_position = _region->position () / samples_per_pixel;
1452 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(&tv);
1453 MidiGhostRegion* ghost;
1455 if (mtv && mtv->midi_view()) {
1456 /* if ghost is inserted into midi track, use a dedicated midi ghost canvas group
1457 to allow having midi notes on top of note lines and waveforms.
1459 ghost = new MidiGhostRegion (*mtv->midi_view(), trackview, unit_position);
1461 ghost = new MidiGhostRegion (tv, trackview, unit_position);
1464 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1465 if ((note = dynamic_cast<Note*>(*i)) != 0) {
1466 ghost->add_note(note);
1470 ghost->set_height ();
1471 ghost->set_duration (_region->length() / samples_per_pixel);
1472 ghosts.push_back (ghost);
1474 GhostRegion::CatchDeletion.connect (*this, invalidator (*this), boost::bind (&RegionView::remove_ghost, this, _1), gui_context());
1480 /** Begin tracking note state for successive calls to add_event
1483 MidiRegionView::begin_write()
1485 if (_active_notes) {
1486 delete[] _active_notes;
1488 _active_notes = new Note*[128];
1489 for (unsigned i = 0; i < 128; ++i) {
1490 _active_notes[i] = 0;
1495 /** Destroy note state for add_event
1498 MidiRegionView::end_write()
1500 delete[] _active_notes;
1502 _marked_for_selection.clear();
1503 _marked_for_velocity.clear();
1507 /** Resolve an active MIDI note (while recording).
1510 MidiRegionView::resolve_note(uint8_t note, Evoral::MusicalTime end_time)
1512 if (midi_view()->note_mode() != Sustained) {
1516 if (_active_notes && _active_notes[note]) {
1518 /* XXX is end_time really region-centric? I think so, because
1519 this is a new region that we're recording, so source zero is
1520 the same as region zero
1522 const framepos_t end_time_frames = region_beats_to_region_frames(end_time);
1524 _active_notes[note]->set_x1 (trackview.editor().sample_to_pixel(end_time_frames));
1525 _active_notes[note]->set_outline_all ();
1526 _active_notes[note] = 0;
1532 /** Extend active notes to rightmost edge of region (if length is changed)
1535 MidiRegionView::extend_active_notes()
1537 if (!_active_notes) {
1541 for (unsigned i=0; i < 128; ++i) {
1542 if (_active_notes[i]) {
1543 _active_notes[i]->set_x1(
1544 trackview.editor().sample_to_pixel(_region->position() + _region->length()));
1551 MidiRegionView::play_midi_note(boost::shared_ptr<NoteType> note)
1553 if (_no_sound_notes || !ARDOUR_UI::config()->get_sound_midi_notes()) {
1557 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1559 if (!route_ui || !route_ui->midi_track()) {
1563 NotePlayer* np = new NotePlayer (route_ui->midi_track ());
1567 /* NotePlayer deletes itself */
1571 MidiRegionView::start_playing_midi_note(boost::shared_ptr<NoteType> note)
1573 const std::vector< boost::shared_ptr<NoteType> > notes(1, note);
1574 start_playing_midi_chord(notes);
1578 MidiRegionView::start_playing_midi_chord (vector<boost::shared_ptr<NoteType> > notes)
1580 if (_no_sound_notes || !ARDOUR_UI::config()->get_sound_midi_notes()) {
1584 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1586 if (!route_ui || !route_ui->midi_track()) {
1590 _note_player = boost::shared_ptr<NotePlayer>(new NotePlayer(route_ui->midi_track()));
1592 for (vector<boost::shared_ptr<NoteType> >::iterator n = notes.begin(); n != notes.end(); ++n) {
1593 _note_player->add (*n);
1596 _note_player->on ();
1601 MidiRegionView::note_in_region_range (const boost::shared_ptr<NoteType> note, bool& visible) const
1603 const framepos_t note_start_frames = source_beats_to_region_frames (note->time());
1604 bool outside = (note_start_frames < 0) || (note_start_frames > _region->last_frame());
1606 visible = (note->note() >= midi_stream_view()->lowest_note()) &&
1607 (note->note() <= midi_stream_view()->highest_note());
1612 /** Update a canvas note's size from its model note.
1613 * @param ev Canvas note to update.
1614 * @param update_ghost_regions true to update the note in any ghost regions that we have, otherwise false.
1617 MidiRegionView::update_note (Note* ev, bool update_ghost_regions)
1619 boost::shared_ptr<NoteType> note = ev->note();
1620 const double x = trackview.editor().sample_to_pixel (source_beats_to_region_frames (note->time()));
1621 const double y0 = 1 + floor(midi_stream_view()->note_to_y(note->note()));
1626 /* trim note display to not overlap the end of its region */
1628 if (note->length() > 0) {
1629 const framepos_t note_end_frames = min (source_beats_to_region_frames (note->end_time()), _region->length());
1630 ev->set_x1 (trackview.editor().sample_to_pixel (note_end_frames));
1632 ev->set_x1 (trackview.editor().sample_to_pixel (_region->length()));
1635 ev->set_y1 (y0 + std::max(1., floor(midi_stream_view()->note_height()) - 1));
1637 if (!note->length()) {
1638 if (_active_notes && note->note() < 128) {
1639 // If this note is already active there's a stuck note,
1640 // finish the old note rectangle
1641 if (_active_notes[note->note()]) {
1642 Note* const old_rect = _active_notes[note->note()];
1643 boost::shared_ptr<NoteType> old_note = old_rect->note();
1644 old_rect->set_x1 (x);
1645 old_rect->set_outline_all ();
1647 _active_notes[note->note()] = ev;
1649 /* outline all but right edge */
1650 ev->set_outline_what (ArdourCanvas::Rectangle::What (
1651 ArdourCanvas::Rectangle::TOP|
1652 ArdourCanvas::Rectangle::LEFT|
1653 ArdourCanvas::Rectangle::BOTTOM));
1655 /* outline all edges */
1656 ev->set_outline_all ();
1659 if (update_ghost_regions) {
1660 for (std::vector<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
1661 MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*i);
1663 gr->update_note (ev);
1670 MidiRegionView::update_hit (Hit* ev)
1672 boost::shared_ptr<NoteType> note = ev->note();
1674 const framepos_t note_start_frames = source_beats_to_region_frames(note->time());
1675 const double x = trackview.editor().sample_to_pixel(note_start_frames);
1676 const double diamond_size = std::max(1., floor(midi_stream_view()->note_height()) - 2.);
1677 const double y = 1.5 + floor(midi_stream_view()->note_to_y(note->note())) + diamond_size * .5;
1679 ev->set_position (ArdourCanvas::Duple (x, y));
1680 ev->set_height (diamond_size);
1683 /** Add a MIDI note to the view (with length).
1685 * If in sustained mode, notes with length 0 will be considered active
1686 * notes, and resolve_note should be called when the corresponding note off
1687 * event arrives, to properly display the note.
1690 MidiRegionView::add_note(const boost::shared_ptr<NoteType> note, bool visible)
1692 NoteBase* event = 0;
1694 if (midi_view()->note_mode() == Sustained) {
1696 Note* ev_rect = new Note (*this, _note_group, note);
1698 update_note (ev_rect);
1702 MidiGhostRegion* gr;
1704 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
1705 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
1706 gr->add_note(ev_rect);
1710 } else if (midi_view()->note_mode() == Percussive) {
1712 const double diamond_size = std::max(1., floor(midi_stream_view()->note_height()) - 2.);
1714 Hit* ev_diamond = new Hit (*this, _note_group, diamond_size, note);
1716 update_hit (ev_diamond);
1725 if (_marked_for_selection.find(note) != _marked_for_selection.end()) {
1726 note_selected(event, true);
1729 if (_marked_for_velocity.find(note) != _marked_for_velocity.end()) {
1730 event->show_velocity();
1733 event->on_channel_selection_change (get_selected_channels());
1734 _events.push_back(event);
1743 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1744 MidiStreamView* const view = mtv->midi_view();
1746 view->update_note_range (note->note());
1750 MidiRegionView::step_add_note (uint8_t channel, uint8_t number, uint8_t velocity,
1751 Evoral::MusicalTime pos, Evoral::MusicalTime len)
1753 boost::shared_ptr<NoteType> new_note (new NoteType (channel, pos, len, number, velocity));
1755 /* potentially extend region to hold new note */
1757 framepos_t end_frame = source_beats_to_absolute_frames (new_note->end_time());
1758 framepos_t region_end = _region->last_frame();
1760 if (end_frame > region_end) {
1761 _region->set_length (end_frame - _region->position());
1764 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1765 MidiStreamView* const view = mtv->midi_view();
1767 view->update_note_range(new_note->note());
1769 _marked_for_selection.clear ();
1772 start_note_diff_command (_("step add"));
1773 note_diff_add_note (new_note, true, false);
1776 // last_step_edit_note = new_note;
1780 MidiRegionView::step_sustain (Evoral::MusicalTime beats)
1782 change_note_lengths (false, false, beats, false, true);
1785 /** Add a new patch change flag to the canvas.
1786 * @param patch the patch change to add
1787 * @param the text to display in the flag
1788 * @param active_channel true to display the flag as on an active channel, false to grey it out for an inactive channel.
1791 MidiRegionView::add_canvas_patch_change (MidiModel::PatchChangePtr patch, const string& displaytext, bool /*active_channel*/)
1793 framecnt_t region_frames = source_beats_to_region_frames (patch->time());
1794 const double x = trackview.editor().sample_to_pixel (region_frames);
1796 double const height = midi_stream_view()->contents_height();
1798 // CAIROCANVAS: active_channel info removed from PatcChange constructor
1799 // so we need to do something more sophisticated to keep its color
1800 // appearance (MidiPatchChangeFill/MidiPatchChangeInactiveChannelFill)
1803 boost::shared_ptr<PatchChange> patch_change = boost::shared_ptr<PatchChange>(
1804 new PatchChange(*this, group,
1811 if (patch_change->item().width() < _pixel_width) {
1812 // Show unless patch change is beyond the region bounds
1813 if (region_frames < 0 || region_frames >= _region->length()) {
1814 patch_change->hide();
1816 patch_change->show();
1819 patch_change->hide ();
1822 _patch_changes.push_back (patch_change);
1825 MIDI::Name::PatchPrimaryKey
1826 MidiRegionView::patch_change_to_patch_key (MidiModel::PatchChangePtr p)
1828 return MIDI::Name::PatchPrimaryKey (p->program(), p->bank());
1831 /// Return true iff @p pc applies to the given time on the given channel.
1833 patch_applies (const ARDOUR::MidiModel::constPatchChangePtr pc, Evoral::MusicalTime time, uint8_t channel)
1835 return pc->time() <= time && pc->channel() == channel;
1839 MidiRegionView::get_patch_key_at (Evoral::MusicalTime time, uint8_t channel, MIDI::Name::PatchPrimaryKey& key) const
1841 // The earliest event not before time
1842 MidiModel::PatchChanges::iterator i = _model->patch_change_lower_bound (time);
1844 // Go backwards until we find the latest PC for this channel, or the start
1845 while (i != _model->patch_changes().begin() &&
1846 (i == _model->patch_changes().end() ||
1847 !patch_applies(*i, time, channel))) {
1851 if (i != _model->patch_changes().end() && patch_applies(*i, time, channel)) {
1852 key.set_bank((*i)->bank());
1853 key.set_program((*i)->program ());
1861 MidiRegionView::change_patch_change (PatchChange& pc, const MIDI::Name::PatchPrimaryKey& new_patch)
1863 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("alter patch change"));
1865 if (pc.patch()->program() != new_patch.program()) {
1866 c->change_program (pc.patch (), new_patch.program());
1869 int const new_bank = new_patch.bank();
1870 if (pc.patch()->bank() != new_bank) {
1871 c->change_bank (pc.patch (), new_bank);
1874 _model->apply_command (*trackview.session(), c);
1876 _patch_changes.clear ();
1877 display_patch_changes ();
1881 MidiRegionView::change_patch_change (MidiModel::PatchChangePtr old_change, const Evoral::PatchChange<Evoral::MusicalTime> & new_change)
1883 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("alter patch change"));
1885 if (old_change->time() != new_change.time()) {
1886 c->change_time (old_change, new_change.time());
1889 if (old_change->channel() != new_change.channel()) {
1890 c->change_channel (old_change, new_change.channel());
1893 if (old_change->program() != new_change.program()) {
1894 c->change_program (old_change, new_change.program());
1897 if (old_change->bank() != new_change.bank()) {
1898 c->change_bank (old_change, new_change.bank());
1901 _model->apply_command (*trackview.session(), c);
1903 _patch_changes.clear ();
1904 display_patch_changes ();
1907 /** Add a patch change to the region.
1908 * @param t Time in frames relative to region position
1909 * @param patch Patch to add; time and channel are ignored (time is converted from t, and channel comes from
1910 * MidiTimeAxisView::get_channel_for_add())
1913 MidiRegionView::add_patch_change (framecnt_t t, Evoral::PatchChange<Evoral::MusicalTime> const & patch)
1915 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1917 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("add patch change"));
1918 c->add (MidiModel::PatchChangePtr (
1919 new Evoral::PatchChange<Evoral::MusicalTime> (
1920 absolute_frames_to_source_beats (_region->position() + t),
1921 mtv->get_channel_for_add(), patch.program(), patch.bank()
1926 _model->apply_command (*trackview.session(), c);
1928 _patch_changes.clear ();
1929 display_patch_changes ();
1933 MidiRegionView::move_patch_change (PatchChange& pc, Evoral::MusicalTime t)
1935 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("move patch change"));
1936 c->change_time (pc.patch (), t);
1937 _model->apply_command (*trackview.session(), c);
1939 _patch_changes.clear ();
1940 display_patch_changes ();
1944 MidiRegionView::delete_patch_change (PatchChange* pc)
1946 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("delete patch change"));
1947 c->remove (pc->patch ());
1948 _model->apply_command (*trackview.session(), c);
1950 _patch_changes.clear ();
1951 display_patch_changes ();
1955 MidiRegionView::step_patch (PatchChange& patch, bool bank, int delta)
1957 MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key(patch.patch());
1959 key.set_bank(key.bank() + delta);
1961 key.set_program(key.program() + delta);
1963 change_patch_change(patch, key);
1967 MidiRegionView::maybe_remove_deleted_note_from_selection (NoteBase* cne)
1969 if (_selection.empty()) {
1973 _selection.erase (cne);
1977 MidiRegionView::delete_selection()
1979 if (_selection.empty()) {
1983 start_note_diff_command (_("delete selection"));
1985 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1986 if ((*i)->selected()) {
1987 _note_diff_command->remove((*i)->note());
1997 MidiRegionView::delete_note (boost::shared_ptr<NoteType> n)
1999 start_note_diff_command (_("delete note"));
2000 _note_diff_command->remove (n);
2003 trackview.editor().verbose_cursor()->hide ();
2007 MidiRegionView::clear_selection_except (NoteBase* ev, bool signal)
2009 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2011 Selection::iterator tmp = i;
2014 (*i)->set_selected (false);
2015 (*i)->hide_velocity ();
2016 _selection.erase (i);
2024 if (!ev && _entered) {
2025 // Clearing selection entirely, ungrab keyboard
2026 Keyboard::magic_widget_drop_focus();
2027 _grabbed_keyboard = false;
2030 /* this does not change the status of this regionview w.r.t the editor
2035 SelectionCleared (this); /* EMIT SIGNAL */
2040 MidiRegionView::unique_select(NoteBase* ev)
2042 const bool selection_was_empty = _selection.empty();
2044 clear_selection_except (ev);
2046 /* don't bother with checking to see if we should remove this
2047 regionview from the editor selection, since we're about to add
2048 another note, and thus put/keep this regionview in the editor
2052 if (!ev->selected()) {
2053 add_to_selection (ev);
2054 if (selection_was_empty && _entered) {
2055 // Grab keyboard for moving notes with arrow keys
2056 Keyboard::magic_widget_grab_focus();
2057 _grabbed_keyboard = true;
2063 MidiRegionView::select_all_notes ()
2067 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2068 add_to_selection (*i);
2073 MidiRegionView::select_range (framepos_t start, framepos_t end)
2077 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2078 framepos_t t = source_beats_to_absolute_frames((*i)->note()->time());
2079 if (t >= start && t <= end) {
2080 add_to_selection (*i);
2086 MidiRegionView::invert_selection ()
2088 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2089 if ((*i)->selected()) {
2090 remove_from_selection(*i);
2092 add_to_selection (*i);
2098 MidiRegionView::select_matching_notes (uint8_t notenum, uint16_t channel_mask, bool add, bool extend)
2100 bool have_selection = !_selection.empty();
2101 uint8_t low_note = 127;
2102 uint8_t high_note = 0;
2103 MidiModel::Notes& notes (_model->notes());
2104 _optimization_iterator = _events.begin();
2106 if (extend && !have_selection) {
2110 /* scan existing selection to get note range */
2112 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2113 if ((*i)->note()->note() < low_note) {
2114 low_note = (*i)->note()->note();
2116 if ((*i)->note()->note() > high_note) {
2117 high_note = (*i)->note()->note();
2124 if (!extend && (low_note == high_note) && (high_note == notenum)) {
2125 /* only note previously selected is the one we are
2126 * reselecting. treat this as cancelling the selection.
2133 low_note = min (low_note, notenum);
2134 high_note = max (high_note, notenum);
2137 _no_sound_notes = true;
2139 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
2141 boost::shared_ptr<NoteType> note (*n);
2143 bool select = false;
2145 if (((1 << note->channel()) & channel_mask) != 0) {
2147 if ((note->note() >= low_note && note->note() <= high_note)) {
2150 } else if (note->note() == notenum) {
2156 if ((cne = find_canvas_note (note)) != 0) {
2157 // extend is false because we've taken care of it,
2158 // since it extends by time range, not pitch.
2159 note_selected (cne, add, false);
2163 add = true; // we need to add all remaining matching notes, even if the passed in value was false (for "set")
2167 _no_sound_notes = false;
2171 MidiRegionView::toggle_matching_notes (uint8_t notenum, uint16_t channel_mask)
2173 MidiModel::Notes& notes (_model->notes());
2174 _optimization_iterator = _events.begin();
2176 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
2178 boost::shared_ptr<NoteType> note (*n);
2181 if (note->note() == notenum && (((0x0001 << note->channel()) & channel_mask) != 0)) {
2182 if ((cne = find_canvas_note (note)) != 0) {
2183 if (cne->selected()) {
2184 note_deselected (cne);
2186 note_selected (cne, true, false);
2194 MidiRegionView::note_selected (NoteBase* ev, bool add, bool extend)
2197 clear_selection_except (ev);
2198 if (!_selection.empty()) {
2199 PublicEditor& editor (trackview.editor());
2200 editor.get_selection().add (this);
2206 if (!ev->selected()) {
2207 add_to_selection (ev);
2211 /* find end of latest note selected, select all between that and the start of "ev" */
2213 Evoral::MusicalTime earliest = Evoral::MaxMusicalTime;
2214 Evoral::MusicalTime latest = Evoral::MusicalTime();
2216 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2217 if ((*i)->note()->end_time() > latest) {
2218 latest = (*i)->note()->end_time();
2220 if ((*i)->note()->time() < earliest) {
2221 earliest = (*i)->note()->time();
2225 if (ev->note()->end_time() > latest) {
2226 latest = ev->note()->end_time();
2229 if (ev->note()->time() < earliest) {
2230 earliest = ev->note()->time();
2233 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2235 /* find notes entirely within OR spanning the earliest..latest range */
2237 if (((*i)->note()->time() >= earliest && (*i)->note()->end_time() <= latest) ||
2238 ((*i)->note()->time() <= earliest && (*i)->note()->end_time() >= latest)) {
2239 add_to_selection (*i);
2247 MidiRegionView::note_deselected(NoteBase* ev)
2249 remove_from_selection (ev);
2253 MidiRegionView::update_drag_selection(framepos_t start, framepos_t end, double gy0, double gy1, bool extend)
2255 PublicEditor& editor = trackview.editor();
2257 // Convert to local coordinates
2258 const framepos_t p = _region->position();
2259 const double y = midi_view()->y_position();
2260 const double x0 = editor.sample_to_pixel(max((framepos_t)0, start - p));
2261 const double x1 = editor.sample_to_pixel(max((framepos_t)0, end - p));
2262 const double y0 = max(0.0, gy0 - y);
2263 const double y1 = max(0.0, gy1 - y);
2265 // TODO: Make this faster by storing the last updated selection rect, and only
2266 // adjusting things that are in the area that appears/disappeared.
2267 // We probably need a tree to be able to find events in O(log(n)) time.
2269 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2270 if ((*i)->x0() < x1 && (*i)->x1() > x0 && (*i)->y0() < y1 && (*i)->y1() > y0) {
2271 // Rectangles intersect
2272 if (!(*i)->selected()) {
2273 add_to_selection (*i);
2275 } else if ((*i)->selected() && !extend) {
2276 // Rectangles do not intersect
2277 remove_from_selection (*i);
2281 typedef RouteTimeAxisView::AutomationTracks ATracks;
2282 typedef std::list<Selectable*> Selectables;
2284 /* Add control points to selection. */
2285 const ATracks& atracks = midi_view()->automation_tracks();
2286 Selectables selectables;
2287 editor.get_selection().clear_points();
2288 for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) {
2289 a->second->get_selectables(start, end, gy0, gy1, selectables);
2290 for (Selectables::const_iterator s = selectables.begin(); s != selectables.end(); ++s) {
2291 ControlPoint* cp = dynamic_cast<ControlPoint*>(*s);
2293 editor.get_selection().add(cp);
2296 a->second->set_selected_points(editor.get_selection().points);
2301 MidiRegionView::update_vertical_drag_selection (double y1, double y2, bool extend)
2307 // TODO: Make this faster by storing the last updated selection rect, and only
2308 // adjusting things that are in the area that appears/disappeared.
2309 // We probably need a tree to be able to find events in O(log(n)) time.
2311 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2312 if (((*i)->y1() >= y1 && (*i)->y1() <= y2)) {
2313 // within y- (note-) range
2314 if (!(*i)->selected()) {
2315 add_to_selection (*i);
2317 } else if ((*i)->selected() && !extend) {
2318 remove_from_selection (*i);
2324 MidiRegionView::remove_from_selection (NoteBase* ev)
2326 Selection::iterator i = _selection.find (ev);
2328 if (i != _selection.end()) {
2329 _selection.erase (i);
2330 if (_selection.empty() && _grabbed_keyboard) {
2332 Keyboard::magic_widget_drop_focus();
2333 _grabbed_keyboard = false;
2337 ev->set_selected (false);
2338 ev->hide_velocity ();
2340 if (_selection.empty()) {
2341 PublicEditor& editor (trackview.editor());
2342 editor.get_selection().remove (this);
2347 MidiRegionView::add_to_selection (NoteBase* ev)
2349 const bool selection_was_empty = _selection.empty();
2351 if (_selection.insert (ev).second) {
2352 ev->set_selected (true);
2353 start_playing_midi_note ((ev)->note());
2354 if (selection_was_empty && _entered) {
2355 // Grab keyboard for moving notes with arrow keys
2356 Keyboard::magic_widget_grab_focus();
2357 _grabbed_keyboard = true;
2361 if (selection_was_empty) {
2362 PublicEditor& editor (trackview.editor());
2363 editor.get_selection().add (this);
2368 MidiRegionView::move_selection(double dx, double dy, double cumulative_dy)
2370 typedef vector<boost::shared_ptr<NoteType> > PossibleChord;
2371 PossibleChord to_play;
2372 Evoral::MusicalTime earliest = Evoral::MaxMusicalTime;
2374 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2375 if ((*i)->note()->time() < earliest) {
2376 earliest = (*i)->note()->time();
2380 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2381 if ((*i)->note()->time() == earliest) {
2382 to_play.push_back ((*i)->note());
2384 (*i)->move_event(dx, dy);
2387 if (dy && !_selection.empty() && !_no_sound_notes && ARDOUR_UI::config()->get_sound_midi_notes()) {
2389 if (to_play.size() > 1) {
2391 PossibleChord shifted;
2393 for (PossibleChord::iterator n = to_play.begin(); n != to_play.end(); ++n) {
2394 boost::shared_ptr<NoteType> moved_note (new NoteType (**n));
2395 moved_note->set_note (moved_note->note() + cumulative_dy);
2396 shifted.push_back (moved_note);
2399 start_playing_midi_chord (shifted);
2401 } else if (!to_play.empty()) {
2403 boost::shared_ptr<NoteType> moved_note (new NoteType (*to_play.front()));
2404 moved_note->set_note (moved_note->note() + cumulative_dy);
2405 start_playing_midi_note (moved_note);
2411 MidiRegionView::note_dropped(NoteBase *, frameoffset_t dt, int8_t dnote)
2413 uint8_t lowest_note_in_selection = 127;
2414 uint8_t highest_note_in_selection = 0;
2415 uint8_t highest_note_difference = 0;
2417 // find highest and lowest notes first
2419 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2420 uint8_t pitch = (*i)->note()->note();
2421 lowest_note_in_selection = std::min(lowest_note_in_selection, pitch);
2422 highest_note_in_selection = std::max(highest_note_in_selection, pitch);
2426 cerr << "dnote: " << (int) dnote << endl;
2427 cerr << "lowest note (streamview): " << int(midi_stream_view()->lowest_note())
2428 << " highest note (streamview): " << int(midi_stream_view()->highest_note()) << endl;
2429 cerr << "lowest note (selection): " << int(lowest_note_in_selection) << " highest note(selection): "
2430 << int(highest_note_in_selection) << endl;
2431 cerr << "selection size: " << _selection.size() << endl;
2432 cerr << "Highest note in selection: " << (int) highest_note_in_selection << endl;
2435 // Make sure the note pitch does not exceed the MIDI standard range
2436 if (highest_note_in_selection + dnote > 127) {
2437 highest_note_difference = highest_note_in_selection - 127;
2440 start_note_diff_command (_("move notes"));
2442 for (Selection::iterator i = _selection.begin(); i != _selection.end() ; ++i) {
2444 framepos_t new_frames = source_beats_to_absolute_frames ((*i)->note()->time()) + dt;
2445 Evoral::MusicalTime new_time = absolute_frames_to_source_beats (new_frames);
2451 note_diff_add_change (*i, MidiModel::NoteDiffCommand::StartTime, new_time);
2453 uint8_t original_pitch = (*i)->note()->note();
2454 uint8_t new_pitch = original_pitch + dnote - highest_note_difference;
2456 // keep notes in standard midi range
2457 clamp_to_0_127(new_pitch);
2459 lowest_note_in_selection = std::min(lowest_note_in_selection, new_pitch);
2460 highest_note_in_selection = std::max(highest_note_in_selection, new_pitch);
2462 note_diff_add_change (*i, MidiModel::NoteDiffCommand::NoteNumber, new_pitch);
2467 // care about notes being moved beyond the upper/lower bounds on the canvas
2468 if (lowest_note_in_selection < midi_stream_view()->lowest_note() ||
2469 highest_note_in_selection > midi_stream_view()->highest_note()) {
2470 midi_stream_view()->set_note_range(MidiStreamView::ContentsRange);
2474 /** @param x Pixel relative to the region position.
2475 * @return Snapped frame relative to the region position.
2478 MidiRegionView::snap_pixel_to_sample(double x)
2480 PublicEditor& editor (trackview.editor());
2481 return snap_frame_to_frame (editor.pixel_to_sample (x));
2484 /** @param x Pixel relative to the region position.
2485 * @return Snapped pixel relative to the region position.
2488 MidiRegionView::snap_to_pixel(double x)
2490 return (double) trackview.editor().sample_to_pixel(snap_pixel_to_sample(x));
2494 MidiRegionView::get_position_pixels()
2496 framepos_t region_frame = get_position();
2497 return trackview.editor().sample_to_pixel(region_frame);
2501 MidiRegionView::get_end_position_pixels()
2503 framepos_t frame = get_position() + get_duration ();
2504 return trackview.editor().sample_to_pixel(frame);
2508 MidiRegionView::source_beats_to_absolute_frames(Evoral::MusicalTime beats) const
2510 /* the time converter will return the frame corresponding to `beats'
2511 relative to the start of the source. The start of the source
2512 is an implied position given by region->position - region->start
2514 const framepos_t source_start = _region->position() - _region->start();
2515 return source_start + _source_relative_time_converter.to (beats);
2519 MidiRegionView::absolute_frames_to_source_beats(framepos_t frames) const
2521 /* the `frames' argument needs to be converted into a frame count
2522 relative to the start of the source before being passed in to the
2525 const framepos_t source_start = _region->position() - _region->start();
2526 return _source_relative_time_converter.from (frames - source_start);
2530 MidiRegionView::region_beats_to_region_frames(Evoral::MusicalTime beats) const
2532 return _region_relative_time_converter.to(beats);
2536 MidiRegionView::region_frames_to_region_beats(framepos_t frames) const
2538 return _region_relative_time_converter.from(frames);
2542 MidiRegionView::begin_resizing (bool /*at_front*/)
2544 _resize_data.clear();
2546 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2547 Note *note = dynamic_cast<Note*> (*i);
2549 // only insert CanvasNotes into the map
2551 NoteResizeData *resize_data = new NoteResizeData();
2552 resize_data->note = note;
2554 // create a new SimpleRect from the note which will be the resize preview
2555 ArdourCanvas::Rectangle *resize_rect = new ArdourCanvas::Rectangle (_note_group,
2556 ArdourCanvas::Rect (note->x0(), note->y0(), note->x0(), note->y1()));
2558 // calculate the colors: get the color settings
2559 uint32_t fill_color = UINT_RGBA_CHANGE_A(
2560 ARDOUR_UI::config()->color ("midi note selected"),
2563 // make the resize preview notes more transparent and bright
2564 fill_color = UINT_INTERPOLATE(fill_color, 0xFFFFFF40, 0.5);
2566 // calculate color based on note velocity
2567 resize_rect->set_fill_color (UINT_INTERPOLATE(
2568 NoteBase::meter_style_fill_color(note->note()->velocity(), note->selected()),
2572 resize_rect->set_outline_color (NoteBase::calculate_outline (
2573 ARDOUR_UI::config()->color ("midi note selected")));
2575 resize_data->resize_rect = resize_rect;
2576 _resize_data.push_back(resize_data);
2581 /** Update resizing notes while user drags.
2582 * @param primary `primary' note for the drag; ie the one that is used as the reference in non-relative mode.
2583 * @param at_front which end of the note (true == note on, false == note off)
2584 * @param delta_x change in mouse position since the start of the drag
2585 * @param relative true if relative resizing is taking place, false if absolute resizing. This only makes
2586 * a difference when multiple notes are being resized; in relative mode, each note's length is changed by the
2587 * amount of the drag. In non-relative mode, all selected notes are set to have the same start or end point
2588 * as the \a primary note.
2591 MidiRegionView::update_resizing (NoteBase* primary, bool at_front, double delta_x, bool relative)
2593 bool cursor_set = false;
2595 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2596 ArdourCanvas::Rectangle* resize_rect = (*i)->resize_rect;
2597 Note* canvas_note = (*i)->note;
2602 current_x = canvas_note->x0() + delta_x;
2604 current_x = primary->x0() + delta_x;
2608 current_x = canvas_note->x1() + delta_x;
2610 current_x = primary->x1() + delta_x;
2614 if (current_x < 0) {
2615 // This works even with snapping because RegionView::snap_frame_to_frame()
2616 // snaps forward if the snapped sample is before the beginning of the region
2619 if (current_x > trackview.editor().sample_to_pixel(_region->length())) {
2620 current_x = trackview.editor().sample_to_pixel(_region->length());
2624 resize_rect->set_x0 (snap_to_pixel(current_x));
2625 resize_rect->set_x1 (canvas_note->x1());
2627 resize_rect->set_x1 (snap_to_pixel(current_x));
2628 resize_rect->set_x0 (canvas_note->x0());
2632 const double snapped_x = snap_pixel_to_sample (current_x);
2633 Evoral::MusicalTime beats = region_frames_to_region_beats (snapped_x);
2634 Evoral::MusicalTime len = Evoral::MusicalTime();
2637 if (beats < canvas_note->note()->end_time()) {
2638 len = canvas_note->note()->time() - beats;
2639 len += canvas_note->note()->length();
2642 if (beats >= canvas_note->note()->time()) {
2643 len = beats - canvas_note->note()->time();
2648 snprintf (buf, sizeof (buf), "%.3g beats", len.to_double());
2649 show_verbose_cursor (buf, 0, 0);
2658 /** Finish resizing notes when the user releases the mouse button.
2659 * Parameters the same as for \a update_resizing().
2662 MidiRegionView::commit_resizing (NoteBase* primary, bool at_front, double delta_x, bool relative)
2664 start_note_diff_command (_("resize notes"));
2666 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2667 Note* canvas_note = (*i)->note;
2668 ArdourCanvas::Rectangle* resize_rect = (*i)->resize_rect;
2670 /* Get the new x position for this resize, which is in pixels relative
2671 * to the region position.
2678 current_x = canvas_note->x0() + delta_x;
2680 current_x = primary->x0() + delta_x;
2684 current_x = canvas_note->x1() + delta_x;
2686 current_x = primary->x1() + delta_x;
2690 if (current_x < 0) {
2693 if (current_x > trackview.editor().sample_to_pixel(_region->length())) {
2694 current_x = trackview.editor().sample_to_pixel(_region->length());
2697 /* Convert that to a frame within the source */
2698 current_x = snap_pixel_to_sample (current_x) + _region->start ();
2700 /* and then to beats */
2701 const Evoral::MusicalTime x_beats = region_frames_to_region_beats (current_x);
2703 if (at_front && x_beats < canvas_note->note()->end_time()) {
2704 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::StartTime, x_beats);
2706 Evoral::MusicalTime len = canvas_note->note()->time() - x_beats;
2707 len += canvas_note->note()->length();
2710 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
2715 const Evoral::MusicalTime len = x_beats - canvas_note->note()->time();
2718 /* XXX convert to beats */
2719 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
2727 _resize_data.clear();
2732 MidiRegionView::abort_resizing ()
2734 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2735 delete (*i)->resize_rect;
2739 _resize_data.clear ();
2743 MidiRegionView::change_note_velocity(NoteBase* event, int8_t velocity, bool relative)
2745 uint8_t new_velocity;
2748 new_velocity = event->note()->velocity() + velocity;
2749 clamp_to_0_127(new_velocity);
2751 new_velocity = velocity;
2754 event->set_selected (event->selected()); // change color
2756 note_diff_add_change (event, MidiModel::NoteDiffCommand::Velocity, new_velocity);
2760 MidiRegionView::change_note_note (NoteBase* event, int8_t note, bool relative)
2765 new_note = event->note()->note() + note;
2770 clamp_to_0_127 (new_note);
2771 note_diff_add_change (event, MidiModel::NoteDiffCommand::NoteNumber, new_note);
2775 MidiRegionView::trim_note (NoteBase* event, Evoral::MusicalTime front_delta, Evoral::MusicalTime end_delta)
2777 bool change_start = false;
2778 bool change_length = false;
2779 Evoral::MusicalTime new_start;
2780 Evoral::MusicalTime new_length;
2782 /* NOTE: the semantics of the two delta arguments are slightly subtle:
2784 front_delta: if positive - move the start of the note later in time (shortening it)
2785 if negative - move the start of the note earlier in time (lengthening it)
2787 end_delta: if positive - move the end of the note later in time (lengthening it)
2788 if negative - move the end of the note earlier in time (shortening it)
2791 if (!!front_delta) {
2792 if (front_delta < 0) {
2794 if (event->note()->time() < -front_delta) {
2795 new_start = Evoral::MusicalTime();
2797 new_start = event->note()->time() + front_delta; // moves earlier
2800 /* start moved toward zero, so move the end point out to where it used to be.
2801 Note that front_delta is negative, so this increases the length.
2804 new_length = event->note()->length() - front_delta;
2805 change_start = true;
2806 change_length = true;
2810 Evoral::MusicalTime new_pos = event->note()->time() + front_delta;
2812 if (new_pos < event->note()->end_time()) {
2813 new_start = event->note()->time() + front_delta;
2814 /* start moved toward the end, so move the end point back to where it used to be */
2815 new_length = event->note()->length() - front_delta;
2816 change_start = true;
2817 change_length = true;
2824 bool can_change = true;
2825 if (end_delta < 0) {
2826 if (event->note()->length() < -end_delta) {
2832 new_length = event->note()->length() + end_delta;
2833 change_length = true;
2838 note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_start);
2841 if (change_length) {
2842 note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, new_length);
2847 MidiRegionView::change_note_channel (NoteBase* event, int8_t chn, bool relative)
2849 uint8_t new_channel;
2853 if (event->note()->channel() < -chn) {
2856 new_channel = event->note()->channel() + chn;
2859 new_channel = event->note()->channel() + chn;
2862 new_channel = (uint8_t) chn;
2865 note_diff_add_change (event, MidiModel::NoteDiffCommand::Channel, new_channel);
2869 MidiRegionView::change_note_time (NoteBase* event, Evoral::MusicalTime delta, bool relative)
2871 Evoral::MusicalTime new_time;
2875 if (event->note()->time() < -delta) {
2876 new_time = Evoral::MusicalTime();
2878 new_time = event->note()->time() + delta;
2881 new_time = event->note()->time() + delta;
2887 note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_time);
2891 MidiRegionView::change_note_length (NoteBase* event, Evoral::MusicalTime t)
2893 note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, t);
2897 MidiRegionView::change_velocities (bool up, bool fine, bool allow_smush, bool all_together)
2902 if (_selection.empty()) {
2917 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2918 if ((*i)->note()->velocity() < -delta || (*i)->note()->velocity() + delta > 127) {
2924 start_note_diff_command (_("change velocities"));
2926 for (Selection::iterator i = _selection.begin(); i != _selection.end();) {
2927 Selection::iterator next = i;
2931 if (i == _selection.begin()) {
2932 change_note_velocity (*i, delta, true);
2933 value = (*i)->note()->velocity() + delta;
2935 change_note_velocity (*i, value, false);
2939 change_note_velocity (*i, delta, true);
2948 if (!_selection.empty()) {
2950 snprintf (buf, sizeof (buf), "Vel %d",
2951 (int) (*_selection.begin())->note()->velocity());
2952 show_verbose_cursor (buf, 10, 10);
2958 MidiRegionView::transpose (bool up, bool fine, bool allow_smush)
2960 if (_selection.empty()) {
2977 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2979 if ((int8_t) (*i)->note()->note() + delta <= 0) {
2983 if ((int8_t) (*i)->note()->note() + delta > 127) {
2990 start_note_diff_command (_("transpose"));
2992 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2993 Selection::iterator next = i;
2995 change_note_note (*i, delta, true);
3003 MidiRegionView::change_note_lengths (bool fine, bool shorter, Evoral::MusicalTime delta, bool start, bool end)
3007 delta = Evoral::MusicalTime(1.0/128.0);
3009 /* grab the current grid distance */
3010 delta = get_grid_beats(_region->position());
3018 start_note_diff_command (_("change note lengths"));
3020 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3021 Selection::iterator next = i;
3024 /* note the negation of the delta for start */
3027 (start ? -delta : Evoral::MusicalTime()),
3028 (end ? delta : Evoral::MusicalTime()));
3037 MidiRegionView::nudge_notes (bool forward, bool fine)
3039 if (_selection.empty()) {
3043 /* pick a note as the point along the timeline to get the nudge distance.
3044 its not necessarily the earliest note, so we may want to pull the notes out
3045 into a vector and sort before using the first one.
3048 const framepos_t ref_point = source_beats_to_absolute_frames ((*(_selection.begin()))->note()->time());
3049 Evoral::MusicalTime delta;
3053 /* non-fine, move by 1 bar regardless of snap */
3054 delta = Evoral::MusicalTime(trackview.session()->tempo_map().meter_at(ref_point).divisions_per_bar());
3056 } else if (trackview.editor().snap_mode() == Editing::SnapOff) {
3058 /* grid is off - use nudge distance */
3061 const framecnt_t distance = trackview.editor().get_nudge_distance (ref_point, unused);
3062 delta = region_frames_to_region_beats (fabs ((double)distance));
3068 framepos_t next_pos = ref_point;
3071 if (max_framepos - 1 < next_pos) {
3075 if (next_pos == 0) {
3081 trackview.editor().snap_to (next_pos, (forward ? RoundUpAlways : RoundDownAlways), false);
3082 const framecnt_t distance = ref_point - next_pos;
3083 delta = region_frames_to_region_beats (fabs ((double)distance));
3094 start_note_diff_command (_("nudge"));
3096 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3097 Selection::iterator next = i;
3099 change_note_time (*i, delta, true);
3107 MidiRegionView::change_channel(uint8_t channel)
3109 start_note_diff_command(_("change channel"));
3110 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3111 note_diff_add_change (*i, MidiModel::NoteDiffCommand::Channel, channel);
3119 MidiRegionView::note_entered(NoteBase* ev)
3121 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3123 if (_mouse_state == SelectTouchDragging) {
3124 note_selected (ev, true);
3125 } else if (editor->current_mouse_mode() == MouseContent) {
3126 show_verbose_cursor (ev->note ());
3127 } else if (editor->current_mouse_mode() == MouseDraw) {
3128 show_verbose_cursor (ev->note ());
3133 MidiRegionView::note_left (NoteBase*)
3135 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3137 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3138 (*i)->hide_velocity ();
3141 editor->verbose_cursor()->hide ();
3145 MidiRegionView::patch_entered (PatchChange* p)
3148 /* XXX should get patch name if we can */
3149 s << _("Bank ") << (p->patch()->bank() + MIDI_BP_ZERO) << '\n'
3150 << _("Program ") << ((int) p->patch()->program()) + MIDI_BP_ZERO << '\n'
3151 << _("Channel ") << ((int) p->patch()->channel() + 1);
3152 show_verbose_cursor (s.str(), 10, 20);
3153 p->item().grab_focus();
3157 MidiRegionView::patch_left (PatchChange *)
3159 trackview.editor().verbose_cursor()->hide ();
3160 /* focus will transfer back via the enter-notify event sent to this
3166 MidiRegionView::sysex_entered (SysEx* p)
3170 // need a way to extract text from p->_flag->_text
3172 // show_verbose_cursor (s.str(), 10, 20);
3173 p->item().grab_focus();
3177 MidiRegionView::sysex_left (SysEx *)
3179 trackview.editor().verbose_cursor()->hide ();
3180 /* focus will transfer back via the enter-notify event sent to this
3186 MidiRegionView::note_mouse_position (float x_fraction, float /*y_fraction*/, bool can_set_cursor)
3188 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3189 Editing::MouseMode mm = editor->current_mouse_mode();
3190 bool trimmable = (mm == MouseContent || mm == MouseTimeFX || mm == MouseDraw);
3192 Editor::EnterContext* ctx = editor->get_enter_context(NoteItem);
3193 if (can_set_cursor && ctx) {
3194 if (trimmable && x_fraction > 0.0 && x_fraction < 0.2) {
3195 ctx->cursor_ctx->change(editor->cursors()->left_side_trim);
3196 } else if (trimmable && x_fraction >= 0.8 && x_fraction < 1.0) {
3197 ctx->cursor_ctx->change(editor->cursors()->right_side_trim);
3199 ctx->cursor_ctx->change(editor->cursors()->grabber_note);
3205 MidiRegionView::get_fill_color() const
3207 const std::string mod_name = (_dragging ? "dragging region" :
3208 trackview.editor().internal_editing() ? "editable region" :
3211 return ARDOUR_UI::config()->color_mod ("selected region base", mod_name);
3212 } else if (high_enough_for_name || !ARDOUR_UI::config()->get_color_regions_using_track_color()) {
3213 return ARDOUR_UI::config()->color_mod ("midi frame base", mod_name);
3215 return ARDOUR_UI::config()->color_mod (fill_color, mod_name);
3219 MidiRegionView::midi_channel_mode_changed ()
3221 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3222 uint16_t mask = mtv->midi_track()->get_playback_channel_mask();
3223 ChannelMode mode = mtv->midi_track()->get_playback_channel_mode ();
3225 if (mode == ForceChannel) {
3226 mask = 0xFFFF; // Show all notes as active (below)
3229 // Update notes for selection
3230 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3231 (*i)->on_channel_selection_change (mask);
3234 _patch_changes.clear ();
3235 display_patch_changes ();
3239 MidiRegionView::instrument_settings_changed ()
3245 MidiRegionView::cut_copy_clear (Editing::CutCopyOp op)
3247 if (_selection.empty()) {
3251 PublicEditor& editor (trackview.editor());
3255 /* XXX what to do ? */
3259 editor.get_cut_buffer().add (selection_as_cut_buffer());
3267 start_note_diff_command();
3269 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3276 note_diff_remove_note (*i);
3286 MidiRegionView::selection_as_cut_buffer () const
3290 for (Selection::const_iterator i = _selection.begin(); i != _selection.end(); ++i) {
3291 NoteType* n = (*i)->note().get();
3292 notes.insert (boost::shared_ptr<NoteType> (new NoteType (*n)));
3295 MidiCutBuffer* cb = new MidiCutBuffer (trackview.session());
3301 /** This method handles undo */
3303 MidiRegionView::paste (framepos_t pos, const ::Selection& selection, PasteContext& ctx)
3305 // Paste notes, if available
3306 MidiNoteSelection::const_iterator m = selection.midi_notes.get_nth(ctx.counts.n_notes());
3307 if (m != selection.midi_notes.end()) {
3308 ctx.counts.increase_n_notes();
3309 paste_internal(pos, ctx.count, ctx.times, **m);
3312 // Paste control points to automation children, if available
3313 typedef RouteTimeAxisView::AutomationTracks ATracks;
3314 const ATracks& atracks = midi_view()->automation_tracks();
3315 for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) {
3316 a->second->paste(pos, selection, ctx);
3322 /** This method handles undo */
3324 MidiRegionView::paste_internal (framepos_t pos, unsigned paste_count, float times, const MidiCutBuffer& mcb)
3330 start_note_diff_command (_("paste"));
3332 const Evoral::MusicalTime snap_beats = get_grid_beats(pos);
3333 const Evoral::MusicalTime first_time = (*mcb.notes().begin())->time();
3334 const Evoral::MusicalTime last_time = (*mcb.notes().rbegin())->end_time();
3335 const Evoral::MusicalTime duration = last_time - first_time;
3336 const Evoral::MusicalTime snap_duration = duration.snap_to(snap_beats);
3337 const Evoral::MusicalTime paste_offset = snap_duration * paste_count;
3338 const Evoral::MusicalTime pos_beats = absolute_frames_to_source_beats(pos) + paste_offset;
3339 Evoral::MusicalTime end_point = Evoral::MusicalTime();
3341 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste data spans from %1 to %2 (%3) ; paste pos beats = %4 (based on %5 - %6)\n",
3344 duration, pos, _region->position(),
3349 for (int n = 0; n < (int) times; ++n) {
3351 for (Notes::const_iterator i = mcb.notes().begin(); i != mcb.notes().end(); ++i) {
3353 boost::shared_ptr<NoteType> copied_note (new NoteType (*((*i).get())));
3354 copied_note->set_time (pos_beats + copied_note->time() - first_time);
3356 /* make all newly added notes selected */
3358 note_diff_add_note (copied_note, true);
3359 end_point = copied_note->end_time();
3363 /* if we pasted past the current end of the region, extend the region */
3365 framepos_t end_frame = source_beats_to_absolute_frames (end_point);
3366 framepos_t region_end = _region->position() + _region->length() - 1;
3368 if (end_frame > region_end) {
3370 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste extended region from %1 to %2\n", region_end, end_frame));
3372 _region->clear_changes ();
3373 _region->set_length (end_frame - _region->position());
3374 trackview.session()->add_command (new StatefulDiffCommand (_region));
3380 struct EventNoteTimeEarlyFirstComparator {
3381 bool operator() (NoteBase* a, NoteBase* b) {
3382 return a->note()->time() < b->note()->time();
3387 MidiRegionView::time_sort_events ()
3389 if (!_sort_needed) {
3393 EventNoteTimeEarlyFirstComparator cmp;
3396 _sort_needed = false;
3400 MidiRegionView::goto_next_note (bool add_to_selection)
3402 bool use_next = false;
3404 if (_events.back()->selected()) {
3408 time_sort_events ();
3410 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3411 uint16_t const channel_mask = mtv->midi_track()->get_playback_channel_mask();
3413 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3414 if ((*i)->selected()) {
3417 } else if (use_next) {
3418 if (channel_mask & (1 << (*i)->note()->channel())) {
3419 if (!add_to_selection) {
3422 note_selected (*i, true, false);
3429 /* use the first one */
3431 if (!_events.empty() && (channel_mask & (1 << _events.front()->note()->channel ()))) {
3432 unique_select (_events.front());
3437 MidiRegionView::goto_previous_note (bool add_to_selection)
3439 bool use_next = false;
3441 if (_events.front()->selected()) {
3445 time_sort_events ();
3447 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3448 uint16_t const channel_mask = mtv->midi_track()->get_playback_channel_mask ();
3450 for (Events::reverse_iterator i = _events.rbegin(); i != _events.rend(); ++i) {
3451 if ((*i)->selected()) {
3454 } else if (use_next) {
3455 if (channel_mask & (1 << (*i)->note()->channel())) {
3456 if (!add_to_selection) {
3459 note_selected (*i, true, false);
3466 /* use the last one */
3468 if (!_events.empty() && (channel_mask & (1 << (*_events.rbegin())->note()->channel ()))) {
3469 unique_select (*(_events.rbegin()));
3474 MidiRegionView::selection_as_notelist (Notes& selected, bool allow_all_if_none_selected)
3476 bool had_selected = false;
3478 time_sort_events ();
3480 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3481 if ((*i)->selected()) {
3482 selected.insert ((*i)->note());
3483 had_selected = true;
3487 if (allow_all_if_none_selected && !had_selected) {
3488 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3489 selected.insert ((*i)->note());
3495 MidiRegionView::update_ghost_note (double x, double y)
3497 x = std::max(0.0, x);
3499 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3504 _note_group->canvas_to_item (x, y);
3506 PublicEditor& editor = trackview.editor ();
3508 framepos_t const unsnapped_frame = editor.pixel_to_sample (x);
3509 framecnt_t grid_frames;
3510 framepos_t const f = snap_frame_to_grid_underneath (unsnapped_frame, grid_frames);
3512 /* use region_frames... because we are converting a delta within the region
3515 const Evoral::MusicalTime length = get_grid_beats(unsnapped_frame);
3517 /* note that this sets the time of the ghost note in beats relative to
3518 the start of the source; that is how all note times are stored.
3520 _ghost_note->note()->set_time (
3521 std::max(Evoral::MusicalTime(),
3522 absolute_frames_to_source_beats (f + _region->position ())));
3523 _ghost_note->note()->set_length (length);
3524 _ghost_note->note()->set_note (midi_stream_view()->y_to_note (y));
3525 _ghost_note->note()->set_channel (mtv->get_channel_for_add ());
3527 /* the ghost note does not appear in ghost regions, so pass false in here */
3528 update_note (_ghost_note, false);
3530 show_verbose_cursor (_ghost_note->note ());
3534 MidiRegionView::create_ghost_note (double x, double y)
3536 remove_ghost_note ();
3538 boost::shared_ptr<NoteType> g (new NoteType);
3539 _ghost_note = new Note (*this, _note_group, g);
3540 _ghost_note->set_ignore_events (true);
3541 _ghost_note->set_outline_color (0x000000aa);
3542 update_ghost_note (x, y);
3543 _ghost_note->show ();
3545 show_verbose_cursor (_ghost_note->note ());
3549 MidiRegionView::remove_ghost_note ()
3556 MidiRegionView::snap_changed ()
3562 create_ghost_note (_last_ghost_x, _last_ghost_y);
3566 MidiRegionView::drop_down_keys ()
3568 _mouse_state = None;
3572 MidiRegionView::maybe_select_by_position (GdkEventButton* ev, double /*x*/, double y)
3574 /* XXX: This is dead code. What was it for? */
3576 double note = midi_stream_view()->y_to_note(y);
3578 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3580 uint16_t chn_mask = mtv->midi_track()->get_playback_channel_mask();
3582 if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
3583 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchGreaterThanOrEqual, (uint8_t) floor (note), chn_mask);
3584 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
3585 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchLessThanOrEqual, (uint8_t) floor (note), chn_mask);
3590 bool add_mrv_selection = false;
3592 if (_selection.empty()) {
3593 add_mrv_selection = true;
3596 for (Events::iterator i = e.begin(); i != e.end(); ++i) {
3597 if (_selection.insert (*i).second) {
3598 (*i)->set_selected (true);
3602 if (add_mrv_selection) {
3603 PublicEditor& editor (trackview.editor());
3604 editor.get_selection().add (this);
3609 MidiRegionView::color_handler ()
3611 RegionView::color_handler ();
3613 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3614 (*i)->set_selected ((*i)->selected()); // will change color
3617 /* XXX probably more to do here */
3621 MidiRegionView::enable_display (bool yn)
3623 RegionView::enable_display (yn);
3630 MidiRegionView::show_step_edit_cursor (Evoral::MusicalTime pos)
3632 if (_step_edit_cursor == 0) {
3633 ArdourCanvas::Item* const group = get_canvas_group();
3635 _step_edit_cursor = new ArdourCanvas::Rectangle (group);
3636 _step_edit_cursor->set_y0 (0);
3637 _step_edit_cursor->set_y1 (midi_stream_view()->contents_height());
3638 _step_edit_cursor->set_fill_color (RGBA_TO_UINT (45,0,0,90));
3639 _step_edit_cursor->set_outline_color (RGBA_TO_UINT (85,0,0,90));
3642 move_step_edit_cursor (pos);
3643 _step_edit_cursor->show ();
3647 MidiRegionView::move_step_edit_cursor (Evoral::MusicalTime pos)
3649 _step_edit_cursor_position = pos;
3651 if (_step_edit_cursor) {
3652 double pixel = trackview.editor().sample_to_pixel (region_beats_to_region_frames (pos));
3653 _step_edit_cursor->set_x0 (pixel);
3654 set_step_edit_cursor_width (_step_edit_cursor_width);
3659 MidiRegionView::hide_step_edit_cursor ()
3661 if (_step_edit_cursor) {
3662 _step_edit_cursor->hide ();
3667 MidiRegionView::set_step_edit_cursor_width (Evoral::MusicalTime beats)
3669 _step_edit_cursor_width = beats;
3671 if (_step_edit_cursor) {
3672 _step_edit_cursor->set_x1 (_step_edit_cursor->x0() + trackview.editor().sample_to_pixel (region_beats_to_region_frames (beats)));
3676 /** Called when a diskstream on our track has received some data. Update the view, if applicable.
3677 * @param w Source that the data will end up in.
3680 MidiRegionView::data_recorded (boost::weak_ptr<MidiSource> w)
3682 if (!_active_notes) {
3683 /* we aren't actively being recorded to */
3687 boost::shared_ptr<MidiSource> src = w.lock ();
3688 if (!src || src != midi_region()->midi_source()) {
3689 /* recorded data was not destined for our source */
3693 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (&trackview);
3695 boost::shared_ptr<MidiBuffer> buf = mtv->midi_track()->get_gui_feed_buffer ();
3697 framepos_t back = max_framepos;
3699 for (MidiBuffer::iterator i = buf->begin(); i != buf->end(); ++i) {
3700 Evoral::MIDIEvent<MidiBuffer::TimeType> const ev (*i, false);
3702 if (ev.is_channel_event()) {
3703 if (get_channel_mode() == FilterChannels) {
3704 if (((uint16_t(1) << ev.channel()) & get_selected_channels()) == 0) {
3710 /* convert from session frames to source beats */
3711 Evoral::MusicalTime const time_beats = _source_relative_time_converter.from(ev.time());
3713 if (ev.type() == MIDI_CMD_NOTE_ON) {
3714 boost::shared_ptr<NoteType> note (
3715 new NoteType (ev.channel(), time_beats, Evoral::MusicalTime(), ev.note(), ev.velocity()));
3717 add_note (note, true);
3719 /* fix up our note range */
3720 if (ev.note() < _current_range_min) {
3721 midi_stream_view()->apply_note_range (ev.note(), _current_range_max, true);
3722 } else if (ev.note() > _current_range_max) {
3723 midi_stream_view()->apply_note_range (_current_range_min, ev.note(), true);
3726 } else if (ev.type() == MIDI_CMD_NOTE_OFF) {
3727 resolve_note (ev.note (), time_beats);
3733 midi_stream_view()->check_record_layers (region(), back);
3737 MidiRegionView::trim_front_starting ()
3739 /* Reparent the note group to the region view's parent, so that it doesn't change
3740 when the region view is trimmed.
3742 _temporary_note_group = new ArdourCanvas::Container (group->parent ());
3743 _temporary_note_group->move (group->position ());
3744 _note_group->reparent (_temporary_note_group);
3748 MidiRegionView::trim_front_ending ()
3750 _note_group->reparent (group);
3751 delete _temporary_note_group;
3752 _temporary_note_group = 0;
3754 if (_region->start() < 0) {
3755 /* Trim drag made start time -ve; fix this */
3756 midi_region()->fix_negative_start ();
3761 MidiRegionView::edit_patch_change (PatchChange* pc)
3763 PatchChangeDialog d (&_source_relative_time_converter, trackview.session(), *pc->patch (), instrument_info(), Gtk::Stock::APPLY, true);
3765 int response = d.run();
3768 case Gtk::RESPONSE_ACCEPT:
3770 case Gtk::RESPONSE_REJECT:
3771 delete_patch_change (pc);
3777 change_patch_change (pc->patch(), d.patch ());
3781 MidiRegionView::delete_sysex (SysEx* /*sysex*/)
3784 // sysyex object doesn't have a pointer to a sysex event
3785 // MidiModel::SysExDiffCommand* c = _model->new_sysex_diff_command (_("delete sysex"));
3786 // c->remove (sysex->sysex());
3787 // _model->apply_command (*trackview.session(), c);
3789 //_sys_exes.clear ();
3790 // display_sysexes();
3794 MidiRegionView::show_verbose_cursor (boost::shared_ptr<NoteType> n) const
3796 using namespace MIDI::Name;
3800 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3802 boost::shared_ptr<MasterDeviceNames> device_names(mtv->get_device_names());
3804 MIDI::Name::PatchPrimaryKey patch_key;
3805 get_patch_key_at(n->time(), n->channel(), patch_key);
3806 name = device_names->note_name(mtv->gui_property(X_("midnam-custom-device-mode")),
3809 patch_key.program(),
3815 snprintf (buf, sizeof (buf), "%d %s\nCh %d Vel %d",
3817 name.empty() ? Evoral::midi_note_name (n->note()).c_str() : name.c_str(),
3818 (int) n->channel() + 1,
3819 (int) n->velocity());
3821 show_verbose_cursor(buf, 10, 20);
3825 MidiRegionView::show_verbose_cursor (string const & text, double xoffset, double yoffset) const
3827 trackview.editor().verbose_cursor()->set (text);
3828 trackview.editor().verbose_cursor()->show ();
3829 trackview.editor().verbose_cursor()->set_offset (ArdourCanvas::Duple (xoffset, yoffset));
3832 /** @param p A session framepos.
3833 * @param grid_frames Filled in with the number of frames that a grid interval is at p.
3834 * @return p snapped to the grid subdivision underneath it.
3837 MidiRegionView::snap_frame_to_grid_underneath (framepos_t p, framecnt_t& grid_frames) const
3839 PublicEditor& editor = trackview.editor ();
3841 const Evoral::MusicalTime grid_beats = get_grid_beats(p);
3843 grid_frames = region_beats_to_region_frames (grid_beats);
3845 /* Hack so that we always snap to the note that we are over, instead of snapping
3846 to the next one if we're more than halfway through the one we're over.
3848 if (editor.snap_mode() == SnapNormal && p >= grid_frames / 2) {
3849 p -= grid_frames / 2;
3852 return snap_frame_to_frame (p);
3855 /** Called when the selection has been cleared in any MidiRegionView.
3856 * @param rv MidiRegionView that the selection was cleared in.
3859 MidiRegionView::selection_cleared (MidiRegionView* rv)
3865 /* Clear our selection in sympathy; but don't signal the fact */
3866 clear_selection (false);
3870 MidiRegionView::note_button_release ()
3872 _note_player.reset();
3876 MidiRegionView::get_channel_mode () const
3878 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (&trackview);
3879 return rtav->midi_track()->get_playback_channel_mode();
3883 MidiRegionView::get_selected_channels () const
3885 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (&trackview);
3886 return rtav->midi_track()->get_playback_channel_mask();
3891 MidiRegionView::get_grid_beats(framepos_t pos) const
3893 PublicEditor& editor = trackview.editor();
3894 bool success = false;
3895 Evoral::MusicalTime beats = editor.get_grid_type_as_beats(success, pos);
3897 beats = Evoral::MusicalTime(1);