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)
121 , _last_display_zoom (0)
124 , _grabbed_keyboard (false)
126 , _mouse_changed_selection (false)
128 CANVAS_DEBUG_NAME (_note_group, string_compose ("note group for %1", get_item_name()));
129 _note_group->raise_to_top();
130 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
132 Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&MidiRegionView::parameter_changed, this, _1), gui_context());
133 connect_to_diskstream ();
135 SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), boost::bind (&MidiRegionView::selection_cleared, this, _1), gui_context ());
137 PublicEditor& editor (trackview.editor());
138 editor.get_selection().ClearMidiNoteSelection.connect (_clear_midi_selection_connection, invalidator (*this), boost::bind (&MidiRegionView::clear_midi_selection, this), gui_context ());
141 MidiRegionView::MidiRegionView (ArdourCanvas::Container* parent,
142 RouteTimeAxisView& tv,
143 boost::shared_ptr<MidiRegion> r,
145 uint32_t basic_color,
147 TimeAxisViewItem::Visibility visibility)
148 : RegionView (parent, tv, r, spu, basic_color, recording, visibility)
149 , _current_range_min(0)
150 , _current_range_max(0)
151 , _region_relative_time_converter(r->session().tempo_map(), r->position())
152 , _source_relative_time_converter(r->session().tempo_map(), r->position() - r->start())
154 , _note_group (new ArdourCanvas::Container (group))
155 , _note_diff_command (0)
157 , _step_edit_cursor (0)
158 , _step_edit_cursor_width (1.0)
159 , _step_edit_cursor_position (0.0)
160 , _channel_selection_scoped_note (0)
161 , _temporary_note_group (0)
164 , _sort_needed (true)
165 , _optimization_iterator (_events.end())
167 , _no_sound_notes (false)
168 , _last_display_zoom (0)
171 , _grabbed_keyboard (false)
173 , _mouse_changed_selection (false)
175 CANVAS_DEBUG_NAME (_note_group, string_compose ("note group for %1", get_item_name()));
176 _note_group->raise_to_top();
178 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
180 connect_to_diskstream ();
182 SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), boost::bind (&MidiRegionView::selection_cleared, this, _1), gui_context ());
184 PublicEditor& editor (trackview.editor());
185 editor.get_selection().ClearMidiNoteSelection.connect (_clear_midi_selection_connection, invalidator (*this), boost::bind (&MidiRegionView::clear_midi_selection, this), gui_context ());
189 MidiRegionView::parameter_changed (std::string const & p)
191 if (p == "display-first-midi-bank-as-zero") {
192 if (_enable_display) {
198 MidiRegionView::MidiRegionView (const MidiRegionView& other)
199 : sigc::trackable(other)
201 , _current_range_min(0)
202 , _current_range_max(0)
203 , _region_relative_time_converter(other.region_relative_time_converter())
204 , _source_relative_time_converter(other.source_relative_time_converter())
206 , _note_group (new ArdourCanvas::Container (get_canvas_group()))
207 , _note_diff_command (0)
209 , _step_edit_cursor (0)
210 , _step_edit_cursor_width (1.0)
211 , _step_edit_cursor_position (0.0)
212 , _channel_selection_scoped_note (0)
213 , _temporary_note_group (0)
216 , _sort_needed (true)
217 , _optimization_iterator (_events.end())
219 , _no_sound_notes (false)
220 , _last_display_zoom (0)
223 , _grabbed_keyboard (false)
225 , _mouse_changed_selection (false)
230 MidiRegionView::MidiRegionView (const MidiRegionView& other, boost::shared_ptr<MidiRegion> region)
231 : RegionView (other, boost::shared_ptr<Region> (region))
232 , _current_range_min(0)
233 , _current_range_max(0)
234 , _region_relative_time_converter(other.region_relative_time_converter())
235 , _source_relative_time_converter(other.source_relative_time_converter())
237 , _note_group (new ArdourCanvas::Container (get_canvas_group()))
238 , _note_diff_command (0)
240 , _step_edit_cursor (0)
241 , _step_edit_cursor_width (1.0)
242 , _step_edit_cursor_position (0.0)
243 , _channel_selection_scoped_note (0)
244 , _temporary_note_group (0)
247 , _sort_needed (true)
248 , _optimization_iterator (_events.end())
250 , _no_sound_notes (false)
251 , _last_display_zoom (0)
254 , _grabbed_keyboard (false)
256 , _mouse_changed_selection (false)
262 MidiRegionView::init (bool wfd)
264 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
266 NoteBase::NoteBaseDeleted.connect (note_delete_connection, MISSING_INVALIDATOR,
267 boost::bind (&MidiRegionView::maybe_remove_deleted_note_from_selection, this, _1),
271 Glib::Threads::Mutex::Lock lm(midi_region()->midi_source(0)->mutex());
272 midi_region()->midi_source(0)->load_model(lm);
275 _model = midi_region()->midi_source(0)->model();
276 _enable_display = false;
277 fill_color_name = "midi frame base";
279 RegionView::init (false);
281 set_height (trackview.current_height());
284 region_sync_changed ();
285 region_resized (ARDOUR::bounds_change);
290 _enable_display = true;
293 display_model (_model);
297 reset_width_dependent_items (_pixel_width);
299 group->raise_to_top();
301 midi_view()->midi_track()->PlaybackChannelModeChanged.connect (_channel_mode_changed_connection, invalidator (*this),
302 boost::bind (&MidiRegionView::midi_channel_mode_changed, this),
305 instrument_info().Changed.connect (_instrument_changed_connection, invalidator (*this),
306 boost::bind (&MidiRegionView::instrument_settings_changed, this), gui_context());
308 trackview.editor().SnapChanged.connect(snap_changed_connection, invalidator(*this),
309 boost::bind (&MidiRegionView::snap_changed, this),
312 trackview.editor().MouseModeChanged.connect(_mouse_mode_connection, invalidator (*this),
313 boost::bind (&MidiRegionView::mouse_mode_changed, this),
316 Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&MidiRegionView::parameter_changed, this, _1), gui_context());
317 connect_to_diskstream ();
319 SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), boost::bind (&MidiRegionView::selection_cleared, this, _1), gui_context ());
321 PublicEditor& editor (trackview.editor());
322 editor.get_selection().ClearMidiNoteSelection.connect (_clear_midi_selection_connection, invalidator (*this), boost::bind (&MidiRegionView::clear_midi_selection, this), gui_context ());
326 MidiRegionView::instrument_info () const
328 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
329 return route_ui->route()->instrument_info();
332 const boost::shared_ptr<ARDOUR::MidiRegion>
333 MidiRegionView::midi_region() const
335 return boost::dynamic_pointer_cast<ARDOUR::MidiRegion>(_region);
339 MidiRegionView::connect_to_diskstream ()
341 midi_view()->midi_track()->DataRecorded.connect(
342 *this, invalidator(*this),
343 boost::bind (&MidiRegionView::data_recorded, this, _1),
348 MidiRegionView::canvas_group_event(GdkEvent* ev)
350 if (in_destructor || _recregion) {
354 if (!trackview.editor().internal_editing()) {
355 // not in internal edit mode, so just act like a normal region
356 return RegionView::canvas_group_event (ev);
362 case GDK_ENTER_NOTIFY:
363 _last_event_x = ev->crossing.x;
364 _last_event_y = ev->crossing.y;
365 enter_notify(&ev->crossing);
366 // set entered_regionview (among other things)
367 return RegionView::canvas_group_event (ev);
369 case GDK_LEAVE_NOTIFY:
370 _last_event_x = ev->crossing.x;
371 _last_event_y = ev->crossing.y;
372 leave_notify(&ev->crossing);
373 // reset entered_regionview (among other things)
374 return RegionView::canvas_group_event (ev);
377 if (scroll (&ev->scroll)) {
383 return key_press (&ev->key);
385 case GDK_KEY_RELEASE:
386 return key_release (&ev->key);
388 case GDK_BUTTON_PRESS:
389 return button_press (&ev->button);
391 case GDK_BUTTON_RELEASE:
392 r = button_release (&ev->button);
393 _note_player.reset();
396 case GDK_MOTION_NOTIFY:
397 _last_event_x = ev->motion.x;
398 _last_event_y = ev->motion.y;
399 return motion (&ev->motion);
405 return RegionView::canvas_group_event (ev);
409 MidiRegionView::enter_notify (GdkEventCrossing* ev)
418 MidiRegionView::leave_notify (GdkEventCrossing*)
427 MidiRegionView::mouse_mode_changed ()
429 // Adjust frame colour (become more transparent for internal tools)
433 if (trackview.editor().internal_editing()) {
434 // Switched in to internal editing mode while entered
437 // Switched out of internal editing mode while entered
444 MidiRegionView::enter_internal()
446 if (trackview.editor().current_mouse_mode() == MouseDraw && _mouse_state != AddDragging) {
447 // Show ghost note under pencil
448 create_ghost_note(_last_event_x, _last_event_y);
451 if (!_selection.empty()) {
452 // Grab keyboard for moving selected notes with arrow keys
453 Keyboard::magic_widget_grab_focus();
454 _grabbed_keyboard = true;
457 // Lower frame handles below notes so they don't steal events
458 if (frame_handle_start) {
459 frame_handle_start->lower_to_bottom();
461 if (frame_handle_end) {
462 frame_handle_end->lower_to_bottom();
467 MidiRegionView::leave_internal()
469 trackview.editor().verbose_cursor()->hide ();
470 remove_ghost_note ();
472 if (_grabbed_keyboard) {
473 Keyboard::magic_widget_drop_focus();
474 _grabbed_keyboard = false;
477 // Raise frame handles above notes so they catch events
478 if (frame_handle_start) {
479 frame_handle_start->raise_to_top();
481 if (frame_handle_end) {
482 frame_handle_end->raise_to_top();
487 MidiRegionView::button_press (GdkEventButton* ev)
489 if (ev->button != 1) {
493 Editor* editor = dynamic_cast<Editor *> (&trackview.editor());
494 MouseMode m = editor->current_mouse_mode();
496 if (m == MouseContent && Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())) {
497 _press_cursor_ctx = CursorContext::create(*editor, editor->cursors()->midi_pencil);
500 if (_mouse_state != SelectTouchDragging) {
502 _pressed_button = ev->button;
503 _mouse_state = Pressed;
508 _pressed_button = ev->button;
509 _mouse_changed_selection = false;
515 MidiRegionView::button_release (GdkEventButton* ev)
517 double event_x, event_y;
519 if (ev->button != 1) {
526 group->canvas_to_item (event_x, event_y);
529 PublicEditor& editor = trackview.editor ();
531 _press_cursor_ctx.reset();
533 switch (_mouse_state) {
534 case Pressed: // Clicked
536 switch (editor.current_mouse_mode()) {
538 /* no motion occured - simple click */
540 _mouse_changed_selection = true;
547 _mouse_changed_selection = true;
549 if (Keyboard::is_insert_note_event(ev)) {
551 double event_x, event_y;
555 group->canvas_to_item (event_x, event_y);
557 Evoral::Beats beats = get_grid_beats(editor.pixel_to_sample(event_x));
559 /* Shorten the length by 1 tick so that we can add a new note at the next
560 grid snap without it overlapping this one.
562 beats -= Evoral::Beats::tick();
564 create_note_at (editor.pixel_to_sample (event_x), event_y, beats, true);
571 Evoral::Beats beats = get_grid_beats(editor.pixel_to_sample(event_x));
573 /* Shorten the length by 1 tick so that we can add a new note at the next
574 grid snap without it overlapping this one.
576 beats -= Evoral::Beats::tick();
578 create_note_at (editor.pixel_to_sample (event_x), event_y, beats, true);
589 case SelectRectDragging:
591 editor.drags()->end_grab ((GdkEvent *) ev);
593 create_ghost_note (ev->x, ev->y);
601 if(_mouse_changed_selection) {
602 trackview.editor().begin_reversible_selection_op (_("Mouse Selection Change"));
603 trackview.editor().commit_reversible_selection_op ();
610 MidiRegionView::motion (GdkEventMotion* ev)
612 PublicEditor& editor = trackview.editor ();
614 if (!_ghost_note && editor.current_mouse_mode() == MouseContent &&
615 Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier()) &&
616 _mouse_state != AddDragging) {
618 create_ghost_note (ev->x, ev->y);
620 } else if (_ghost_note && editor.current_mouse_mode() == MouseContent &&
621 Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())) {
623 update_ghost_note (ev->x, ev->y);
625 } else if (_ghost_note && editor.current_mouse_mode() == MouseContent) {
627 remove_ghost_note ();
628 editor.verbose_cursor()->hide ();
630 } else if (_ghost_note && editor.current_mouse_mode() == MouseDraw) {
632 update_ghost_note (ev->x, ev->y);
635 /* any motion immediately hides velocity text that may have been visible */
637 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
638 (*i)->hide_velocity ();
641 switch (_mouse_state) {
644 if (_pressed_button == 1) {
646 MouseMode m = editor.current_mouse_mode();
648 if (m == MouseDraw || (m == MouseContent && Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier()))) {
649 editor.drags()->set (new NoteCreateDrag (dynamic_cast<Editor *> (&editor), group, this), (GdkEvent *) ev);
650 _mouse_state = AddDragging;
651 remove_ghost_note ();
652 editor.verbose_cursor()->hide ();
654 } else if (m == MouseContent) {
655 editor.drags()->set (new MidiRubberbandSelectDrag (dynamic_cast<Editor *> (&editor), this), (GdkEvent *) ev);
656 if (!Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
658 _mouse_changed_selection = true;
660 _mouse_state = SelectRectDragging;
662 } else if (m == MouseRange) {
663 editor.drags()->set (new MidiVerticalSelectDrag (dynamic_cast<Editor *> (&editor), this), (GdkEvent *) ev);
664 _mouse_state = SelectVerticalDragging;
671 case SelectRectDragging:
672 case SelectVerticalDragging:
674 editor.drags()->motion_handler ((GdkEvent *) ev, false);
677 case SelectTouchDragging:
685 /* we may be dragging some non-note object (eg. patch-change, sysex)
688 return editor.drags()->motion_handler ((GdkEvent *) ev, false);
693 MidiRegionView::scroll (GdkEventScroll* ev)
695 if (_selection.empty()) {
699 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
700 /* XXX: bit of a hack; allow PrimaryModifier scroll through so that
701 it still works for zoom.
706 trackview.editor().verbose_cursor()->hide ();
708 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
709 bool together = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
711 if (ev->direction == GDK_SCROLL_UP) {
712 change_velocities (true, fine, false, together);
713 } else if (ev->direction == GDK_SCROLL_DOWN) {
714 change_velocities (false, fine, false, together);
716 /* left, right: we don't use them */
724 MidiRegionView::key_press (GdkEventKey* ev)
726 /* since GTK bindings are generally activated on press, and since
727 detectable auto-repeat is the name of the game and only sends
728 repeated presses, carry out key actions at key press, not release.
731 bool unmodified = Keyboard::no_modifier_keys_pressed (ev);
733 if (unmodified && (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R)) {
734 _mouse_state = SelectTouchDragging;
737 } else if (ev->keyval == GDK_Escape && unmodified) {
741 } else if (ev->keyval == GDK_comma || ev->keyval == GDK_period) {
743 bool start = (ev->keyval == GDK_comma);
744 bool end = (ev->keyval == GDK_period);
745 bool shorter = Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier);
746 bool fine = Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
748 change_note_lengths (fine, shorter, Evoral::Beats(), start, end);
752 } else if ((ev->keyval == GDK_BackSpace || ev->keyval == GDK_Delete) && unmodified) {
754 if (_selection.empty()) {
761 } else if (ev->keyval == GDK_Tab) {
763 trackview.editor().begin_reversible_selection_op (_("Select Adjacent Note"));
765 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
766 goto_previous_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
768 goto_next_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
771 trackview.editor().commit_reversible_selection_op();
775 } else if (ev->keyval == GDK_ISO_Left_Tab) {
777 /* Shift-TAB generates ISO Left Tab, for some reason */
779 trackview.editor().begin_reversible_selection_op (_("Select Adjacent Note"));
781 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
782 goto_previous_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
784 goto_next_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
787 trackview.editor().commit_reversible_selection_op();
793 } else if (ev->keyval == GDK_Up) {
795 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
796 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
797 bool together = Keyboard::modifier_state_contains (ev->state, Keyboard::Level4Modifier);
799 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
800 change_velocities (true, fine, allow_smush, together);
802 transpose (true, fine, allow_smush);
806 } else if (ev->keyval == GDK_Down) {
808 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
809 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
810 bool together = Keyboard::modifier_state_contains (ev->state, Keyboard::Level4Modifier);
812 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
813 change_velocities (false, fine, allow_smush, together);
815 transpose (false, fine, allow_smush);
819 } else if (ev->keyval == GDK_Left) {
821 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
822 nudge_notes (false, fine);
825 } else if (ev->keyval == GDK_Right) {
827 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
828 nudge_notes (true, fine);
831 } else if (ev->keyval == GDK_c && unmodified) {
835 } else if (ev->keyval == GDK_v && unmodified) {
844 MidiRegionView::key_release (GdkEventKey* ev)
846 if ((_mouse_state == SelectTouchDragging) && (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R)) {
854 MidiRegionView::channel_edit ()
856 if (_selection.empty()) {
860 /* pick a note somewhat at random (since Selection is a set<>) to
861 * provide the "current" channel for the dialog.
864 uint8_t current_channel = (*_selection.begin())->note()->channel ();
865 MidiChannelDialog channel_dialog (current_channel);
866 int ret = channel_dialog.run ();
869 case Gtk::RESPONSE_OK:
875 uint8_t new_channel = channel_dialog.active_channel ();
877 start_note_diff_command (_("channel edit"));
879 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
880 Selection::iterator next = i;
882 change_note_channel (*i, new_channel);
890 MidiRegionView::velocity_edit ()
892 if (_selection.empty()) {
896 /* pick a note somewhat at random (since Selection is a set<>) to
897 * provide the "current" velocity for the dialog.
900 uint8_t current_velocity = (*_selection.begin())->note()->velocity ();
901 MidiVelocityDialog velocity_dialog (current_velocity);
902 int ret = velocity_dialog.run ();
905 case Gtk::RESPONSE_OK:
911 uint8_t new_velocity = velocity_dialog.velocity ();
913 start_note_diff_command (_("velocity edit"));
915 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
916 Selection::iterator next = i;
918 change_note_velocity (*i, new_velocity, false);
926 MidiRegionView::show_list_editor ()
929 _list_editor = new MidiListEditor (trackview.session(), midi_region(), midi_view()->midi_track());
931 _list_editor->present ();
934 /** Add a note to the model, and the view, at a canvas (click) coordinate.
935 * \param t time in frames relative to the position of the region
936 * \param y vertical position in pixels
937 * \param length duration of the note in beats
938 * \param snap_t true to snap t to the grid, otherwise false.
941 MidiRegionView::create_note_at (framepos_t t, double y, Evoral::Beats length, bool snap_t)
943 if (length < 2 * DBL_EPSILON) {
947 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
948 MidiStreamView* const view = mtv->midi_view();
950 // Start of note in frames relative to region start
952 framecnt_t grid_frames;
953 t = snap_frame_to_grid_underneath (t, grid_frames);
956 const MidiModel::TimeType beat_time = region_frames_to_region_beats(
957 t + _region->start());
959 const double note = view->y_to_note(y);
960 const uint8_t chan = mtv->get_channel_for_add();
961 const uint8_t velocity = get_velocity_for_add(beat_time);
963 const boost::shared_ptr<NoteType> new_note(
964 new NoteType (chan, beat_time, length, (uint8_t)note, velocity));
966 if (_model->contains (new_note)) {
970 view->update_note_range(new_note->note());
972 trackview.editor().begin_reversible_command(_("add note"));
973 MidiModel::NoteDiffCommand* cmd = _model->new_note_diff_command(_("add note"));
975 _model->apply_command(*trackview.session(), cmd);
976 trackview.editor().commit_reversible_command();
978 play_midi_note (new_note);
982 MidiRegionView::clear_events (bool with_selection_signal)
984 clear_selection (with_selection_signal);
987 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
988 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
993 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
998 _patch_changes.clear();
1000 _optimization_iterator = _events.end();
1004 MidiRegionView::display_model(boost::shared_ptr<MidiModel> model)
1008 content_connection.disconnect ();
1009 _model->ContentsChanged.connect (content_connection, invalidator (*this), boost::bind (&MidiRegionView::redisplay_model, this), gui_context());
1010 /* Don't signal as nobody else needs to know until selection has been altered.*/
1011 clear_events (false);
1013 if (_enable_display) {
1019 MidiRegionView::start_note_diff_command (string name)
1021 if (!_note_diff_command) {
1022 trackview.editor().begin_reversible_command (name);
1023 _note_diff_command = _model->new_note_diff_command (name);
1028 MidiRegionView::note_diff_add_note (const boost::shared_ptr<NoteType> note, bool selected, bool show_velocity)
1030 if (_note_diff_command) {
1031 _note_diff_command->add (note);
1034 _marked_for_selection.insert(note);
1036 if (show_velocity) {
1037 _marked_for_velocity.insert(note);
1042 MidiRegionView::note_diff_remove_note (NoteBase* ev)
1044 if (_note_diff_command && ev->note()) {
1045 _note_diff_command->remove(ev->note());
1050 MidiRegionView::note_diff_add_change (NoteBase* ev,
1051 MidiModel::NoteDiffCommand::Property property,
1054 if (_note_diff_command) {
1055 _note_diff_command->change (ev->note(), property, val);
1060 MidiRegionView::note_diff_add_change (NoteBase* ev,
1061 MidiModel::NoteDiffCommand::Property property,
1064 if (_note_diff_command) {
1065 _note_diff_command->change (ev->note(), property, val);
1070 MidiRegionView::apply_diff (bool as_subcommand)
1073 bool commit = false;
1075 if (!_note_diff_command) {
1079 if ((add_or_remove = _note_diff_command->adds_or_removes())) {
1080 // Mark all selected notes for selection when model reloads
1081 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1082 _marked_for_selection.insert((*i)->note());
1086 if (as_subcommand) {
1087 _model->apply_command_as_subcommand (*trackview.session(), _note_diff_command);
1089 _model->apply_command (*trackview.session(), _note_diff_command);
1093 _note_diff_command = 0;
1094 midi_view()->midi_track()->playlist_modified();
1096 if (add_or_remove) {
1097 _marked_for_selection.clear();
1100 _marked_for_velocity.clear();
1102 trackview.editor().commit_reversible_command ();
1107 MidiRegionView::abort_command()
1109 delete _note_diff_command;
1110 _note_diff_command = 0;
1115 MidiRegionView::find_canvas_note (boost::shared_ptr<NoteType> note)
1117 if (_optimization_iterator != _events.end()) {
1118 ++_optimization_iterator;
1121 if (_optimization_iterator != _events.end() && (*_optimization_iterator)->note() == note) {
1122 return *_optimization_iterator;
1125 for (_optimization_iterator = _events.begin(); _optimization_iterator != _events.end(); ++_optimization_iterator) {
1126 if ((*_optimization_iterator)->note() == note) {
1127 return *_optimization_iterator;
1134 /** This version finds any canvas note matching the supplied note.*/
1136 MidiRegionView::find_canvas_note (NoteType note)
1138 if (_optimization_iterator != _events.end()) {
1139 ++_optimization_iterator;
1142 if (_optimization_iterator != _events.end() && (*(*_optimization_iterator)->note()) == note) {
1143 return *_optimization_iterator;
1146 for (_optimization_iterator = _events.begin(); _optimization_iterator != _events.end(); ++_optimization_iterator) {
1147 if (*((*_optimization_iterator)->note()) == note) {
1148 return *_optimization_iterator;
1156 MidiRegionView::get_events (Events& e, Evoral::Sequence<Evoral::Beats>::NoteOperator op, uint8_t val, int chan_mask)
1158 MidiModel::Notes notes;
1159 _model->get_notes (notes, op, val, chan_mask);
1161 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1162 NoteBase* cne = find_canvas_note (*n);
1170 MidiRegionView::redisplay_model()
1172 if (_active_notes) {
1173 // Currently recording
1174 const framecnt_t zoom = trackview.editor().get_current_zoom();
1175 if (zoom != _last_display_zoom) {
1176 /* Update resolved canvas notes to reflect changes in zoom without
1177 touching model. Leave active notes (with length 0) alone since
1178 they are being extended. */
1179 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1180 if ((*i)->note()->length() > 0) {
1184 _last_display_zoom = zoom;
1193 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1194 (*i)->invalidate ();
1197 MidiModel::ReadLock lock(_model->read_lock());
1199 MidiModel::Notes& notes (_model->notes());
1200 _optimization_iterator = _events.begin();
1202 bool empty_when_starting = _events.empty();
1204 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1206 boost::shared_ptr<NoteType> note (*n);
1210 if (note_in_region_range (note, visible)) {
1212 if (!empty_when_starting && (cne = find_canvas_note (note)) != 0) {
1225 cne = add_note (note, visible);
1228 set<boost::shared_ptr<NoteType> >::iterator it;
1229 for (it = _pending_note_selection.begin(); it != _pending_note_selection.end(); ++it) {
1230 if (*(*it) == *note) {
1231 add_to_selection (cne);
1237 if (!empty_when_starting && (cne = find_canvas_note (note)) != 0) {
1244 /* remove note items that are no longer valid */
1246 if (!empty_when_starting) {
1247 for (Events::iterator i = _events.begin(); i != _events.end(); ) {
1248 if (!(*i)->valid ()) {
1250 for (vector<GhostRegion*>::iterator j = ghosts.begin(); j != ghosts.end(); ++j) {
1251 MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*j);
1253 gr->remove_note (*i);
1258 i = _events.erase (i);
1266 _patch_changes.clear();
1270 display_patch_changes ();
1272 _marked_for_selection.clear ();
1273 _marked_for_velocity.clear ();
1274 _pending_note_selection.clear ();
1276 /* we may have caused _events to contain things out of order (e.g. if a note
1277 moved earlier or later). we don't generally need them in time order, but
1278 make a note that a sort is required for those cases that require it.
1281 _sort_needed = true;
1285 MidiRegionView::display_patch_changes ()
1287 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1288 uint16_t chn_mask = mtv->midi_track()->get_playback_channel_mask();
1290 for (uint8_t i = 0; i < 16; ++i) {
1291 display_patch_changes_on_channel (i, chn_mask & (1 << i));
1295 /** @param active_channel true to display patch changes fully, false to display
1296 * them `greyed-out' (as on an inactive channel)
1299 MidiRegionView::display_patch_changes_on_channel (uint8_t channel, bool active_channel)
1301 for (MidiModel::PatchChanges::const_iterator i = _model->patch_changes().begin(); i != _model->patch_changes().end(); ++i) {
1303 if ((*i)->channel() != channel) {
1307 const string patch_name = instrument_info().get_patch_name ((*i)->bank(), (*i)->program(), channel);
1308 add_canvas_patch_change (*i, patch_name, active_channel);
1313 MidiRegionView::display_sysexes()
1315 bool have_periodic_system_messages = false;
1316 bool display_periodic_messages = true;
1318 if (!ARDOUR_UI::config()->get_never_display_periodic_midi()) {
1320 for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
1321 const boost::shared_ptr<const Evoral::MIDIEvent<Evoral::Beats> > mev =
1322 boost::static_pointer_cast<const Evoral::MIDIEvent<Evoral::Beats> > (*i);
1325 if (mev->is_spp() || mev->is_mtc_quarter() || mev->is_mtc_full()) {
1326 have_periodic_system_messages = true;
1332 if (have_periodic_system_messages) {
1333 double zoom = trackview.editor().get_current_zoom (); // frames per pixel
1335 /* get an approximate value for the number of samples per video frame */
1337 double video_frame = trackview.session()->frame_rate() * (1.0/30);
1339 /* if we are zoomed out beyond than the cutoff (i.e. more
1340 * frames per pixel than frames per 4 video frames), don't
1341 * show periodic sysex messages.
1344 if (zoom > (video_frame*4)) {
1345 display_periodic_messages = false;
1349 display_periodic_messages = false;
1352 for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
1354 const boost::shared_ptr<const Evoral::MIDIEvent<Evoral::Beats> > mev =
1355 boost::static_pointer_cast<const Evoral::MIDIEvent<Evoral::Beats> > (*i);
1357 Evoral::Beats time = (*i)->time();
1360 if (mev->is_spp() || mev->is_mtc_quarter() || mev->is_mtc_full()) {
1361 if (!display_periodic_messages) {
1369 for (uint32_t b = 0; b < (*i)->size(); ++b) {
1370 str << int((*i)->buffer()[b]);
1371 if (b != (*i)->size() -1) {
1375 string text = str.str();
1377 const double x = trackview.editor().sample_to_pixel(source_beats_to_region_frames(time));
1379 double height = midi_stream_view()->contents_height();
1381 // CAIROCANVAS: no longer passing *i (the sysex event) to the
1382 // SysEx canvas object!!!
1384 boost::shared_ptr<SysEx> sysex = boost::shared_ptr<SysEx>(
1385 new SysEx (*this, _note_group, text, height, x, 1.0));
1387 // Show unless message is beyond the region bounds
1388 if (time - _region->start() >= _region->length() || time < _region->start()) {
1394 _sys_exes.push_back(sysex);
1398 MidiRegionView::~MidiRegionView ()
1400 in_destructor = true;
1402 trackview.editor().verbose_cursor()->hide ();
1404 note_delete_connection.disconnect ();
1406 delete _list_editor;
1408 RegionViewGoingAway (this); /* EMIT_SIGNAL */
1410 if (_active_notes) {
1414 _selection_cleared_connection.disconnect ();
1417 clear_events (false);
1420 delete _note_diff_command;
1421 delete _step_edit_cursor;
1422 delete _temporary_note_group;
1426 MidiRegionView::region_resized (const PropertyChange& what_changed)
1428 RegionView::region_resized(what_changed);
1430 if (what_changed.contains (ARDOUR::Properties::position)) {
1431 _region_relative_time_converter.set_origin_b(_region->position());
1432 set_duration(_region->length(), 0);
1433 if (_enable_display) {
1438 if (what_changed.contains (ARDOUR::Properties::start) ||
1439 what_changed.contains (ARDOUR::Properties::position)) {
1440 _source_relative_time_converter.set_origin_b (_region->position() - _region->start());
1445 MidiRegionView::reset_width_dependent_items (double pixel_width)
1447 RegionView::reset_width_dependent_items(pixel_width);
1449 if (_enable_display) {
1453 for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
1454 if ((*x)->canvas_item()->width() >= _pixel_width) {
1461 move_step_edit_cursor (_step_edit_cursor_position);
1462 set_step_edit_cursor_width (_step_edit_cursor_width);
1466 MidiRegionView::set_height (double height)
1468 double old_height = _height;
1469 RegionView::set_height(height);
1471 apply_note_range (midi_stream_view()->lowest_note(),
1472 midi_stream_view()->highest_note(),
1473 height != old_height);
1476 name_text->raise_to_top();
1479 for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
1480 (*x)->set_height (midi_stream_view()->contents_height());
1483 if (_step_edit_cursor) {
1484 _step_edit_cursor->set_y1 (midi_stream_view()->contents_height());
1489 /** Apply the current note range from the stream view
1490 * by repositioning/hiding notes as necessary
1493 MidiRegionView::apply_note_range (uint8_t min, uint8_t max, bool force)
1495 if (!_enable_display) {
1499 if (!force && _current_range_min == min && _current_range_max == max) {
1503 _current_range_min = min;
1504 _current_range_max = max;
1506 for (Events::const_iterator i = _events.begin(); i != _events.end(); ++i) {
1507 NoteBase* event = *i;
1508 boost::shared_ptr<NoteType> note (event->note());
1510 if (note->note() < _current_range_min ||
1511 note->note() > _current_range_max) {
1517 if (Note* cnote = dynamic_cast<Note*>(event)) {
1519 const double y0 = 1. + floor (midi_stream_view()->note_to_y(note->note()));
1520 const double y1 = y0 + std::max(1., floor(midi_stream_view()->note_height()) - 1.);
1525 } else if (Hit* chit = dynamic_cast<Hit*>(event)) {
1532 MidiRegionView::add_ghost (TimeAxisView& tv)
1534 double unit_position = _region->position () / samples_per_pixel;
1535 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(&tv);
1536 MidiGhostRegion* ghost;
1538 if (mtv && mtv->midi_view()) {
1539 /* if ghost is inserted into midi track, use a dedicated midi ghost canvas group
1540 to allow having midi notes on top of note lines and waveforms.
1542 ghost = new MidiGhostRegion (*mtv->midi_view(), trackview, unit_position);
1544 ghost = new MidiGhostRegion (tv, trackview, unit_position);
1547 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1548 ghost->add_note(*i);
1551 ghost->set_height ();
1552 ghost->set_duration (_region->length() / samples_per_pixel);
1553 ghosts.push_back (ghost);
1555 GhostRegion::CatchDeletion.connect (*this, invalidator (*this), boost::bind (&RegionView::remove_ghost, this, _1), gui_context());
1561 /** Begin tracking note state for successive calls to add_event
1564 MidiRegionView::begin_write()
1566 if (_active_notes) {
1567 delete[] _active_notes;
1569 _active_notes = new Note*[128];
1570 for (unsigned i = 0; i < 128; ++i) {
1571 _active_notes[i] = 0;
1576 /** Destroy note state for add_event
1579 MidiRegionView::end_write()
1581 delete[] _active_notes;
1583 _marked_for_selection.clear();
1584 _marked_for_velocity.clear();
1588 /** Resolve an active MIDI note (while recording).
1591 MidiRegionView::resolve_note(uint8_t note, Evoral::Beats end_time)
1593 if (midi_view()->note_mode() != Sustained) {
1597 if (_active_notes && _active_notes[note]) {
1598 /* Set note length so update_note() works. Note this is a local note
1599 for recording, not from a model, so we can safely mess with it. */
1600 _active_notes[note]->note()->set_length(
1601 end_time - _active_notes[note]->note()->time());
1603 /* End time is relative to the region being recorded. */
1604 const framepos_t end_time_frames = region_beats_to_region_frames(end_time);
1606 _active_notes[note]->set_x1 (trackview.editor().sample_to_pixel(end_time_frames));
1607 _active_notes[note]->set_outline_all ();
1608 _active_notes[note] = 0;
1613 /** Extend active notes to rightmost edge of region (if length is changed)
1616 MidiRegionView::extend_active_notes()
1618 if (!_active_notes) {
1622 for (unsigned i = 0; i < 128; ++i) {
1623 if (_active_notes[i]) {
1624 _active_notes[i]->set_x1(
1625 trackview.editor().sample_to_pixel(_region->length()));
1631 MidiRegionView::play_midi_note(boost::shared_ptr<NoteType> note)
1633 if (_no_sound_notes || !ARDOUR_UI::config()->get_sound_midi_notes()) {
1637 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1639 if (!route_ui || !route_ui->midi_track()) {
1643 NotePlayer* np = new NotePlayer (route_ui->midi_track ());
1647 /* NotePlayer deletes itself */
1651 MidiRegionView::start_playing_midi_note(boost::shared_ptr<NoteType> note)
1653 const std::vector< boost::shared_ptr<NoteType> > notes(1, note);
1654 start_playing_midi_chord(notes);
1658 MidiRegionView::start_playing_midi_chord (vector<boost::shared_ptr<NoteType> > notes)
1660 if (_no_sound_notes || !ARDOUR_UI::config()->get_sound_midi_notes()) {
1664 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1666 if (!route_ui || !route_ui->midi_track()) {
1670 _note_player = boost::shared_ptr<NotePlayer>(new NotePlayer(route_ui->midi_track()));
1672 for (vector<boost::shared_ptr<NoteType> >::iterator n = notes.begin(); n != notes.end(); ++n) {
1673 _note_player->add (*n);
1676 _note_player->on ();
1681 MidiRegionView::note_in_region_range (const boost::shared_ptr<NoteType> note, bool& visible) const
1683 const framepos_t note_start_frames = source_beats_to_region_frames (note->time());
1684 bool outside = (note_start_frames < 0) || (note_start_frames > _region->last_frame());
1686 visible = (note->note() >= midi_stream_view()->lowest_note()) &&
1687 (note->note() <= midi_stream_view()->highest_note());
1693 MidiRegionView::update_note (NoteBase* note, bool update_ghost_regions)
1697 if ((sus = dynamic_cast<Note*>(note))) {
1698 update_sustained(sus, update_ghost_regions);
1699 } else if ((hit = dynamic_cast<Hit*>(note))) {
1700 update_hit(hit, update_ghost_regions);
1704 /** Update a canvas note's size from its model note.
1705 * @param ev Canvas note to update.
1706 * @param update_ghost_regions true to update the note in any ghost regions that we have, otherwise false.
1709 MidiRegionView::update_sustained (Note* ev, bool update_ghost_regions)
1711 boost::shared_ptr<NoteType> note = ev->note();
1712 const double x = trackview.editor().sample_to_pixel (source_beats_to_region_frames (note->time()));
1713 const double y0 = 1 + floor(midi_stream_view()->note_to_y(note->note()));
1718 /* trim note display to not overlap the end of its region */
1720 if (note->length() > 0) {
1721 const framepos_t note_end_frames = min (source_beats_to_region_frames (note->end_time()), _region->length());
1722 ev->set_x1 (trackview.editor().sample_to_pixel (note_end_frames));
1724 ev->set_x1 (trackview.editor().sample_to_pixel (_region->length()));
1727 ev->set_y1 (y0 + std::max(1., floor(midi_stream_view()->note_height()) - 1));
1729 if (!note->length()) {
1730 if (_active_notes && note->note() < 128) {
1731 Note* const old_rect = _active_notes[note->note()];
1733 /* There is an active note on this key, so we have a stuck
1734 note. Finish the old rectangle here. */
1735 old_rect->set_x1 (x);
1736 old_rect->set_outline_all ();
1738 _active_notes[note->note()] = ev;
1740 /* outline all but right edge */
1741 ev->set_outline_what (ArdourCanvas::Rectangle::What (
1742 ArdourCanvas::Rectangle::TOP|
1743 ArdourCanvas::Rectangle::LEFT|
1744 ArdourCanvas::Rectangle::BOTTOM));
1746 /* outline all edges */
1747 ev->set_outline_all ();
1750 // Update color in case velocity has changed
1751 ev->set_fill_color(ev->base_color());
1752 ev->set_outline_color(ev->calculate_outline(ev->base_color(), ev->selected()));
1754 if (update_ghost_regions) {
1755 for (std::vector<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
1756 MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*i);
1758 gr->update_note (ev);
1765 MidiRegionView::update_hit (Hit* ev, bool update_ghost_regions)
1767 boost::shared_ptr<NoteType> note = ev->note();
1769 const framepos_t note_start_frames = source_beats_to_region_frames(note->time());
1770 const double x = trackview.editor().sample_to_pixel(note_start_frames);
1771 const double diamond_size = std::max(1., floor(midi_stream_view()->note_height()) - 2.);
1772 const double y = 1.5 + floor(midi_stream_view()->note_to_y(note->note())) + diamond_size * .5;
1774 ev->set_position (ArdourCanvas::Duple (x, y));
1775 ev->set_height (diamond_size);
1777 // Update color in case velocity has changed
1778 ev->set_fill_color(ev->base_color());
1779 ev->set_outline_color(ev->calculate_outline(ev->base_color(), ev->selected()));
1781 if (update_ghost_regions) {
1782 for (std::vector<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
1783 MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*i);
1785 gr->update_note (ev);
1791 /** Add a MIDI note to the view (with length).
1793 * If in sustained mode, notes with length 0 will be considered active
1794 * notes, and resolve_note should be called when the corresponding note off
1795 * event arrives, to properly display the note.
1798 MidiRegionView::add_note(const boost::shared_ptr<NoteType> note, bool visible)
1800 NoteBase* event = 0;
1802 if (midi_view()->note_mode() == Sustained) {
1804 Note* ev_rect = new Note (*this, _note_group, note);
1806 update_sustained (ev_rect);
1810 } else if (midi_view()->note_mode() == Percussive) {
1812 const double diamond_size = std::max(1., floor(midi_stream_view()->note_height()) - 2.);
1814 Hit* ev_diamond = new Hit (*this, _note_group, diamond_size, note);
1816 update_hit (ev_diamond);
1825 MidiGhostRegion* gr;
1827 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
1828 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
1829 gr->add_note(event);
1833 if (_marked_for_selection.find(note) != _marked_for_selection.end()) {
1834 note_selected(event, true);
1837 if (_marked_for_velocity.find(note) != _marked_for_velocity.end()) {
1838 event->show_velocity();
1841 event->on_channel_selection_change (get_selected_channels());
1842 _events.push_back(event);
1851 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1852 MidiStreamView* const view = mtv->midi_view();
1854 view->update_note_range (note->note());
1859 MidiRegionView::step_add_note (uint8_t channel, uint8_t number, uint8_t velocity,
1860 Evoral::Beats pos, Evoral::Beats len)
1862 boost::shared_ptr<NoteType> new_note (new NoteType (channel, pos, len, number, velocity));
1864 /* potentially extend region to hold new note */
1866 framepos_t end_frame = source_beats_to_absolute_frames (new_note->end_time());
1867 framepos_t region_end = _region->last_frame();
1869 if (end_frame > region_end) {
1870 _region->set_length (end_frame - _region->position());
1873 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1874 MidiStreamView* const view = mtv->midi_view();
1876 view->update_note_range(new_note->note());
1878 _marked_for_selection.clear ();
1881 start_note_diff_command (_("step add"));
1882 note_diff_add_note (new_note, true, false);
1885 // last_step_edit_note = new_note;
1889 MidiRegionView::step_sustain (Evoral::Beats beats)
1891 change_note_lengths (false, false, beats, false, true);
1894 /** Add a new patch change flag to the canvas.
1895 * @param patch the patch change to add
1896 * @param the text to display in the flag
1897 * @param active_channel true to display the flag as on an active channel, false to grey it out for an inactive channel.
1900 MidiRegionView::add_canvas_patch_change (MidiModel::PatchChangePtr patch, const string& displaytext, bool /*active_channel*/)
1902 framecnt_t region_frames = source_beats_to_region_frames (patch->time());
1903 const double x = trackview.editor().sample_to_pixel (region_frames);
1905 double const height = midi_stream_view()->contents_height();
1907 // CAIROCANVAS: active_channel info removed from PatcChange constructor
1908 // so we need to do something more sophisticated to keep its color
1909 // appearance (MidiPatchChangeFill/MidiPatchChangeInactiveChannelFill)
1912 boost::shared_ptr<PatchChange> patch_change = boost::shared_ptr<PatchChange>(
1913 new PatchChange(*this, group,
1920 if (patch_change->item().width() < _pixel_width) {
1921 // Show unless patch change is beyond the region bounds
1922 if (region_frames < 0 || region_frames >= _region->length()) {
1923 patch_change->hide();
1925 patch_change->show();
1928 patch_change->hide ();
1931 _patch_changes.push_back (patch_change);
1934 MIDI::Name::PatchPrimaryKey
1935 MidiRegionView::patch_change_to_patch_key (MidiModel::PatchChangePtr p)
1937 return MIDI::Name::PatchPrimaryKey (p->program(), p->bank());
1940 /// Return true iff @p pc applies to the given time on the given channel.
1942 patch_applies (const ARDOUR::MidiModel::constPatchChangePtr pc, Evoral::Beats time, uint8_t channel)
1944 return pc->time() <= time && pc->channel() == channel;
1948 MidiRegionView::get_patch_key_at (Evoral::Beats time, uint8_t channel, MIDI::Name::PatchPrimaryKey& key) const
1950 // The earliest event not before time
1951 MidiModel::PatchChanges::iterator i = _model->patch_change_lower_bound (time);
1953 // Go backwards until we find the latest PC for this channel, or the start
1954 while (i != _model->patch_changes().begin() &&
1955 (i == _model->patch_changes().end() ||
1956 !patch_applies(*i, time, channel))) {
1960 if (i != _model->patch_changes().end() && patch_applies(*i, time, channel)) {
1961 key.set_bank((*i)->bank());
1962 key.set_program((*i)->program ());
1970 MidiRegionView::change_patch_change (PatchChange& pc, const MIDI::Name::PatchPrimaryKey& new_patch)
1972 string name = _("alter patch change");
1973 trackview.editor().begin_reversible_command (name);
1974 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (name);
1976 if (pc.patch()->program() != new_patch.program()) {
1977 c->change_program (pc.patch (), new_patch.program());
1980 int const new_bank = new_patch.bank();
1981 if (pc.patch()->bank() != new_bank) {
1982 c->change_bank (pc.patch (), new_bank);
1985 _model->apply_command (*trackview.session(), c);
1986 trackview.editor().commit_reversible_command ();
1988 _patch_changes.clear ();
1989 display_patch_changes ();
1993 MidiRegionView::change_patch_change (MidiModel::PatchChangePtr old_change, const Evoral::PatchChange<Evoral::Beats> & new_change)
1995 string name = _("alter patch change");
1996 trackview.editor().begin_reversible_command (name);
1997 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (name);
1999 if (old_change->time() != new_change.time()) {
2000 c->change_time (old_change, new_change.time());
2003 if (old_change->channel() != new_change.channel()) {
2004 c->change_channel (old_change, new_change.channel());
2007 if (old_change->program() != new_change.program()) {
2008 c->change_program (old_change, new_change.program());
2011 if (old_change->bank() != new_change.bank()) {
2012 c->change_bank (old_change, new_change.bank());
2015 _model->apply_command (*trackview.session(), c);
2016 trackview.editor().commit_reversible_command ();
2018 _patch_changes.clear ();
2019 display_patch_changes ();
2022 /** Add a patch change to the region.
2023 * @param t Time in frames relative to region position
2024 * @param patch Patch to add; time and channel are ignored (time is converted from t, and channel comes from
2025 * MidiTimeAxisView::get_channel_for_add())
2028 MidiRegionView::add_patch_change (framecnt_t t, Evoral::PatchChange<Evoral::Beats> const & patch)
2030 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
2031 string name = _("add patch change");
2033 trackview.editor().begin_reversible_command (name);
2034 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (name);
2035 c->add (MidiModel::PatchChangePtr (
2036 new Evoral::PatchChange<Evoral::Beats> (
2037 absolute_frames_to_source_beats (_region->position() + t),
2038 mtv->get_channel_for_add(), patch.program(), patch.bank()
2043 _model->apply_command (*trackview.session(), c);
2044 trackview.editor().commit_reversible_command ();
2046 _patch_changes.clear ();
2047 display_patch_changes ();
2051 MidiRegionView::move_patch_change (PatchChange& pc, Evoral::Beats t)
2053 trackview.editor().begin_reversible_command (_("move patch change"));
2054 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("move patch change"));
2055 c->change_time (pc.patch (), t);
2056 _model->apply_command (*trackview.session(), c);
2057 trackview.editor().commit_reversible_command ();
2059 _patch_changes.clear ();
2060 display_patch_changes ();
2064 MidiRegionView::delete_patch_change (PatchChange* pc)
2066 trackview.editor().begin_reversible_command (_("delete patch change"));
2067 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("delete patch change"));
2068 c->remove (pc->patch ());
2069 _model->apply_command (*trackview.session(), c);
2070 trackview.editor().commit_reversible_command ();
2072 _patch_changes.clear ();
2073 display_patch_changes ();
2077 MidiRegionView::step_patch (PatchChange& patch, bool bank, int delta)
2079 MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key(patch.patch());
2081 key.set_bank(key.bank() + delta);
2083 key.set_program(key.program() + delta);
2085 change_patch_change(patch, key);
2089 MidiRegionView::maybe_remove_deleted_note_from_selection (NoteBase* cne)
2091 if (_selection.empty()) {
2095 _selection.erase (cne);
2099 MidiRegionView::delete_selection()
2101 if (_selection.empty()) {
2105 start_note_diff_command (_("delete selection"));
2107 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2108 if ((*i)->selected()) {
2109 _note_diff_command->remove((*i)->note());
2119 MidiRegionView::delete_note (boost::shared_ptr<NoteType> n)
2121 start_note_diff_command (_("delete note"));
2122 _note_diff_command->remove (n);
2125 trackview.editor().verbose_cursor()->hide ();
2129 MidiRegionView::clear_selection_except (NoteBase* ev, bool signal)
2131 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2133 Selection::iterator tmp = i;
2136 (*i)->set_selected (false);
2137 (*i)->hide_velocity ();
2138 _selection.erase (i);
2146 if (!ev && _entered) {
2147 // Clearing selection entirely, ungrab keyboard
2148 Keyboard::magic_widget_drop_focus();
2149 _grabbed_keyboard = false;
2152 /* this does not change the status of this regionview w.r.t the editor
2157 SelectionCleared (this); /* EMIT SIGNAL */
2162 MidiRegionView::unique_select(NoteBase* ev)
2164 const bool selection_was_empty = _selection.empty();
2166 clear_selection_except (ev);
2168 /* don't bother with checking to see if we should remove this
2169 regionview from the editor selection, since we're about to add
2170 another note, and thus put/keep this regionview in the editor
2174 if (!ev->selected()) {
2175 add_to_selection (ev);
2176 if (selection_was_empty && _entered) {
2177 // Grab keyboard for moving notes with arrow keys
2178 Keyboard::magic_widget_grab_focus();
2179 _grabbed_keyboard = true;
2185 MidiRegionView::select_all_notes ()
2189 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2190 add_to_selection (*i);
2195 MidiRegionView::select_range (framepos_t start, framepos_t end)
2199 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2200 framepos_t t = source_beats_to_absolute_frames((*i)->note()->time());
2201 if (t >= start && t <= end) {
2202 add_to_selection (*i);
2208 MidiRegionView::invert_selection ()
2210 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2211 if ((*i)->selected()) {
2212 remove_from_selection(*i);
2214 add_to_selection (*i);
2219 /** Used for selection undo/redo.
2220 The requested notes most likely won't exist in the view until the next model redisplay.
2223 MidiRegionView::select_notes (list<boost::shared_ptr<NoteType> > notes)
2226 list<boost::shared_ptr<NoteType> >::iterator n;
2228 for (n = notes.begin(); n != notes.end(); ++n) {
2229 if ((cne = find_canvas_note(*(*n))) != 0) {
2230 add_to_selection (cne);
2232 _pending_note_selection.insert(*n);
2238 MidiRegionView::select_matching_notes (uint8_t notenum, uint16_t channel_mask, bool add, bool extend)
2240 bool have_selection = !_selection.empty();
2241 uint8_t low_note = 127;
2242 uint8_t high_note = 0;
2243 MidiModel::Notes& notes (_model->notes());
2244 _optimization_iterator = _events.begin();
2246 if (extend && !have_selection) {
2250 /* scan existing selection to get note range */
2252 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2253 if ((*i)->note()->note() < low_note) {
2254 low_note = (*i)->note()->note();
2256 if ((*i)->note()->note() > high_note) {
2257 high_note = (*i)->note()->note();
2264 if (!extend && (low_note == high_note) && (high_note == notenum)) {
2265 /* only note previously selected is the one we are
2266 * reselecting. treat this as cancelling the selection.
2273 low_note = min (low_note, notenum);
2274 high_note = max (high_note, notenum);
2277 _no_sound_notes = true;
2279 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
2281 boost::shared_ptr<NoteType> note (*n);
2283 bool select = false;
2285 if (((1 << note->channel()) & channel_mask) != 0) {
2287 if ((note->note() >= low_note && note->note() <= high_note)) {
2290 } else if (note->note() == notenum) {
2296 if ((cne = find_canvas_note (note)) != 0) {
2297 // extend is false because we've taken care of it,
2298 // since it extends by time range, not pitch.
2299 note_selected (cne, add, false);
2303 add = true; // we need to add all remaining matching notes, even if the passed in value was false (for "set")
2307 _no_sound_notes = false;
2311 MidiRegionView::toggle_matching_notes (uint8_t notenum, uint16_t channel_mask)
2313 MidiModel::Notes& notes (_model->notes());
2314 _optimization_iterator = _events.begin();
2316 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
2318 boost::shared_ptr<NoteType> note (*n);
2321 if (note->note() == notenum && (((0x0001 << note->channel()) & channel_mask) != 0)) {
2322 if ((cne = find_canvas_note (note)) != 0) {
2323 if (cne->selected()) {
2324 note_deselected (cne);
2326 note_selected (cne, true, false);
2334 MidiRegionView::note_selected (NoteBase* ev, bool add, bool extend)
2337 clear_selection_except (ev);
2338 if (!_selection.empty()) {
2339 PublicEditor& editor (trackview.editor());
2340 editor.get_selection().add (this);
2346 if (!ev->selected()) {
2347 add_to_selection (ev);
2351 /* find end of latest note selected, select all between that and the start of "ev" */
2353 Evoral::Beats earliest = Evoral::MaxBeats;
2354 Evoral::Beats latest = Evoral::Beats();
2356 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2357 if ((*i)->note()->end_time() > latest) {
2358 latest = (*i)->note()->end_time();
2360 if ((*i)->note()->time() < earliest) {
2361 earliest = (*i)->note()->time();
2365 if (ev->note()->end_time() > latest) {
2366 latest = ev->note()->end_time();
2369 if (ev->note()->time() < earliest) {
2370 earliest = ev->note()->time();
2373 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2375 /* find notes entirely within OR spanning the earliest..latest range */
2377 if (((*i)->note()->time() >= earliest && (*i)->note()->end_time() <= latest) ||
2378 ((*i)->note()->time() <= earliest && (*i)->note()->end_time() >= latest)) {
2379 add_to_selection (*i);
2387 MidiRegionView::note_deselected(NoteBase* ev)
2389 remove_from_selection (ev);
2393 MidiRegionView::update_drag_selection(framepos_t start, framepos_t end, double gy0, double gy1, bool extend)
2395 PublicEditor& editor = trackview.editor();
2397 // Convert to local coordinates
2398 const framepos_t p = _region->position();
2399 const double y = midi_view()->y_position();
2400 const double x0 = editor.sample_to_pixel(max((framepos_t)0, start - p));
2401 const double x1 = editor.sample_to_pixel(max((framepos_t)0, end - p));
2402 const double y0 = max(0.0, gy0 - y);
2403 const double y1 = max(0.0, gy1 - y);
2405 // TODO: Make this faster by storing the last updated selection rect, and only
2406 // adjusting things that are in the area that appears/disappeared.
2407 // We probably need a tree to be able to find events in O(log(n)) time.
2409 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2410 if ((*i)->x0() < x1 && (*i)->x1() > x0 && (*i)->y0() < y1 && (*i)->y1() > y0) {
2411 // Rectangles intersect
2412 if (!(*i)->selected()) {
2413 add_to_selection (*i);
2415 } else if ((*i)->selected() && !extend) {
2416 // Rectangles do not intersect
2417 remove_from_selection (*i);
2421 typedef RouteTimeAxisView::AutomationTracks ATracks;
2422 typedef std::list<Selectable*> Selectables;
2424 /* Add control points to selection. */
2425 const ATracks& atracks = midi_view()->automation_tracks();
2426 Selectables selectables;
2427 editor.get_selection().clear_points();
2428 for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) {
2429 a->second->get_selectables(start, end, gy0, gy1, selectables);
2430 for (Selectables::const_iterator s = selectables.begin(); s != selectables.end(); ++s) {
2431 ControlPoint* cp = dynamic_cast<ControlPoint*>(*s);
2433 editor.get_selection().add(cp);
2436 a->second->set_selected_points(editor.get_selection().points);
2441 MidiRegionView::update_vertical_drag_selection (double y1, double y2, bool extend)
2447 // TODO: Make this faster by storing the last updated selection rect, and only
2448 // adjusting things that are in the area that appears/disappeared.
2449 // We probably need a tree to be able to find events in O(log(n)) time.
2451 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2452 if (((*i)->y1() >= y1 && (*i)->y1() <= y2)) {
2453 // within y- (note-) range
2454 if (!(*i)->selected()) {
2455 add_to_selection (*i);
2457 } else if ((*i)->selected() && !extend) {
2458 remove_from_selection (*i);
2464 MidiRegionView::remove_from_selection (NoteBase* ev)
2466 Selection::iterator i = _selection.find (ev);
2468 if (i != _selection.end()) {
2469 _selection.erase (i);
2470 if (_selection.empty() && _grabbed_keyboard) {
2472 Keyboard::magic_widget_drop_focus();
2473 _grabbed_keyboard = false;
2477 ev->set_selected (false);
2478 ev->hide_velocity ();
2480 if (_selection.empty()) {
2481 PublicEditor& editor (trackview.editor());
2482 editor.get_selection().remove (this);
2487 MidiRegionView::add_to_selection (NoteBase* ev)
2489 const bool selection_was_empty = _selection.empty();
2491 if (_selection.insert (ev).second) {
2492 ev->set_selected (true);
2493 start_playing_midi_note ((ev)->note());
2494 if (selection_was_empty && _entered) {
2495 // Grab keyboard for moving notes with arrow keys
2496 Keyboard::magic_widget_grab_focus();
2497 _grabbed_keyboard = true;
2501 if (selection_was_empty) {
2502 PublicEditor& editor (trackview.editor());
2503 editor.get_selection().add (this);
2508 MidiRegionView::move_selection(double dx, double dy, double cumulative_dy)
2510 typedef vector<boost::shared_ptr<NoteType> > PossibleChord;
2511 PossibleChord to_play;
2512 Evoral::Beats earliest = Evoral::MaxBeats;
2514 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2515 if ((*i)->note()->time() < earliest) {
2516 earliest = (*i)->note()->time();
2520 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2521 if ((*i)->note()->time() == earliest) {
2522 to_play.push_back ((*i)->note());
2524 (*i)->move_event(dx, dy);
2527 if (dy && !_selection.empty() && !_no_sound_notes && ARDOUR_UI::config()->get_sound_midi_notes()) {
2529 if (to_play.size() > 1) {
2531 PossibleChord shifted;
2533 for (PossibleChord::iterator n = to_play.begin(); n != to_play.end(); ++n) {
2534 boost::shared_ptr<NoteType> moved_note (new NoteType (**n));
2535 moved_note->set_note (moved_note->note() + cumulative_dy);
2536 shifted.push_back (moved_note);
2539 start_playing_midi_chord (shifted);
2541 } else if (!to_play.empty()) {
2543 boost::shared_ptr<NoteType> moved_note (new NoteType (*to_play.front()));
2544 moved_note->set_note (moved_note->note() + cumulative_dy);
2545 start_playing_midi_note (moved_note);
2551 MidiRegionView::note_dropped(NoteBase *, frameoffset_t dt, int8_t dnote)
2553 uint8_t lowest_note_in_selection = 127;
2554 uint8_t highest_note_in_selection = 0;
2555 uint8_t highest_note_difference = 0;
2557 // find highest and lowest notes first
2559 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2560 uint8_t pitch = (*i)->note()->note();
2561 lowest_note_in_selection = std::min(lowest_note_in_selection, pitch);
2562 highest_note_in_selection = std::max(highest_note_in_selection, pitch);
2566 cerr << "dnote: " << (int) dnote << endl;
2567 cerr << "lowest note (streamview): " << int(midi_stream_view()->lowest_note())
2568 << " highest note (streamview): " << int(midi_stream_view()->highest_note()) << endl;
2569 cerr << "lowest note (selection): " << int(lowest_note_in_selection) << " highest note(selection): "
2570 << int(highest_note_in_selection) << endl;
2571 cerr << "selection size: " << _selection.size() << endl;
2572 cerr << "Highest note in selection: " << (int) highest_note_in_selection << endl;
2575 // Make sure the note pitch does not exceed the MIDI standard range
2576 if (highest_note_in_selection + dnote > 127) {
2577 highest_note_difference = highest_note_in_selection - 127;
2580 start_note_diff_command (_("move notes"));
2582 for (Selection::iterator i = _selection.begin(); i != _selection.end() ; ++i) {
2584 framepos_t new_frames = source_beats_to_absolute_frames ((*i)->note()->time()) + dt;
2585 Evoral::Beats new_time = absolute_frames_to_source_beats (new_frames);
2591 note_diff_add_change (*i, MidiModel::NoteDiffCommand::StartTime, new_time);
2593 uint8_t original_pitch = (*i)->note()->note();
2594 uint8_t new_pitch = original_pitch + dnote - highest_note_difference;
2596 // keep notes in standard midi range
2597 clamp_to_0_127(new_pitch);
2599 lowest_note_in_selection = std::min(lowest_note_in_selection, new_pitch);
2600 highest_note_in_selection = std::max(highest_note_in_selection, new_pitch);
2602 note_diff_add_change (*i, MidiModel::NoteDiffCommand::NoteNumber, new_pitch);
2607 // care about notes being moved beyond the upper/lower bounds on the canvas
2608 if (lowest_note_in_selection < midi_stream_view()->lowest_note() ||
2609 highest_note_in_selection > midi_stream_view()->highest_note()) {
2610 midi_stream_view()->set_note_range(MidiStreamView::ContentsRange);
2614 /** @param x Pixel relative to the region position.
2615 * @return Snapped frame relative to the region position.
2618 MidiRegionView::snap_pixel_to_sample(double x)
2620 PublicEditor& editor (trackview.editor());
2621 return snap_frame_to_frame (editor.pixel_to_sample (x));
2624 /** @param x Pixel relative to the region position.
2625 * @return Snapped pixel relative to the region position.
2628 MidiRegionView::snap_to_pixel(double x)
2630 return (double) trackview.editor().sample_to_pixel(snap_pixel_to_sample(x));
2634 MidiRegionView::get_position_pixels()
2636 framepos_t region_frame = get_position();
2637 return trackview.editor().sample_to_pixel(region_frame);
2641 MidiRegionView::get_end_position_pixels()
2643 framepos_t frame = get_position() + get_duration ();
2644 return trackview.editor().sample_to_pixel(frame);
2648 MidiRegionView::source_beats_to_absolute_frames(Evoral::Beats beats) const
2650 /* the time converter will return the frame corresponding to `beats'
2651 relative to the start of the source. The start of the source
2652 is an implied position given by region->position - region->start
2654 const framepos_t source_start = _region->position() - _region->start();
2655 return source_start + _source_relative_time_converter.to (beats);
2659 MidiRegionView::absolute_frames_to_source_beats(framepos_t frames) const
2661 /* the `frames' argument needs to be converted into a frame count
2662 relative to the start of the source before being passed in to the
2665 const framepos_t source_start = _region->position() - _region->start();
2666 return _source_relative_time_converter.from (frames - source_start);
2670 MidiRegionView::region_beats_to_region_frames(Evoral::Beats beats) const
2672 return _region_relative_time_converter.to(beats);
2676 MidiRegionView::region_frames_to_region_beats(framepos_t frames) const
2678 return _region_relative_time_converter.from(frames);
2682 MidiRegionView::begin_resizing (bool /*at_front*/)
2684 _resize_data.clear();
2686 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2687 Note *note = dynamic_cast<Note*> (*i);
2689 // only insert CanvasNotes into the map
2691 NoteResizeData *resize_data = new NoteResizeData();
2692 resize_data->note = note;
2694 // create a new SimpleRect from the note which will be the resize preview
2695 ArdourCanvas::Rectangle *resize_rect = new ArdourCanvas::Rectangle (_note_group,
2696 ArdourCanvas::Rect (note->x0(), note->y0(), note->x0(), note->y1()));
2698 // calculate the colors: get the color settings
2699 uint32_t fill_color = UINT_RGBA_CHANGE_A(
2700 ARDOUR_UI::config()->color ("midi note selected"),
2703 // make the resize preview notes more transparent and bright
2704 fill_color = UINT_INTERPOLATE(fill_color, 0xFFFFFF40, 0.5);
2706 // calculate color based on note velocity
2707 resize_rect->set_fill_color (UINT_INTERPOLATE(
2708 NoteBase::meter_style_fill_color(note->note()->velocity(), note->selected()),
2712 resize_rect->set_outline_color (NoteBase::calculate_outline (
2713 ARDOUR_UI::config()->color ("midi note selected")));
2715 resize_data->resize_rect = resize_rect;
2716 _resize_data.push_back(resize_data);
2721 /** Update resizing notes while user drags.
2722 * @param primary `primary' note for the drag; ie the one that is used as the reference in non-relative mode.
2723 * @param at_front which end of the note (true == note on, false == note off)
2724 * @param delta_x change in mouse position since the start of the drag
2725 * @param relative true if relative resizing is taking place, false if absolute resizing. This only makes
2726 * a difference when multiple notes are being resized; in relative mode, each note's length is changed by the
2727 * amount of the drag. In non-relative mode, all selected notes are set to have the same start or end point
2728 * as the \a primary note.
2731 MidiRegionView::update_resizing (NoteBase* primary, bool at_front, double delta_x, bool relative)
2733 bool cursor_set = false;
2735 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2736 ArdourCanvas::Rectangle* resize_rect = (*i)->resize_rect;
2737 Note* canvas_note = (*i)->note;
2742 current_x = canvas_note->x0() + delta_x;
2744 current_x = primary->x0() + delta_x;
2748 current_x = canvas_note->x1() + delta_x;
2750 current_x = primary->x1() + delta_x;
2754 if (current_x < 0) {
2755 // This works even with snapping because RegionView::snap_frame_to_frame()
2756 // snaps forward if the snapped sample is before the beginning of the region
2759 if (current_x > trackview.editor().sample_to_pixel(_region->length())) {
2760 current_x = trackview.editor().sample_to_pixel(_region->length());
2764 resize_rect->set_x0 (snap_to_pixel(current_x));
2765 resize_rect->set_x1 (canvas_note->x1());
2767 resize_rect->set_x1 (snap_to_pixel(current_x));
2768 resize_rect->set_x0 (canvas_note->x0());
2772 const double snapped_x = snap_pixel_to_sample (current_x);
2773 Evoral::Beats beats = region_frames_to_region_beats (snapped_x);
2774 Evoral::Beats len = Evoral::Beats();
2777 if (beats < canvas_note->note()->end_time()) {
2778 len = canvas_note->note()->time() - beats;
2779 len += canvas_note->note()->length();
2782 if (beats >= canvas_note->note()->time()) {
2783 len = beats - canvas_note->note()->time();
2787 len = std::max(Evoral::Beats(1 / 512.0), len);
2790 snprintf (buf, sizeof (buf), "%.3g beats", len.to_double());
2791 show_verbose_cursor (buf, 0, 0);
2800 /** Finish resizing notes when the user releases the mouse button.
2801 * Parameters the same as for \a update_resizing().
2804 MidiRegionView::commit_resizing (NoteBase* primary, bool at_front, double delta_x, bool relative)
2806 start_note_diff_command (_("resize notes"));
2808 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2809 Note* canvas_note = (*i)->note;
2810 ArdourCanvas::Rectangle* resize_rect = (*i)->resize_rect;
2812 /* Get the new x position for this resize, which is in pixels relative
2813 * to the region position.
2820 current_x = canvas_note->x0() + delta_x;
2822 current_x = primary->x0() + delta_x;
2826 current_x = canvas_note->x1() + delta_x;
2828 current_x = primary->x1() + delta_x;
2832 if (current_x < 0) {
2835 if (current_x > trackview.editor().sample_to_pixel(_region->length())) {
2836 current_x = trackview.editor().sample_to_pixel(_region->length());
2839 /* Convert that to a frame within the source */
2840 current_x = snap_pixel_to_sample (current_x) + _region->start ();
2842 /* and then to beats */
2843 const Evoral::Beats x_beats = region_frames_to_region_beats (current_x);
2845 if (at_front && x_beats < canvas_note->note()->end_time()) {
2846 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::StartTime, x_beats);
2848 Evoral::Beats len = canvas_note->note()->time() - x_beats;
2849 len += canvas_note->note()->length();
2852 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
2857 const Evoral::Beats len = std::max(Evoral::Beats(1 / 512.0),
2858 x_beats - canvas_note->note()->time());
2859 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
2866 _resize_data.clear();
2871 MidiRegionView::abort_resizing ()
2873 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2874 delete (*i)->resize_rect;
2878 _resize_data.clear ();
2882 MidiRegionView::change_note_velocity(NoteBase* event, int8_t velocity, bool relative)
2884 uint8_t new_velocity;
2887 new_velocity = event->note()->velocity() + velocity;
2888 clamp_to_0_127(new_velocity);
2890 new_velocity = velocity;
2893 event->set_selected (event->selected()); // change color
2895 note_diff_add_change (event, MidiModel::NoteDiffCommand::Velocity, new_velocity);
2899 MidiRegionView::change_note_note (NoteBase* event, int8_t note, bool relative)
2904 new_note = event->note()->note() + note;
2909 clamp_to_0_127 (new_note);
2910 note_diff_add_change (event, MidiModel::NoteDiffCommand::NoteNumber, new_note);
2914 MidiRegionView::trim_note (NoteBase* event, Evoral::Beats front_delta, Evoral::Beats end_delta)
2916 bool change_start = false;
2917 bool change_length = false;
2918 Evoral::Beats new_start;
2919 Evoral::Beats new_length;
2921 /* NOTE: the semantics of the two delta arguments are slightly subtle:
2923 front_delta: if positive - move the start of the note later in time (shortening it)
2924 if negative - move the start of the note earlier in time (lengthening it)
2926 end_delta: if positive - move the end of the note later in time (lengthening it)
2927 if negative - move the end of the note earlier in time (shortening it)
2930 if (!!front_delta) {
2931 if (front_delta < 0) {
2933 if (event->note()->time() < -front_delta) {
2934 new_start = Evoral::Beats();
2936 new_start = event->note()->time() + front_delta; // moves earlier
2939 /* start moved toward zero, so move the end point out to where it used to be.
2940 Note that front_delta is negative, so this increases the length.
2943 new_length = event->note()->length() - front_delta;
2944 change_start = true;
2945 change_length = true;
2949 Evoral::Beats new_pos = event->note()->time() + front_delta;
2951 if (new_pos < event->note()->end_time()) {
2952 new_start = event->note()->time() + front_delta;
2953 /* start moved toward the end, so move the end point back to where it used to be */
2954 new_length = event->note()->length() - front_delta;
2955 change_start = true;
2956 change_length = true;
2963 bool can_change = true;
2964 if (end_delta < 0) {
2965 if (event->note()->length() < -end_delta) {
2971 new_length = event->note()->length() + end_delta;
2972 change_length = true;
2977 note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_start);
2980 if (change_length) {
2981 note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, new_length);
2986 MidiRegionView::change_note_channel (NoteBase* event, int8_t chn, bool relative)
2988 uint8_t new_channel;
2992 if (event->note()->channel() < -chn) {
2995 new_channel = event->note()->channel() + chn;
2998 new_channel = event->note()->channel() + chn;
3001 new_channel = (uint8_t) chn;
3004 note_diff_add_change (event, MidiModel::NoteDiffCommand::Channel, new_channel);
3008 MidiRegionView::change_note_time (NoteBase* event, Evoral::Beats delta, bool relative)
3010 Evoral::Beats new_time;
3014 if (event->note()->time() < -delta) {
3015 new_time = Evoral::Beats();
3017 new_time = event->note()->time() + delta;
3020 new_time = event->note()->time() + delta;
3026 note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_time);
3030 MidiRegionView::change_note_length (NoteBase* event, Evoral::Beats t)
3032 note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, t);
3036 MidiRegionView::change_velocities (bool up, bool fine, bool allow_smush, bool all_together)
3041 if (_selection.empty()) {
3056 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3057 if ((*i)->note()->velocity() < -delta || (*i)->note()->velocity() + delta > 127) {
3063 start_note_diff_command (_("change velocities"));
3065 for (Selection::iterator i = _selection.begin(); i != _selection.end();) {
3066 Selection::iterator next = i;
3070 if (i == _selection.begin()) {
3071 change_note_velocity (*i, delta, true);
3072 value = (*i)->note()->velocity() + delta;
3074 change_note_velocity (*i, value, false);
3078 change_note_velocity (*i, delta, true);
3087 if (!_selection.empty()) {
3089 snprintf (buf, sizeof (buf), "Vel %d",
3090 (int) (*_selection.begin())->note()->velocity());
3091 show_verbose_cursor (buf, 10, 10);
3097 MidiRegionView::transpose (bool up, bool fine, bool allow_smush)
3099 if (_selection.empty()) {
3116 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3118 if ((int8_t) (*i)->note()->note() + delta <= 0) {
3122 if ((int8_t) (*i)->note()->note() + delta > 127) {
3129 start_note_diff_command (_("transpose"));
3131 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3132 Selection::iterator next = i;
3134 change_note_note (*i, delta, true);
3142 MidiRegionView::change_note_lengths (bool fine, bool shorter, Evoral::Beats delta, bool start, bool end)
3146 delta = Evoral::Beats(1.0/128.0);
3148 /* grab the current grid distance */
3149 delta = get_grid_beats(_region->position());
3157 start_note_diff_command (_("change note lengths"));
3159 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3160 Selection::iterator next = i;
3163 /* note the negation of the delta for start */
3166 (start ? -delta : Evoral::Beats()),
3167 (end ? delta : Evoral::Beats()));
3176 MidiRegionView::nudge_notes (bool forward, bool fine)
3178 if (_selection.empty()) {
3182 /* pick a note as the point along the timeline to get the nudge distance.
3183 its not necessarily the earliest note, so we may want to pull the notes out
3184 into a vector and sort before using the first one.
3187 const framepos_t ref_point = source_beats_to_absolute_frames ((*(_selection.begin()))->note()->time());
3188 Evoral::Beats delta;
3192 /* non-fine, move by 1 bar regardless of snap */
3193 delta = Evoral::Beats(trackview.session()->tempo_map().meter_at(ref_point).divisions_per_bar());
3195 } else if (trackview.editor().snap_mode() == Editing::SnapOff) {
3197 /* grid is off - use nudge distance */
3200 const framecnt_t distance = trackview.editor().get_nudge_distance (ref_point, unused);
3201 delta = region_frames_to_region_beats (fabs ((double)distance));
3207 framepos_t next_pos = ref_point;
3210 if (max_framepos - 1 < next_pos) {
3214 if (next_pos == 0) {
3220 trackview.editor().snap_to (next_pos, (forward ? RoundUpAlways : RoundDownAlways), false);
3221 const framecnt_t distance = ref_point - next_pos;
3222 delta = region_frames_to_region_beats (fabs ((double)distance));
3233 start_note_diff_command (_("nudge"));
3235 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3236 Selection::iterator next = i;
3238 change_note_time (*i, delta, true);
3246 MidiRegionView::change_channel(uint8_t channel)
3248 start_note_diff_command(_("change channel"));
3249 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3250 note_diff_add_change (*i, MidiModel::NoteDiffCommand::Channel, channel);
3258 MidiRegionView::note_entered(NoteBase* ev)
3260 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3262 if (_mouse_state == SelectTouchDragging) {
3263 note_selected (ev, true);
3264 } else if (editor->current_mouse_mode() == MouseContent) {
3265 show_verbose_cursor (ev->note ());
3266 } else if (editor->current_mouse_mode() == MouseDraw) {
3267 show_verbose_cursor (ev->note ());
3272 MidiRegionView::note_left (NoteBase*)
3274 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3276 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3277 (*i)->hide_velocity ();
3280 editor->verbose_cursor()->hide ();
3284 MidiRegionView::patch_entered (PatchChange* p)
3287 /* XXX should get patch name if we can */
3288 s << _("Bank ") << (p->patch()->bank() + MIDI_BP_ZERO) << '\n'
3289 << _("Program ") << ((int) p->patch()->program()) + MIDI_BP_ZERO << '\n'
3290 << _("Channel ") << ((int) p->patch()->channel() + 1);
3291 show_verbose_cursor (s.str(), 10, 20);
3292 p->item().grab_focus();
3296 MidiRegionView::patch_left (PatchChange *)
3298 trackview.editor().verbose_cursor()->hide ();
3299 /* focus will transfer back via the enter-notify event sent to this
3305 MidiRegionView::sysex_entered (SysEx* p)
3309 // need a way to extract text from p->_flag->_text
3311 // show_verbose_cursor (s.str(), 10, 20);
3312 p->item().grab_focus();
3316 MidiRegionView::sysex_left (SysEx *)
3318 trackview.editor().verbose_cursor()->hide ();
3319 /* focus will transfer back via the enter-notify event sent to this
3325 MidiRegionView::note_mouse_position (float x_fraction, float /*y_fraction*/, bool can_set_cursor)
3327 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3328 Editing::MouseMode mm = editor->current_mouse_mode();
3329 bool trimmable = (mm == MouseContent || mm == MouseTimeFX || mm == MouseDraw);
3331 Editor::EnterContext* ctx = editor->get_enter_context(NoteItem);
3332 if (can_set_cursor && ctx) {
3333 if (trimmable && x_fraction > 0.0 && x_fraction < 0.2) {
3334 ctx->cursor_ctx->change(editor->cursors()->left_side_trim);
3335 } else if (trimmable && x_fraction >= 0.8 && x_fraction < 1.0) {
3336 ctx->cursor_ctx->change(editor->cursors()->right_side_trim);
3338 ctx->cursor_ctx->change(editor->cursors()->grabber_note);
3344 MidiRegionView::get_fill_color() const
3346 const std::string mod_name = (_dragging ? "dragging region" :
3347 trackview.editor().internal_editing() ? "editable region" :
3350 return ARDOUR_UI::config()->color_mod ("selected region base", mod_name);
3351 } else if ((!ARDOUR_UI::config()->get_show_name_highlight() || high_enough_for_name) &&
3352 !ARDOUR_UI::config()->get_color_regions_using_track_color()) {
3353 return ARDOUR_UI::config()->color_mod ("midi frame base", mod_name);
3355 return ARDOUR_UI::config()->color_mod (fill_color, mod_name);
3359 MidiRegionView::midi_channel_mode_changed ()
3361 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3362 uint16_t mask = mtv->midi_track()->get_playback_channel_mask();
3363 ChannelMode mode = mtv->midi_track()->get_playback_channel_mode ();
3365 if (mode == ForceChannel) {
3366 mask = 0xFFFF; // Show all notes as active (below)
3369 // Update notes for selection
3370 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3371 (*i)->on_channel_selection_change (mask);
3374 _patch_changes.clear ();
3375 display_patch_changes ();
3379 MidiRegionView::instrument_settings_changed ()
3385 MidiRegionView::cut_copy_clear (Editing::CutCopyOp op)
3387 if (_selection.empty()) {
3391 PublicEditor& editor (trackview.editor());
3395 /* XXX what to do ? */
3399 editor.get_cut_buffer().add (selection_as_cut_buffer());
3407 start_note_diff_command();
3409 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3416 note_diff_remove_note (*i);
3426 MidiRegionView::selection_as_cut_buffer () const
3430 for (Selection::const_iterator i = _selection.begin(); i != _selection.end(); ++i) {
3431 NoteType* n = (*i)->note().get();
3432 notes.insert (boost::shared_ptr<NoteType> (new NoteType (*n)));
3435 MidiCutBuffer* cb = new MidiCutBuffer (trackview.session());
3441 /** This method handles undo */
3443 MidiRegionView::paste (framepos_t pos, const ::Selection& selection, PasteContext& ctx)
3445 // Paste notes, if available
3446 MidiNoteSelection::const_iterator m = selection.midi_notes.get_nth(ctx.counts.n_notes());
3447 if (m != selection.midi_notes.end()) {
3448 ctx.counts.increase_n_notes();
3449 paste_internal(pos, ctx.count, ctx.times, **m);
3452 // Paste control points to automation children, if available
3453 typedef RouteTimeAxisView::AutomationTracks ATracks;
3454 const ATracks& atracks = midi_view()->automation_tracks();
3455 for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) {
3456 a->second->paste(pos, selection, ctx);
3462 /** This method handles undo */
3464 MidiRegionView::paste_internal (framepos_t pos, unsigned paste_count, float times, const MidiCutBuffer& mcb)
3470 start_note_diff_command (_("paste"));
3472 const Evoral::Beats snap_beats = get_grid_beats(pos);
3473 const Evoral::Beats first_time = (*mcb.notes().begin())->time();
3474 const Evoral::Beats last_time = (*mcb.notes().rbegin())->end_time();
3475 const Evoral::Beats duration = last_time - first_time;
3476 const Evoral::Beats snap_duration = duration.snap_to(snap_beats);
3477 const Evoral::Beats paste_offset = snap_duration * paste_count;
3478 const Evoral::Beats pos_beats = absolute_frames_to_source_beats(pos) + paste_offset;
3479 Evoral::Beats end_point = Evoral::Beats();
3481 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste data spans from %1 to %2 (%3) ; paste pos beats = %4 (based on %5 - %6)\n",
3484 duration, pos, _region->position(),
3489 for (int n = 0; n < (int) times; ++n) {
3491 for (Notes::const_iterator i = mcb.notes().begin(); i != mcb.notes().end(); ++i) {
3493 boost::shared_ptr<NoteType> copied_note (new NoteType (*((*i).get())));
3494 copied_note->set_time (pos_beats + copied_note->time() - first_time);
3496 /* make all newly added notes selected */
3498 note_diff_add_note (copied_note, true);
3499 end_point = copied_note->end_time();
3503 /* if we pasted past the current end of the region, extend the region */
3505 framepos_t end_frame = source_beats_to_absolute_frames (end_point);
3506 framepos_t region_end = _region->position() + _region->length() - 1;
3508 if (end_frame > region_end) {
3510 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste extended region from %1 to %2\n", region_end, end_frame));
3512 _region->clear_changes ();
3513 _region->set_length (end_frame - _region->position());
3514 trackview.session()->add_command (new StatefulDiffCommand (_region));
3520 struct EventNoteTimeEarlyFirstComparator {
3521 bool operator() (NoteBase* a, NoteBase* b) {
3522 return a->note()->time() < b->note()->time();
3527 MidiRegionView::time_sort_events ()
3529 if (!_sort_needed) {
3533 EventNoteTimeEarlyFirstComparator cmp;
3536 _sort_needed = false;
3540 MidiRegionView::goto_next_note (bool add_to_selection)
3542 bool use_next = false;
3544 if (_events.back()->selected()) {
3548 time_sort_events ();
3550 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3551 uint16_t const channel_mask = mtv->midi_track()->get_playback_channel_mask();
3553 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3554 if ((*i)->selected()) {
3557 } else if (use_next) {
3558 if (channel_mask & (1 << (*i)->note()->channel())) {
3559 if (!add_to_selection) {
3562 note_selected (*i, true, false);
3569 /* use the first one */
3571 if (!_events.empty() && (channel_mask & (1 << _events.front()->note()->channel ()))) {
3572 unique_select (_events.front());
3577 MidiRegionView::goto_previous_note (bool add_to_selection)
3579 bool use_next = false;
3581 if (_events.front()->selected()) {
3585 time_sort_events ();
3587 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3588 uint16_t const channel_mask = mtv->midi_track()->get_playback_channel_mask ();
3590 for (Events::reverse_iterator i = _events.rbegin(); i != _events.rend(); ++i) {
3591 if ((*i)->selected()) {
3594 } else if (use_next) {
3595 if (channel_mask & (1 << (*i)->note()->channel())) {
3596 if (!add_to_selection) {
3599 note_selected (*i, true, false);
3606 /* use the last one */
3608 if (!_events.empty() && (channel_mask & (1 << (*_events.rbegin())->note()->channel ()))) {
3609 unique_select (*(_events.rbegin()));
3614 MidiRegionView::selection_as_notelist (Notes& selected, bool allow_all_if_none_selected)
3616 bool had_selected = false;
3618 time_sort_events ();
3620 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3621 if ((*i)->selected()) {
3622 selected.insert ((*i)->note());
3623 had_selected = true;
3627 if (allow_all_if_none_selected && !had_selected) {
3628 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3629 selected.insert ((*i)->note());
3635 MidiRegionView::update_ghost_note (double x, double y)
3637 x = std::max(0.0, x);
3639 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3644 _note_group->canvas_to_item (x, y);
3646 PublicEditor& editor = trackview.editor ();
3648 framepos_t const unsnapped_frame = editor.pixel_to_sample (x);
3649 framecnt_t grid_frames;
3650 framepos_t const f = snap_frame_to_grid_underneath (unsnapped_frame, grid_frames);
3652 /* calculate time in beats relative to start of source */
3653 const Evoral::Beats length = get_grid_beats(unsnapped_frame);
3654 const Evoral::Beats time = std::max(
3656 absolute_frames_to_source_beats (f + _region->position ()));
3658 _ghost_note->note()->set_time (time);
3659 _ghost_note->note()->set_length (length);
3660 _ghost_note->note()->set_note (midi_stream_view()->y_to_note (y));
3661 _ghost_note->note()->set_channel (mtv->get_channel_for_add ());
3662 _ghost_note->note()->set_velocity (get_velocity_for_add (time));
3664 /* the ghost note does not appear in ghost regions, so pass false in here */
3665 update_note (_ghost_note, false);
3667 show_verbose_cursor (_ghost_note->note ());
3671 MidiRegionView::create_ghost_note (double x, double y)
3673 remove_ghost_note ();
3675 boost::shared_ptr<NoteType> g (new NoteType);
3676 if (midi_view()->note_mode() == Sustained) {
3677 _ghost_note = new Note (*this, _note_group, g);
3679 _ghost_note = new Hit (*this, _note_group, 10, g);
3681 _ghost_note->set_ignore_events (true);
3682 _ghost_note->set_outline_color (0x000000aa);
3683 update_ghost_note (x, y);
3684 _ghost_note->show ();
3686 show_verbose_cursor (_ghost_note->note ());
3690 MidiRegionView::remove_ghost_note ()
3697 MidiRegionView::snap_changed ()
3703 create_ghost_note (_last_ghost_x, _last_ghost_y);
3707 MidiRegionView::drop_down_keys ()
3709 _mouse_state = None;
3713 MidiRegionView::maybe_select_by_position (GdkEventButton* ev, double /*x*/, double y)
3715 /* XXX: This is dead code. What was it for? */
3717 double note = midi_stream_view()->y_to_note(y);
3719 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3721 uint16_t chn_mask = mtv->midi_track()->get_playback_channel_mask();
3723 if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
3724 get_events (e, Evoral::Sequence<Evoral::Beats>::PitchGreaterThanOrEqual, (uint8_t) floor (note), chn_mask);
3725 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
3726 get_events (e, Evoral::Sequence<Evoral::Beats>::PitchLessThanOrEqual, (uint8_t) floor (note), chn_mask);
3731 bool add_mrv_selection = false;
3733 if (_selection.empty()) {
3734 add_mrv_selection = true;
3737 for (Events::iterator i = e.begin(); i != e.end(); ++i) {
3738 if (_selection.insert (*i).second) {
3739 (*i)->set_selected (true);
3743 if (add_mrv_selection) {
3744 PublicEditor& editor (trackview.editor());
3745 editor.get_selection().add (this);
3750 MidiRegionView::color_handler ()
3752 RegionView::color_handler ();
3754 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3755 (*i)->set_selected ((*i)->selected()); // will change color
3758 /* XXX probably more to do here */
3762 MidiRegionView::enable_display (bool yn)
3764 RegionView::enable_display (yn);
3771 MidiRegionView::show_step_edit_cursor (Evoral::Beats pos)
3773 if (_step_edit_cursor == 0) {
3774 ArdourCanvas::Item* const group = get_canvas_group();
3776 _step_edit_cursor = new ArdourCanvas::Rectangle (group);
3777 _step_edit_cursor->set_y0 (0);
3778 _step_edit_cursor->set_y1 (midi_stream_view()->contents_height());
3779 _step_edit_cursor->set_fill_color (RGBA_TO_UINT (45,0,0,90));
3780 _step_edit_cursor->set_outline_color (RGBA_TO_UINT (85,0,0,90));
3783 move_step_edit_cursor (pos);
3784 _step_edit_cursor->show ();
3788 MidiRegionView::move_step_edit_cursor (Evoral::Beats pos)
3790 _step_edit_cursor_position = pos;
3792 if (_step_edit_cursor) {
3793 double pixel = trackview.editor().sample_to_pixel (region_beats_to_region_frames (pos));
3794 _step_edit_cursor->set_x0 (pixel);
3795 set_step_edit_cursor_width (_step_edit_cursor_width);
3800 MidiRegionView::hide_step_edit_cursor ()
3802 if (_step_edit_cursor) {
3803 _step_edit_cursor->hide ();
3808 MidiRegionView::set_step_edit_cursor_width (Evoral::Beats beats)
3810 _step_edit_cursor_width = beats;
3812 if (_step_edit_cursor) {
3813 _step_edit_cursor->set_x1 (_step_edit_cursor->x0() + trackview.editor().sample_to_pixel (region_beats_to_region_frames (beats)));
3817 /** Called when a diskstream on our track has received some data. Update the view, if applicable.
3818 * @param w Source that the data will end up in.
3821 MidiRegionView::data_recorded (boost::weak_ptr<MidiSource> w)
3823 if (!_active_notes) {
3824 /* we aren't actively being recorded to */
3828 boost::shared_ptr<MidiSource> src = w.lock ();
3829 if (!src || src != midi_region()->midi_source()) {
3830 /* recorded data was not destined for our source */
3834 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (&trackview);
3836 boost::shared_ptr<MidiBuffer> buf = mtv->midi_track()->get_gui_feed_buffer ();
3838 framepos_t back = max_framepos;
3840 for (MidiBuffer::iterator i = buf->begin(); i != buf->end(); ++i) {
3841 Evoral::MIDIEvent<MidiBuffer::TimeType> const ev (*i, false);
3843 if (ev.is_channel_event()) {
3844 if (get_channel_mode() == FilterChannels) {
3845 if (((uint16_t(1) << ev.channel()) & get_selected_channels()) == 0) {
3851 /* convert from session frames to source beats */
3852 Evoral::Beats const time_beats = _source_relative_time_converter.from(
3853 ev.time() - src->timeline_position() + _region->start());
3855 if (ev.type() == MIDI_CMD_NOTE_ON) {
3856 boost::shared_ptr<NoteType> note (
3857 new NoteType (ev.channel(), time_beats, Evoral::Beats(), ev.note(), ev.velocity()));
3859 add_note (note, true);
3861 /* fix up our note range */
3862 if (ev.note() < _current_range_min) {
3863 midi_stream_view()->apply_note_range (ev.note(), _current_range_max, true);
3864 } else if (ev.note() > _current_range_max) {
3865 midi_stream_view()->apply_note_range (_current_range_min, ev.note(), true);
3868 } else if (ev.type() == MIDI_CMD_NOTE_OFF) {
3869 resolve_note (ev.note (), time_beats);
3875 midi_stream_view()->check_record_layers (region(), back);
3879 MidiRegionView::trim_front_starting ()
3881 /* Reparent the note group to the region view's parent, so that it doesn't change
3882 when the region view is trimmed.
3884 _temporary_note_group = new ArdourCanvas::Container (group->parent ());
3885 _temporary_note_group->move (group->position ());
3886 _note_group->reparent (_temporary_note_group);
3890 MidiRegionView::trim_front_ending ()
3892 _note_group->reparent (group);
3893 delete _temporary_note_group;
3894 _temporary_note_group = 0;
3896 if (_region->start() < 0) {
3897 /* Trim drag made start time -ve; fix this */
3898 midi_region()->fix_negative_start ();
3903 MidiRegionView::edit_patch_change (PatchChange* pc)
3905 PatchChangeDialog d (&_source_relative_time_converter, trackview.session(), *pc->patch (), instrument_info(), Gtk::Stock::APPLY, true);
3907 int response = d.run();
3910 case Gtk::RESPONSE_ACCEPT:
3912 case Gtk::RESPONSE_REJECT:
3913 delete_patch_change (pc);
3919 change_patch_change (pc->patch(), d.patch ());
3923 MidiRegionView::delete_sysex (SysEx* /*sysex*/)
3926 // sysyex object doesn't have a pointer to a sysex event
3927 // MidiModel::SysExDiffCommand* c = _model->new_sysex_diff_command (_("delete sysex"));
3928 // c->remove (sysex->sysex());
3929 // _model->apply_command (*trackview.session(), c);
3931 //_sys_exes.clear ();
3932 // display_sysexes();
3936 MidiRegionView::show_verbose_cursor (boost::shared_ptr<NoteType> n) const
3938 using namespace MIDI::Name;
3942 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3944 boost::shared_ptr<MasterDeviceNames> device_names(mtv->get_device_names());
3946 MIDI::Name::PatchPrimaryKey patch_key;
3947 get_patch_key_at(n->time(), n->channel(), patch_key);
3948 name = device_names->note_name(mtv->gui_property(X_("midnam-custom-device-mode")),
3951 patch_key.program(),
3957 snprintf (buf, sizeof (buf), "%d %s\nCh %d Vel %d",
3959 name.empty() ? Evoral::midi_note_name (n->note()).c_str() : name.c_str(),
3960 (int) n->channel() + 1,
3961 (int) n->velocity());
3963 show_verbose_cursor(buf, 10, 20);
3967 MidiRegionView::show_verbose_cursor (string const & text, double xoffset, double yoffset) const
3969 trackview.editor().verbose_cursor()->set (text);
3970 trackview.editor().verbose_cursor()->show ();
3971 trackview.editor().verbose_cursor()->set_offset (ArdourCanvas::Duple (xoffset, yoffset));
3975 MidiRegionView::get_velocity_for_add (MidiModel::TimeType time) const
3977 if (_model->notes().empty()) {
3978 return 0x40; // No notes, use default
3981 MidiModel::Notes::const_iterator m = _model->note_lower_bound(time);
3982 if (m == _model->notes().begin()) {
3983 // Before the start, use the velocity of the first note
3984 return (*m)->velocity();
3985 } else if (m == _model->notes().end()) {
3986 // Past the end, use the velocity of the last note
3988 return (*m)->velocity();
3991 // Interpolate velocity of surrounding notes
3992 MidiModel::Notes::const_iterator n = m;
3995 const double frac = ((time - (*n)->time()).to_double() /
3996 ((*m)->time() - (*n)->time()).to_double());
3998 return (*n)->velocity() + (frac * ((*m)->velocity() - (*n)->velocity()));
4001 /** @param p A session framepos.
4002 * @param grid_frames Filled in with the number of frames that a grid interval is at p.
4003 * @return p snapped to the grid subdivision underneath it.
4006 MidiRegionView::snap_frame_to_grid_underneath (framepos_t p, framecnt_t& grid_frames) const
4008 PublicEditor& editor = trackview.editor ();
4010 const Evoral::Beats grid_beats = get_grid_beats(p);
4012 grid_frames = region_beats_to_region_frames (grid_beats);
4014 /* Hack so that we always snap to the note that we are over, instead of snapping
4015 to the next one if we're more than halfway through the one we're over.
4017 if (editor.snap_mode() == SnapNormal && p >= grid_frames / 2) {
4018 p -= grid_frames / 2;
4021 return snap_frame_to_frame (p);
4024 /** Called when the selection has been cleared in any MidiRegionView.
4025 * @param rv MidiRegionView that the selection was cleared in.
4028 MidiRegionView::selection_cleared (MidiRegionView* rv)
4034 /* Clear our selection in sympathy; but don't signal the fact */
4035 clear_selection (false);
4039 MidiRegionView::note_button_release ()
4041 _note_player.reset();
4045 MidiRegionView::get_channel_mode () const
4047 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (&trackview);
4048 return rtav->midi_track()->get_playback_channel_mode();
4052 MidiRegionView::get_selected_channels () const
4054 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (&trackview);
4055 return rtav->midi_track()->get_playback_channel_mask();
4060 MidiRegionView::get_grid_beats(framepos_t pos) const
4062 PublicEditor& editor = trackview.editor();
4063 bool success = false;
4064 Evoral::Beats beats = editor.get_grid_type_as_beats(success, pos);
4066 beats = Evoral::Beats(1);