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_playlist.h"
37 #include "ardour/midi_region.h"
38 #include "ardour/midi_source.h"
39 #include "ardour/midi_track.h"
40 #include "ardour/operations.h"
41 #include "ardour/session.h"
43 #include "evoral/Parameter.hpp"
44 #include "evoral/MIDIEvent.hpp"
45 #include "evoral/Control.hpp"
46 #include "evoral/midi_util.h"
48 #include "canvas/debug.h"
49 #include "canvas/text.h"
51 #include "automation_region_view.h"
52 #include "automation_time_axis.h"
53 #include "control_point.h"
56 #include "editor_drag.h"
57 #include "ghostregion.h"
58 #include "gui_thread.h"
59 #include "item_counts.h"
61 #include "midi_channel_dialog.h"
62 #include "midi_cut_buffer.h"
63 #include "midi_list_editor.h"
64 #include "midi_region_view.h"
65 #include "midi_streamview.h"
66 #include "midi_time_axis.h"
67 #include "midi_util.h"
68 #include "midi_velocity_dialog.h"
69 #include "mouse_cursors.h"
70 #include "note_player.h"
71 #include "paste_context.h"
72 #include "public_editor.h"
73 #include "route_time_axis.h"
74 #include "rgb_macros.h"
75 #include "selection.h"
76 #include "streamview.h"
77 #include "patch_change_dialog.h"
78 #include "verbose_cursor.h"
79 #include "ardour_ui.h"
82 #include "patch_change.h"
87 using namespace ARDOUR;
89 using namespace Editing;
91 using Gtkmm2ext::Keyboard;
93 PBD::Signal1<void, MidiRegionView *> MidiRegionView::SelectionCleared;
95 #define MIDI_BP_ZERO ((Config->get_first_midi_bank_is_zero())?0:1)
97 MidiRegionView::MidiRegionView (ArdourCanvas::Container* parent,
98 RouteTimeAxisView& tv,
99 boost::shared_ptr<MidiRegion> r,
101 uint32_t basic_color)
102 : RegionView (parent, tv, r, spu, basic_color)
103 , _current_range_min(0)
104 , _current_range_max(0)
105 , _region_relative_time_converter(r->session().tempo_map(), r->position())
106 , _source_relative_time_converter(r->session().tempo_map(), r->position() - r->start())
107 , _region_relative_time_converter_double(r->session().tempo_map(), r->position())
109 , _note_group (new ArdourCanvas::Container (group))
110 , _note_diff_command (0)
112 , _step_edit_cursor (0)
113 , _step_edit_cursor_width (1.0)
114 , _step_edit_cursor_position (0.0)
115 , _channel_selection_scoped_note (0)
116 , _temporary_note_group (0)
119 , _sort_needed (true)
120 , _optimization_iterator (_events.end())
122 , _no_sound_notes (false)
123 , _last_display_zoom (0)
126 , _grabbed_keyboard (false)
128 , _mouse_changed_selection (false)
130 CANVAS_DEBUG_NAME (_note_group, string_compose ("note group for %1", get_item_name()));
131 _note_group->raise_to_top();
132 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
134 Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&MidiRegionView::parameter_changed, this, _1), gui_context());
135 connect_to_diskstream ();
137 SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), boost::bind (&MidiRegionView::selection_cleared, this, _1), gui_context ());
139 PublicEditor& editor (trackview.editor());
140 editor.get_selection().ClearMidiNoteSelection.connect (_clear_midi_selection_connection, invalidator (*this), boost::bind (&MidiRegionView::clear_midi_selection, this), gui_context ());
143 MidiRegionView::MidiRegionView (ArdourCanvas::Container* parent,
144 RouteTimeAxisView& tv,
145 boost::shared_ptr<MidiRegion> r,
147 uint32_t basic_color,
149 TimeAxisViewItem::Visibility visibility)
150 : RegionView (parent, tv, r, spu, basic_color, recording, visibility)
151 , _current_range_min(0)
152 , _current_range_max(0)
153 , _region_relative_time_converter(r->session().tempo_map(), r->position())
154 , _source_relative_time_converter(r->session().tempo_map(), r->position() - r->start())
155 , _region_relative_time_converter_double(r->session().tempo_map(), r->position())
157 , _note_group (new ArdourCanvas::Container (group))
158 , _note_diff_command (0)
160 , _step_edit_cursor (0)
161 , _step_edit_cursor_width (1.0)
162 , _step_edit_cursor_position (0.0)
163 , _channel_selection_scoped_note (0)
164 , _temporary_note_group (0)
167 , _sort_needed (true)
168 , _optimization_iterator (_events.end())
170 , _no_sound_notes (false)
171 , _last_display_zoom (0)
174 , _grabbed_keyboard (false)
176 , _mouse_changed_selection (false)
178 CANVAS_DEBUG_NAME (_note_group, string_compose ("note group for %1", get_item_name()));
179 _note_group->raise_to_top();
181 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
183 connect_to_diskstream ();
185 SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), boost::bind (&MidiRegionView::selection_cleared, this, _1), gui_context ());
187 PublicEditor& editor (trackview.editor());
188 editor.get_selection().ClearMidiNoteSelection.connect (_clear_midi_selection_connection, invalidator (*this), boost::bind (&MidiRegionView::clear_midi_selection, this), gui_context ());
192 MidiRegionView::parameter_changed (std::string const & p)
194 if (p == "display-first-midi-bank-as-zero") {
195 if (_enable_display) {
201 MidiRegionView::MidiRegionView (const MidiRegionView& other)
202 : sigc::trackable(other)
204 , _current_range_min(0)
205 , _current_range_max(0)
206 , _region_relative_time_converter(other.region_relative_time_converter())
207 , _source_relative_time_converter(other.source_relative_time_converter())
208 , _region_relative_time_converter_double(other.region_relative_time_converter_double())
210 , _note_group (new ArdourCanvas::Container (get_canvas_group()))
211 , _note_diff_command (0)
213 , _step_edit_cursor (0)
214 , _step_edit_cursor_width (1.0)
215 , _step_edit_cursor_position (0.0)
216 , _channel_selection_scoped_note (0)
217 , _temporary_note_group (0)
220 , _sort_needed (true)
221 , _optimization_iterator (_events.end())
223 , _no_sound_notes (false)
224 , _last_display_zoom (0)
227 , _grabbed_keyboard (false)
229 , _mouse_changed_selection (false)
234 MidiRegionView::MidiRegionView (const MidiRegionView& other, boost::shared_ptr<MidiRegion> region)
235 : RegionView (other, boost::shared_ptr<Region> (region))
236 , _current_range_min(0)
237 , _current_range_max(0)
238 , _region_relative_time_converter(other.region_relative_time_converter())
239 , _source_relative_time_converter(other.source_relative_time_converter())
240 , _region_relative_time_converter_double(other.region_relative_time_converter_double())
242 , _note_group (new ArdourCanvas::Container (get_canvas_group()))
243 , _note_diff_command (0)
245 , _step_edit_cursor (0)
246 , _step_edit_cursor_width (1.0)
247 , _step_edit_cursor_position (0.0)
248 , _channel_selection_scoped_note (0)
249 , _temporary_note_group (0)
252 , _sort_needed (true)
253 , _optimization_iterator (_events.end())
255 , _no_sound_notes (false)
256 , _last_display_zoom (0)
259 , _grabbed_keyboard (false)
261 , _mouse_changed_selection (false)
267 MidiRegionView::init (bool wfd)
269 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
271 NoteBase::NoteBaseDeleted.connect (note_delete_connection, MISSING_INVALIDATOR,
272 boost::bind (&MidiRegionView::maybe_remove_deleted_note_from_selection, this, _1),
276 Glib::Threads::Mutex::Lock lm(midi_region()->midi_source(0)->mutex());
277 midi_region()->midi_source(0)->load_model(lm);
280 _model = midi_region()->midi_source(0)->model();
281 _enable_display = false;
282 fill_color_name = "midi frame base";
284 RegionView::init (false);
286 set_height (trackview.current_height());
289 region_sync_changed ();
290 region_resized (ARDOUR::bounds_change);
295 _enable_display = true;
298 display_model (_model);
302 reset_width_dependent_items (_pixel_width);
304 group->raise_to_top();
306 midi_view()->midi_track()->playback_filter().ChannelModeChanged.connect (_channel_mode_changed_connection, invalidator (*this),
307 boost::bind (&MidiRegionView::midi_channel_mode_changed, this),
310 instrument_info().Changed.connect (_instrument_changed_connection, invalidator (*this),
311 boost::bind (&MidiRegionView::instrument_settings_changed, this), gui_context());
313 trackview.editor().SnapChanged.connect(snap_changed_connection, invalidator(*this),
314 boost::bind (&MidiRegionView::snap_changed, this),
317 trackview.editor().MouseModeChanged.connect(_mouse_mode_connection, invalidator (*this),
318 boost::bind (&MidiRegionView::mouse_mode_changed, this),
321 Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&MidiRegionView::parameter_changed, this, _1), gui_context());
322 connect_to_diskstream ();
324 SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), boost::bind (&MidiRegionView::selection_cleared, this, _1), gui_context ());
326 PublicEditor& editor (trackview.editor());
327 editor.get_selection().ClearMidiNoteSelection.connect (_clear_midi_selection_connection, invalidator (*this), boost::bind (&MidiRegionView::clear_midi_selection, this), gui_context ());
331 MidiRegionView::instrument_info () const
333 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
334 return route_ui->route()->instrument_info();
337 const boost::shared_ptr<ARDOUR::MidiRegion>
338 MidiRegionView::midi_region() const
340 return boost::dynamic_pointer_cast<ARDOUR::MidiRegion>(_region);
344 MidiRegionView::connect_to_diskstream ()
346 midi_view()->midi_track()->DataRecorded.connect(
347 *this, invalidator(*this),
348 boost::bind (&MidiRegionView::data_recorded, this, _1),
353 MidiRegionView::canvas_group_event(GdkEvent* ev)
355 if (in_destructor || _recregion) {
359 if (!trackview.editor().internal_editing()) {
360 // not in internal edit mode, so just act like a normal region
361 return RegionView::canvas_group_event (ev);
367 case GDK_ENTER_NOTIFY:
368 _last_event_x = ev->crossing.x;
369 _last_event_y = ev->crossing.y;
370 enter_notify(&ev->crossing);
371 // set entered_regionview (among other things)
372 return RegionView::canvas_group_event (ev);
374 case GDK_LEAVE_NOTIFY:
375 _last_event_x = ev->crossing.x;
376 _last_event_y = ev->crossing.y;
377 leave_notify(&ev->crossing);
378 // reset entered_regionview (among other things)
379 return RegionView::canvas_group_event (ev);
382 if (scroll (&ev->scroll)) {
388 return key_press (&ev->key);
390 case GDK_KEY_RELEASE:
391 return key_release (&ev->key);
393 case GDK_BUTTON_PRESS:
394 return button_press (&ev->button);
396 case GDK_BUTTON_RELEASE:
397 r = button_release (&ev->button);
400 case GDK_MOTION_NOTIFY:
401 _last_event_x = ev->motion.x;
402 _last_event_y = ev->motion.y;
403 return motion (&ev->motion);
409 return RegionView::canvas_group_event (ev);
413 MidiRegionView::enter_notify (GdkEventCrossing* ev)
422 MidiRegionView::leave_notify (GdkEventCrossing*)
431 MidiRegionView::mouse_mode_changed ()
433 // Adjust frame colour (become more transparent for internal tools)
437 if (trackview.editor().internal_editing()) {
438 // Switched in to internal editing mode while entered
441 // Switched out of internal editing mode while entered
448 MidiRegionView::enter_internal()
450 if (trackview.editor().current_mouse_mode() == MouseDraw && _mouse_state != AddDragging) {
451 // Show ghost note under pencil
452 create_ghost_note(_last_event_x, _last_event_y);
455 if (!_selection.empty()) {
456 // Grab keyboard for moving selected notes with arrow keys
457 Keyboard::magic_widget_grab_focus();
458 _grabbed_keyboard = true;
461 // Lower frame handles below notes so they don't steal events
462 if (frame_handle_start) {
463 frame_handle_start->lower_to_bottom();
465 if (frame_handle_end) {
466 frame_handle_end->lower_to_bottom();
471 MidiRegionView::leave_internal()
473 trackview.editor().verbose_cursor()->hide ();
474 remove_ghost_note ();
476 if (_grabbed_keyboard) {
477 Keyboard::magic_widget_drop_focus();
478 _grabbed_keyboard = false;
481 // Raise frame handles above notes so they catch events
482 if (frame_handle_start) {
483 frame_handle_start->raise_to_top();
485 if (frame_handle_end) {
486 frame_handle_end->raise_to_top();
489 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
491 mtv->set_note_highlight (NO_MIDI_NOTE);
496 MidiRegionView::button_press (GdkEventButton* ev)
498 if (ev->button != 1) {
502 Editor* editor = dynamic_cast<Editor *> (&trackview.editor());
503 MouseMode m = editor->current_mouse_mode();
505 if (m == MouseContent && Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())) {
506 _press_cursor_ctx = CursorContext::create(*editor, editor->cursors()->midi_pencil);
509 if (_mouse_state != SelectTouchDragging) {
511 _pressed_button = ev->button;
512 _mouse_state = Pressed;
517 _pressed_button = ev->button;
518 _mouse_changed_selection = false;
524 MidiRegionView::button_release (GdkEventButton* ev)
526 double event_x, event_y;
528 if (ev->button != 1) {
535 group->canvas_to_item (event_x, event_y);
538 PublicEditor& editor = trackview.editor ();
540 _press_cursor_ctx.reset();
542 switch (_mouse_state) {
543 case Pressed: // Clicked
545 switch (editor.current_mouse_mode()) {
547 /* no motion occured - simple click */
549 _mouse_changed_selection = true;
556 _mouse_changed_selection = true;
558 if (Keyboard::is_insert_note_event(ev)) {
560 double event_x, event_y;
564 group->canvas_to_item (event_x, event_y);
566 Evoral::Beats beats = get_grid_beats(editor.pixel_to_sample(event_x));
568 /* Shorten the length by 1 tick so that we can add a new note at the next
569 grid snap without it overlapping this one.
571 beats -= Evoral::Beats::tick();
573 create_note_at (editor.pixel_to_sample (event_x), event_y, beats, true);
580 Evoral::Beats beats = get_grid_beats(editor.pixel_to_sample(event_x));
582 /* Shorten the length by 1 tick so that we can add a new note at the next
583 grid snap without it overlapping this one.
585 beats -= Evoral::Beats::tick();
587 create_note_at (editor.pixel_to_sample (event_x), event_y, beats, true);
598 case SelectRectDragging:
600 editor.drags()->end_grab ((GdkEvent *) ev);
602 create_ghost_note (ev->x, ev->y);
610 if (_mouse_changed_selection) {
611 trackview.editor().begin_reversible_selection_op (X_("Mouse Selection Change"));
612 trackview.editor().commit_reversible_selection_op ();
619 MidiRegionView::motion (GdkEventMotion* ev)
621 PublicEditor& editor = trackview.editor ();
623 if (!_ghost_note && editor.current_mouse_mode() == MouseContent &&
624 Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier()) &&
625 _mouse_state != AddDragging) {
627 create_ghost_note (ev->x, ev->y);
629 } else if (_ghost_note && editor.current_mouse_mode() == MouseContent &&
630 Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())) {
632 update_ghost_note (ev->x, ev->y);
634 } else if (_ghost_note && editor.current_mouse_mode() == MouseContent) {
636 remove_ghost_note ();
637 editor.verbose_cursor()->hide ();
639 } else if (_ghost_note && editor.current_mouse_mode() == MouseDraw) {
641 update_ghost_note (ev->x, ev->y);
644 /* any motion immediately hides velocity text that may have been visible */
646 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
647 (*i)->hide_velocity ();
650 switch (_mouse_state) {
653 if (_pressed_button == 1) {
655 MouseMode m = editor.current_mouse_mode();
657 if (m == MouseDraw || (m == MouseContent && Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier()))) {
658 editor.drags()->set (new NoteCreateDrag (dynamic_cast<Editor *> (&editor), group, this), (GdkEvent *) ev);
659 _mouse_state = AddDragging;
660 remove_ghost_note ();
661 editor.verbose_cursor()->hide ();
663 } else if (m == MouseContent) {
664 editor.drags()->set (new MidiRubberbandSelectDrag (dynamic_cast<Editor *> (&editor), this), (GdkEvent *) ev);
665 if (!Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
667 _mouse_changed_selection = true;
669 _mouse_state = SelectRectDragging;
671 } else if (m == MouseRange) {
672 editor.drags()->set (new MidiVerticalSelectDrag (dynamic_cast<Editor *> (&editor), this), (GdkEvent *) ev);
673 _mouse_state = SelectVerticalDragging;
680 case SelectRectDragging:
681 case SelectVerticalDragging:
683 editor.drags()->motion_handler ((GdkEvent *) ev, false);
686 case SelectTouchDragging:
694 /* we may be dragging some non-note object (eg. patch-change, sysex)
697 return editor.drags()->motion_handler ((GdkEvent *) ev, false);
702 MidiRegionView::scroll (GdkEventScroll* ev)
704 if (_selection.empty()) {
708 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
709 /* XXX: bit of a hack; allow PrimaryModifier scroll through so that
710 it still works for zoom.
715 trackview.editor().verbose_cursor()->hide ();
717 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
718 bool together = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
720 if (ev->direction == GDK_SCROLL_UP) {
721 change_velocities (true, fine, false, together);
722 } else if (ev->direction == GDK_SCROLL_DOWN) {
723 change_velocities (false, fine, false, together);
725 /* left, right: we don't use them */
733 MidiRegionView::key_press (GdkEventKey* ev)
735 /* since GTK bindings are generally activated on press, and since
736 detectable auto-repeat is the name of the game and only sends
737 repeated presses, carry out key actions at key press, not release.
740 bool unmodified = Keyboard::no_modifier_keys_pressed (ev);
742 if (unmodified && (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R)) {
743 _mouse_state = SelectTouchDragging;
746 } else if (ev->keyval == GDK_Escape && unmodified) {
750 } else if (ev->keyval == GDK_comma || ev->keyval == GDK_period) {
752 bool start = (ev->keyval == GDK_comma);
753 bool end = (ev->keyval == GDK_period);
754 bool shorter = Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier);
755 bool fine = Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
757 change_note_lengths (fine, shorter, Evoral::Beats(), start, end);
761 } else if ((ev->keyval == GDK_BackSpace || ev->keyval == GDK_Delete) && unmodified) {
763 if (_selection.empty()) {
770 } else if (ev->keyval == GDK_Tab || ev->keyval == GDK_ISO_Left_Tab) {
772 trackview.editor().begin_reversible_selection_op (X_("Select Adjacent Note"));
774 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
775 goto_previous_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
777 goto_next_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
780 trackview.editor().commit_reversible_selection_op();
784 } else if (ev->keyval == GDK_Up) {
786 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
787 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
788 bool together = Keyboard::modifier_state_contains (ev->state, Keyboard::Level4Modifier);
790 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
791 change_velocities (true, fine, allow_smush, together);
793 transpose (true, fine, allow_smush);
797 } else if (ev->keyval == GDK_Down) {
799 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
800 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
801 bool together = Keyboard::modifier_state_contains (ev->state, Keyboard::Level4Modifier);
803 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
804 change_velocities (false, fine, allow_smush, together);
806 transpose (false, fine, allow_smush);
810 } else if (ev->keyval == GDK_Left) {
812 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
813 nudge_notes (false, fine);
816 } else if (ev->keyval == GDK_Right) {
818 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
819 nudge_notes (true, fine);
822 } else if (ev->keyval == GDK_c && unmodified) {
826 } else if (ev->keyval == GDK_v && unmodified) {
835 MidiRegionView::key_release (GdkEventKey* ev)
837 if ((_mouse_state == SelectTouchDragging) && (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R)) {
845 MidiRegionView::channel_edit ()
847 if (_selection.empty()) {
851 /* pick a note somewhat at random (since Selection is a set<>) to
852 * provide the "current" channel for the dialog.
855 uint8_t current_channel = (*_selection.begin())->note()->channel ();
856 MidiChannelDialog channel_dialog (current_channel);
857 int ret = channel_dialog.run ();
860 case Gtk::RESPONSE_OK:
866 uint8_t new_channel = channel_dialog.active_channel ();
868 start_note_diff_command (_("channel edit"));
870 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
871 Selection::iterator next = i;
873 change_note_channel (*i, new_channel);
881 MidiRegionView::velocity_edit ()
883 if (_selection.empty()) {
887 /* pick a note somewhat at random (since Selection is a set<>) to
888 * provide the "current" velocity for the dialog.
891 uint8_t current_velocity = (*_selection.begin())->note()->velocity ();
892 MidiVelocityDialog velocity_dialog (current_velocity);
893 int ret = velocity_dialog.run ();
896 case Gtk::RESPONSE_OK:
902 uint8_t new_velocity = velocity_dialog.velocity ();
904 start_note_diff_command (_("velocity edit"));
906 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
907 Selection::iterator next = i;
909 change_note_velocity (*i, new_velocity, false);
917 MidiRegionView::show_list_editor ()
920 _list_editor = new MidiListEditor (trackview.session(), midi_region(), midi_view()->midi_track());
922 _list_editor->present ();
925 /** Add a note to the model, and the view, at a canvas (click) coordinate.
926 * \param t time in frames relative to the position of the region
927 * \param y vertical position in pixels
928 * \param length duration of the note in beats
929 * \param snap_t true to snap t to the grid, otherwise false.
932 MidiRegionView::create_note_at (framepos_t t, double y, Evoral::Beats length, bool snap_t)
934 if (length < 2 * DBL_EPSILON) {
938 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
939 MidiStreamView* const view = mtv->midi_view();
941 // Start of note in frames relative to region start
943 framecnt_t grid_frames;
944 t = snap_frame_to_grid_underneath (t, grid_frames);
947 const MidiModel::TimeType beat_time = region_frames_to_region_beats(
948 t + _region->start());
950 const double note = view->y_to_note(y);
951 const uint8_t chan = mtv->get_channel_for_add();
952 const uint8_t velocity = get_velocity_for_add(beat_time);
954 const boost::shared_ptr<NoteType> new_note(
955 new NoteType (chan, beat_time, length, (uint8_t)note, velocity));
957 if (_model->contains (new_note)) {
961 view->update_note_range(new_note->note());
963 start_note_diff_command(_("add note"));
966 note_diff_add_note (new_note, true, false);
970 play_midi_note (new_note);
974 MidiRegionView::clear_events (bool with_selection_signal)
976 clear_selection (with_selection_signal);
979 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
980 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
985 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
990 _patch_changes.clear();
992 _optimization_iterator = _events.end();
996 MidiRegionView::display_model(boost::shared_ptr<MidiModel> model)
1000 content_connection.disconnect ();
1001 _model->ContentsChanged.connect (content_connection, invalidator (*this), boost::bind (&MidiRegionView::redisplay_model, this), gui_context());
1002 /* Don't signal as nobody else needs to know until selection has been altered. */
1003 clear_events (false);
1005 if (_enable_display) {
1011 MidiRegionView::start_note_diff_command (string name)
1013 if (!_note_diff_command) {
1014 trackview.editor().begin_reversible_command (name);
1015 _note_diff_command = _model->new_note_diff_command (name);
1020 MidiRegionView::note_diff_add_note (const boost::shared_ptr<NoteType> note, bool selected, bool show_velocity)
1022 if (_note_diff_command) {
1023 _note_diff_command->add (note);
1026 _marked_for_selection.insert(note);
1028 if (show_velocity) {
1029 _marked_for_velocity.insert(note);
1034 MidiRegionView::note_diff_remove_note (NoteBase* ev)
1036 if (_note_diff_command && ev->note()) {
1037 _note_diff_command->remove(ev->note());
1042 MidiRegionView::note_diff_add_change (NoteBase* ev,
1043 MidiModel::NoteDiffCommand::Property property,
1046 if (_note_diff_command) {
1047 _note_diff_command->change (ev->note(), property, val);
1052 MidiRegionView::note_diff_add_change (NoteBase* ev,
1053 MidiModel::NoteDiffCommand::Property property,
1056 if (_note_diff_command) {
1057 _note_diff_command->change (ev->note(), property, val);
1062 MidiRegionView::apply_diff (bool as_subcommand)
1065 bool commit = false;
1067 if (!_note_diff_command) {
1071 if ((add_or_remove = _note_diff_command->adds_or_removes())) {
1072 // Mark all selected notes for selection when model reloads
1073 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1074 _marked_for_selection.insert((*i)->note());
1078 midi_view()->midi_track()->midi_playlist()->region_edited(
1079 _region, _note_diff_command);
1081 if (as_subcommand) {
1082 _model->apply_command_as_subcommand (*trackview.session(), _note_diff_command);
1084 _model->apply_command (*trackview.session(), _note_diff_command);
1088 _note_diff_command = 0;
1090 if (add_or_remove) {
1091 _marked_for_selection.clear();
1094 _marked_for_velocity.clear();
1096 trackview.editor().commit_reversible_command ();
1101 MidiRegionView::abort_command()
1103 delete _note_diff_command;
1104 _note_diff_command = 0;
1109 MidiRegionView::find_canvas_note (boost::shared_ptr<NoteType> note)
1111 if (_optimization_iterator != _events.end()) {
1112 ++_optimization_iterator;
1115 if (_optimization_iterator != _events.end() && (*_optimization_iterator)->note() == note) {
1116 return *_optimization_iterator;
1119 for (_optimization_iterator = _events.begin(); _optimization_iterator != _events.end(); ++_optimization_iterator) {
1120 if ((*_optimization_iterator)->note() == note) {
1121 return *_optimization_iterator;
1128 /** This version finds any canvas note matching the supplied note. */
1130 MidiRegionView::find_canvas_note (NoteType note)
1132 Events::iterator it;
1134 for (it = _events.begin(); it != _events.end(); ++it) {
1135 if (*((*it)->note()) == note) {
1144 MidiRegionView::get_events (Events& e, Evoral::Sequence<Evoral::Beats>::NoteOperator op, uint8_t val, int chan_mask)
1146 MidiModel::Notes notes;
1147 _model->get_notes (notes, op, val, chan_mask);
1149 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1150 NoteBase* cne = find_canvas_note (*n);
1158 MidiRegionView::redisplay_model()
1160 if (_active_notes) {
1161 // Currently recording
1162 const framecnt_t zoom = trackview.editor().get_current_zoom();
1163 if (zoom != _last_display_zoom) {
1164 /* Update resolved canvas notes to reflect changes in zoom without
1165 touching model. Leave active notes (with length 0) alone since
1166 they are being extended. */
1167 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1168 if ((*i)->note()->length() > 0) {
1172 _last_display_zoom = zoom;
1181 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1182 (*i)->invalidate ();
1185 MidiModel::ReadLock lock(_model->read_lock());
1187 MidiModel::Notes& notes (_model->notes());
1188 _optimization_iterator = _events.begin();
1190 bool empty_when_starting = _events.empty();
1192 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1194 boost::shared_ptr<NoteType> note (*n);
1198 if (note_in_region_range (note, visible)) {
1200 if (!empty_when_starting && (cne = find_canvas_note (note)) != 0) {
1213 cne = add_note (note, visible);
1216 set<boost::shared_ptr<NoteType> >::iterator it;
1217 for (it = _pending_note_selection.begin(); it != _pending_note_selection.end(); ++it) {
1218 if (*(*it) == *note) {
1219 add_to_selection (cne);
1225 if (!empty_when_starting && (cne = find_canvas_note (note)) != 0) {
1232 /* remove note items that are no longer valid */
1234 if (!empty_when_starting) {
1235 for (Events::iterator i = _events.begin(); i != _events.end(); ) {
1236 if (!(*i)->valid ()) {
1238 for (vector<GhostRegion*>::iterator j = ghosts.begin(); j != ghosts.end(); ++j) {
1239 MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*j);
1241 gr->remove_note (*i);
1246 i = _events.erase (i);
1254 _patch_changes.clear();
1258 display_patch_changes ();
1260 _marked_for_selection.clear ();
1261 _marked_for_velocity.clear ();
1262 _pending_note_selection.clear ();
1264 /* we may have caused _events to contain things out of order (e.g. if a note
1265 moved earlier or later). we don't generally need them in time order, but
1266 make a note that a sort is required for those cases that require it.
1269 _sort_needed = true;
1273 MidiRegionView::display_patch_changes ()
1275 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1276 uint16_t chn_mask = mtv->midi_track()->get_playback_channel_mask();
1278 for (uint8_t i = 0; i < 16; ++i) {
1279 display_patch_changes_on_channel (i, chn_mask & (1 << i));
1283 /** @param active_channel true to display patch changes fully, false to display
1284 * them `greyed-out' (as on an inactive channel)
1287 MidiRegionView::display_patch_changes_on_channel (uint8_t channel, bool active_channel)
1289 for (MidiModel::PatchChanges::const_iterator i = _model->patch_changes().begin(); i != _model->patch_changes().end(); ++i) {
1291 if ((*i)->channel() != channel) {
1295 const string patch_name = instrument_info().get_patch_name ((*i)->bank(), (*i)->program(), channel);
1296 add_canvas_patch_change (*i, patch_name, active_channel);
1301 MidiRegionView::display_sysexes()
1303 bool have_periodic_system_messages = false;
1304 bool display_periodic_messages = true;
1306 if (!ARDOUR_UI::config()->get_never_display_periodic_midi()) {
1308 for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
1309 const boost::shared_ptr<const Evoral::MIDIEvent<Evoral::Beats> > mev =
1310 boost::static_pointer_cast<const Evoral::MIDIEvent<Evoral::Beats> > (*i);
1313 if (mev->is_spp() || mev->is_mtc_quarter() || mev->is_mtc_full()) {
1314 have_periodic_system_messages = true;
1320 if (have_periodic_system_messages) {
1321 double zoom = trackview.editor().get_current_zoom (); // frames per pixel
1323 /* get an approximate value for the number of samples per video frame */
1325 double video_frame = trackview.session()->frame_rate() * (1.0/30);
1327 /* if we are zoomed out beyond than the cutoff (i.e. more
1328 * frames per pixel than frames per 4 video frames), don't
1329 * show periodic sysex messages.
1332 if (zoom > (video_frame*4)) {
1333 display_periodic_messages = false;
1337 display_periodic_messages = false;
1340 for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
1342 const boost::shared_ptr<const Evoral::MIDIEvent<Evoral::Beats> > mev =
1343 boost::static_pointer_cast<const Evoral::MIDIEvent<Evoral::Beats> > (*i);
1345 Evoral::Beats time = (*i)->time();
1348 if (mev->is_spp() || mev->is_mtc_quarter() || mev->is_mtc_full()) {
1349 if (!display_periodic_messages) {
1357 for (uint32_t b = 0; b < (*i)->size(); ++b) {
1358 str << int((*i)->buffer()[b]);
1359 if (b != (*i)->size() -1) {
1363 string text = str.str();
1365 const double x = trackview.editor().sample_to_pixel(source_beats_to_region_frames(time));
1367 double height = midi_stream_view()->contents_height();
1369 // CAIROCANVAS: no longer passing *i (the sysex event) to the
1370 // SysEx canvas object!!!
1372 boost::shared_ptr<SysEx> sysex = boost::shared_ptr<SysEx>(
1373 new SysEx (*this, _note_group, text, height, x, 1.0));
1375 // Show unless message is beyond the region bounds
1376 if (time - _region->start() >= _region->length() || time < _region->start()) {
1382 _sys_exes.push_back(sysex);
1386 MidiRegionView::~MidiRegionView ()
1388 in_destructor = true;
1390 trackview.editor().verbose_cursor()->hide ();
1392 note_delete_connection.disconnect ();
1394 delete _list_editor;
1396 RegionViewGoingAway (this); /* EMIT_SIGNAL */
1398 if (_active_notes) {
1402 _selection_cleared_connection.disconnect ();
1405 clear_events (false);
1408 delete _note_diff_command;
1409 delete _step_edit_cursor;
1410 delete _temporary_note_group;
1414 MidiRegionView::region_resized (const PropertyChange& what_changed)
1416 RegionView::region_resized(what_changed);
1418 if (what_changed.contains (ARDOUR::Properties::position)) {
1419 _region_relative_time_converter.set_origin_b(_region->position());
1420 _region_relative_time_converter_double.set_origin_b(_region->position());
1421 set_duration(_region->length(), 0);
1422 if (_enable_display) {
1427 if (what_changed.contains (ARDOUR::Properties::start) ||
1428 what_changed.contains (ARDOUR::Properties::position)) {
1429 _source_relative_time_converter.set_origin_b (_region->position() - _region->start());
1434 MidiRegionView::reset_width_dependent_items (double pixel_width)
1436 RegionView::reset_width_dependent_items(pixel_width);
1438 if (_enable_display) {
1442 for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
1443 if ((*x)->canvas_item()->width() >= _pixel_width) {
1450 move_step_edit_cursor (_step_edit_cursor_position);
1451 set_step_edit_cursor_width (_step_edit_cursor_width);
1455 MidiRegionView::set_height (double height)
1457 double old_height = _height;
1458 RegionView::set_height(height);
1460 apply_note_range (midi_stream_view()->lowest_note(),
1461 midi_stream_view()->highest_note(),
1462 height != old_height);
1465 name_text->raise_to_top();
1468 for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
1469 (*x)->set_height (midi_stream_view()->contents_height());
1472 if (_step_edit_cursor) {
1473 _step_edit_cursor->set_y1 (midi_stream_view()->contents_height());
1478 /** Apply the current note range from the stream view
1479 * by repositioning/hiding notes as necessary
1482 MidiRegionView::apply_note_range (uint8_t min, uint8_t max, bool force)
1484 if (!_enable_display) {
1488 if (!force && _current_range_min == min && _current_range_max == max) {
1492 _current_range_min = min;
1493 _current_range_max = max;
1495 for (Events::const_iterator i = _events.begin(); i != _events.end(); ++i) {
1496 NoteBase* event = *i;
1497 boost::shared_ptr<NoteType> note (event->note());
1499 if (note->note() < _current_range_min ||
1500 note->note() > _current_range_max) {
1506 if (Note* cnote = dynamic_cast<Note*>(event)) {
1508 const double y0 = 1. + floor (midi_stream_view()->note_to_y(note->note()));
1509 const double y1 = y0 + std::max(1., floor(midi_stream_view()->note_height()) - 1.);
1511 if (y0 < 0 || y1 >= _height) {
1512 /* During DnD, the region uses the 'old/current'
1513 * midi_stream_view()'s range and its position/height calculation.
1515 * Ideally DnD would decouple the midi_stream_view() for the
1516 * region(s) being dragged and set it to the target's range
1517 * (or in case of the drop-zone, FullRange).
1518 * but I don't see how this can be done without major rework.
1520 * For now, just prevent visual bleeding of events in case
1521 * the target-track is smaller.
1529 } else if (Hit* chit = dynamic_cast<Hit*>(event)) {
1536 MidiRegionView::add_ghost (TimeAxisView& tv)
1538 double unit_position = _region->position () / samples_per_pixel;
1539 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(&tv);
1540 MidiGhostRegion* ghost;
1542 if (mtv && mtv->midi_view()) {
1543 /* if ghost is inserted into midi track, use a dedicated midi ghost canvas group
1544 to allow having midi notes on top of note lines and waveforms.
1546 ghost = new MidiGhostRegion (*mtv->midi_view(), trackview, unit_position);
1548 ghost = new MidiGhostRegion (tv, trackview, unit_position);
1551 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1552 ghost->add_note(*i);
1555 ghost->set_height ();
1556 ghost->set_duration (_region->length() / samples_per_pixel);
1557 ghosts.push_back (ghost);
1559 GhostRegion::CatchDeletion.connect (*this, invalidator (*this), boost::bind (&RegionView::remove_ghost, this, _1), gui_context());
1565 /** Begin tracking note state for successive calls to add_event
1568 MidiRegionView::begin_write()
1570 if (_active_notes) {
1571 delete[] _active_notes;
1573 _active_notes = new Note*[128];
1574 for (unsigned i = 0; i < 128; ++i) {
1575 _active_notes[i] = 0;
1580 /** Destroy note state for add_event
1583 MidiRegionView::end_write()
1585 delete[] _active_notes;
1587 _marked_for_selection.clear();
1588 _marked_for_velocity.clear();
1592 /** Resolve an active MIDI note (while recording).
1595 MidiRegionView::resolve_note(uint8_t note, Evoral::Beats end_time)
1597 if (midi_view()->note_mode() != Sustained) {
1601 if (_active_notes && _active_notes[note]) {
1602 /* Set note length so update_note() works. Note this is a local note
1603 for recording, not from a model, so we can safely mess with it. */
1604 _active_notes[note]->note()->set_length(
1605 end_time - _active_notes[note]->note()->time());
1607 /* End time is relative to the region being recorded. */
1608 const framepos_t end_time_frames = region_beats_to_region_frames(end_time);
1610 _active_notes[note]->set_x1 (trackview.editor().sample_to_pixel(end_time_frames));
1611 _active_notes[note]->set_outline_all ();
1612 _active_notes[note] = 0;
1617 /** Extend active notes to rightmost edge of region (if length is changed)
1620 MidiRegionView::extend_active_notes()
1622 if (!_active_notes) {
1626 for (unsigned i = 0; i < 128; ++i) {
1627 if (_active_notes[i]) {
1628 _active_notes[i]->set_x1(
1629 trackview.editor().sample_to_pixel(_region->length()));
1635 MidiRegionView::play_midi_note(boost::shared_ptr<NoteType> note)
1637 if (_no_sound_notes || !ARDOUR_UI::config()->get_sound_midi_notes()) {
1641 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1643 if (!route_ui || !route_ui->midi_track()) {
1647 NotePlayer* np = new NotePlayer (route_ui->midi_track ());
1651 /* NotePlayer deletes itself */
1655 MidiRegionView::start_playing_midi_note(boost::shared_ptr<NoteType> note)
1657 const std::vector< boost::shared_ptr<NoteType> > notes(1, note);
1658 start_playing_midi_chord(notes);
1662 MidiRegionView::start_playing_midi_chord (vector<boost::shared_ptr<NoteType> > notes)
1664 if (_no_sound_notes || !ARDOUR_UI::config()->get_sound_midi_notes()) {
1668 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1670 if (!route_ui || !route_ui->midi_track()) {
1674 NotePlayer* player = new NotePlayer (route_ui->midi_track());
1676 for (vector<boost::shared_ptr<NoteType> >::iterator n = notes.begin(); n != notes.end(); ++n) {
1685 MidiRegionView::note_in_region_range (const boost::shared_ptr<NoteType> note, bool& visible) const
1687 /* This is imprecise due to all the conversion conversion involved, so only
1688 hide notes if they seem to start more than one tick before the start. */
1689 const framecnt_t tick_frames = Evoral::Beats::tick().to_ticks(trackview.session()->frame_rate());
1690 const framepos_t note_start_frames = source_beats_to_region_frames (note->time());
1691 const bool outside = ((note_start_frames <= -tick_frames) ||
1692 (note_start_frames >= _region->length()));
1694 visible = (note->note() >= midi_stream_view()->lowest_note()) &&
1695 (note->note() <= midi_stream_view()->highest_note());
1701 MidiRegionView::update_note (NoteBase* note, bool update_ghost_regions)
1705 if ((sus = dynamic_cast<Note*>(note))) {
1706 update_sustained(sus, update_ghost_regions);
1707 } else if ((hit = dynamic_cast<Hit*>(note))) {
1708 update_hit(hit, update_ghost_regions);
1712 /** Update a canvas note's size from its model note.
1713 * @param ev Canvas note to update.
1714 * @param update_ghost_regions true to update the note in any ghost regions that we have, otherwise false.
1717 MidiRegionView::update_sustained (Note* ev, bool update_ghost_regions)
1719 boost::shared_ptr<NoteType> note = ev->note();
1720 const double x = trackview.editor().sample_to_pixel (source_beats_to_region_frames (note->time()));
1721 const double y0 = 1 + floor(midi_stream_view()->note_to_y(note->note()));
1726 /* trim note display to not overlap the end of its region */
1728 if (note->length() > 0) {
1729 const framepos_t note_end_frames = min (source_beats_to_region_frames (note->end_time()), _region->length());
1730 ev->set_x1 (trackview.editor().sample_to_pixel (note_end_frames));
1732 ev->set_x1 (trackview.editor().sample_to_pixel (_region->length()));
1735 ev->set_y1 (y0 + std::max(1., floor(midi_stream_view()->note_height()) - 1));
1737 if (!note->length()) {
1738 if (_active_notes && note->note() < 128) {
1739 Note* const old_rect = _active_notes[note->note()];
1741 /* There is an active note on this key, so we have a stuck
1742 note. Finish the old rectangle here. */
1743 old_rect->set_x1 (x);
1744 old_rect->set_outline_all ();
1746 _active_notes[note->note()] = ev;
1748 /* outline all but right edge */
1749 ev->set_outline_what (ArdourCanvas::Rectangle::What (
1750 ArdourCanvas::Rectangle::TOP|
1751 ArdourCanvas::Rectangle::LEFT|
1752 ArdourCanvas::Rectangle::BOTTOM));
1754 /* outline all edges */
1755 ev->set_outline_all ();
1758 // Update color in case velocity has changed
1759 ev->set_fill_color(ev->base_color());
1760 ev->set_outline_color(ev->calculate_outline(ev->base_color(), ev->selected()));
1762 if (update_ghost_regions) {
1763 for (std::vector<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
1764 MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*i);
1766 gr->update_note (ev);
1773 MidiRegionView::update_hit (Hit* ev, bool update_ghost_regions)
1775 boost::shared_ptr<NoteType> note = ev->note();
1777 const framepos_t note_start_frames = source_beats_to_region_frames(note->time());
1778 const double x = trackview.editor().sample_to_pixel(note_start_frames);
1779 const double diamond_size = std::max(1., floor(midi_stream_view()->note_height()) - 2.);
1780 const double y = 1.5 + floor(midi_stream_view()->note_to_y(note->note())) + diamond_size * .5;
1782 // see DnD note in MidiRegionView::apply_note_range() above
1783 if (y <= 0 || y >= _height) {
1789 ev->set_position (ArdourCanvas::Duple (x, y));
1790 ev->set_height (diamond_size);
1792 // Update color in case velocity has changed
1793 ev->set_fill_color(ev->base_color());
1794 ev->set_outline_color(ev->calculate_outline(ev->base_color(), ev->selected()));
1796 if (update_ghost_regions) {
1797 for (std::vector<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
1798 MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*i);
1800 gr->update_note (ev);
1806 /** Add a MIDI note to the view (with length).
1808 * If in sustained mode, notes with length 0 will be considered active
1809 * notes, and resolve_note should be called when the corresponding note off
1810 * event arrives, to properly display the note.
1813 MidiRegionView::add_note(const boost::shared_ptr<NoteType> note, bool visible)
1815 NoteBase* event = 0;
1817 if (midi_view()->note_mode() == Sustained) {
1819 Note* ev_rect = new Note (*this, _note_group, note);
1821 update_sustained (ev_rect);
1825 } else if (midi_view()->note_mode() == Percussive) {
1827 const double diamond_size = std::max(1., floor(midi_stream_view()->note_height()) - 2.);
1829 Hit* ev_diamond = new Hit (*this, _note_group, diamond_size, note);
1831 update_hit (ev_diamond);
1840 MidiGhostRegion* gr;
1842 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
1843 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
1844 gr->add_note(event);
1848 if (_marked_for_selection.find(note) != _marked_for_selection.end()) {
1849 note_selected(event, true);
1852 if (_marked_for_velocity.find(note) != _marked_for_velocity.end()) {
1853 event->show_velocity();
1856 event->on_channel_selection_change (get_selected_channels());
1857 _events.push_back(event);
1866 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1867 MidiStreamView* const view = mtv->midi_view();
1869 view->update_note_range (note->note());
1874 MidiRegionView::step_add_note (uint8_t channel, uint8_t number, uint8_t velocity,
1875 Evoral::Beats pos, Evoral::Beats len)
1877 boost::shared_ptr<NoteType> new_note (new NoteType (channel, pos, len, number, velocity));
1879 /* potentially extend region to hold new note */
1881 framepos_t end_frame = source_beats_to_absolute_frames (new_note->end_time());
1882 framepos_t region_end = _region->last_frame();
1884 if (end_frame > region_end) {
1885 _region->set_length (end_frame - _region->position());
1888 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1889 MidiStreamView* const view = mtv->midi_view();
1891 view->update_note_range(new_note->note());
1893 _marked_for_selection.clear ();
1895 start_note_diff_command (_("step add"));
1898 note_diff_add_note (new_note, true, false);
1902 // last_step_edit_note = new_note;
1906 MidiRegionView::step_sustain (Evoral::Beats beats)
1908 change_note_lengths (false, false, beats, false, true);
1911 /** Add a new patch change flag to the canvas.
1912 * @param patch the patch change to add
1913 * @param the text to display in the flag
1914 * @param active_channel true to display the flag as on an active channel, false to grey it out for an inactive channel.
1917 MidiRegionView::add_canvas_patch_change (MidiModel::PatchChangePtr patch, const string& displaytext, bool /*active_channel*/)
1919 framecnt_t region_frames = source_beats_to_region_frames (patch->time());
1920 const double x = trackview.editor().sample_to_pixel (region_frames);
1922 double const height = midi_stream_view()->contents_height();
1924 // CAIROCANVAS: active_channel info removed from PatcChange constructor
1925 // so we need to do something more sophisticated to keep its color
1926 // appearance (MidiPatchChangeFill/MidiPatchChangeInactiveChannelFill)
1929 boost::shared_ptr<PatchChange> patch_change = boost::shared_ptr<PatchChange>(
1930 new PatchChange(*this, group,
1937 if (patch_change->item().width() < _pixel_width) {
1938 // Show unless patch change is beyond the region bounds
1939 if (region_frames < 0 || region_frames >= _region->length()) {
1940 patch_change->hide();
1942 patch_change->show();
1945 patch_change->hide ();
1948 _patch_changes.push_back (patch_change);
1951 MIDI::Name::PatchPrimaryKey
1952 MidiRegionView::patch_change_to_patch_key (MidiModel::PatchChangePtr p)
1954 return MIDI::Name::PatchPrimaryKey (p->program(), p->bank());
1957 /// Return true iff @p pc applies to the given time on the given channel.
1959 patch_applies (const ARDOUR::MidiModel::constPatchChangePtr pc, Evoral::Beats time, uint8_t channel)
1961 return pc->time() <= time && pc->channel() == channel;
1965 MidiRegionView::get_patch_key_at (Evoral::Beats time, uint8_t channel, MIDI::Name::PatchPrimaryKey& key) const
1967 // The earliest event not before time
1968 MidiModel::PatchChanges::iterator i = _model->patch_change_lower_bound (time);
1970 // Go backwards until we find the latest PC for this channel, or the start
1971 while (i != _model->patch_changes().begin() &&
1972 (i == _model->patch_changes().end() ||
1973 !patch_applies(*i, time, channel))) {
1977 if (i != _model->patch_changes().end() && patch_applies(*i, time, channel)) {
1978 key.set_bank((*i)->bank());
1979 key.set_program((*i)->program ());
1987 MidiRegionView::change_patch_change (PatchChange& pc, const MIDI::Name::PatchPrimaryKey& new_patch)
1989 string name = _("alter patch change");
1990 trackview.editor().begin_reversible_command (name);
1991 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (name);
1993 if (pc.patch()->program() != new_patch.program()) {
1994 c->change_program (pc.patch (), new_patch.program());
1997 int const new_bank = new_patch.bank();
1998 if (pc.patch()->bank() != new_bank) {
1999 c->change_bank (pc.patch (), new_bank);
2002 _model->apply_command (*trackview.session(), c);
2003 trackview.editor().commit_reversible_command ();
2005 _patch_changes.clear ();
2006 display_patch_changes ();
2010 MidiRegionView::change_patch_change (MidiModel::PatchChangePtr old_change, const Evoral::PatchChange<Evoral::Beats> & new_change)
2012 string name = _("alter patch change");
2013 trackview.editor().begin_reversible_command (name);
2014 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (name);
2016 if (old_change->time() != new_change.time()) {
2017 c->change_time (old_change, new_change.time());
2020 if (old_change->channel() != new_change.channel()) {
2021 c->change_channel (old_change, new_change.channel());
2024 if (old_change->program() != new_change.program()) {
2025 c->change_program (old_change, new_change.program());
2028 if (old_change->bank() != new_change.bank()) {
2029 c->change_bank (old_change, new_change.bank());
2032 _model->apply_command (*trackview.session(), c);
2033 trackview.editor().commit_reversible_command ();
2035 _patch_changes.clear ();
2036 display_patch_changes ();
2039 /** Add a patch change to the region.
2040 * @param t Time in frames relative to region position
2041 * @param patch Patch to add; time and channel are ignored (time is converted from t, and channel comes from
2042 * MidiTimeAxisView::get_channel_for_add())
2045 MidiRegionView::add_patch_change (framecnt_t t, Evoral::PatchChange<Evoral::Beats> const & patch)
2047 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
2048 string name = _("add patch change");
2050 trackview.editor().begin_reversible_command (name);
2051 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (name);
2052 c->add (MidiModel::PatchChangePtr (
2053 new Evoral::PatchChange<Evoral::Beats> (
2054 absolute_frames_to_source_beats (_region->position() + t),
2055 mtv->get_channel_for_add(), patch.program(), patch.bank()
2060 _model->apply_command (*trackview.session(), c);
2061 trackview.editor().commit_reversible_command ();
2063 _patch_changes.clear ();
2064 display_patch_changes ();
2068 MidiRegionView::move_patch_change (PatchChange& pc, Evoral::Beats t)
2070 trackview.editor().begin_reversible_command (_("move patch change"));
2071 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("move patch change"));
2072 c->change_time (pc.patch (), t);
2073 _model->apply_command (*trackview.session(), c);
2074 trackview.editor().commit_reversible_command ();
2076 _patch_changes.clear ();
2077 display_patch_changes ();
2081 MidiRegionView::delete_patch_change (PatchChange* pc)
2083 trackview.editor().begin_reversible_command (_("delete patch change"));
2084 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("delete patch change"));
2085 c->remove (pc->patch ());
2086 _model->apply_command (*trackview.session(), c);
2087 trackview.editor().commit_reversible_command ();
2089 _patch_changes.clear ();
2090 display_patch_changes ();
2094 MidiRegionView::step_patch (PatchChange& patch, bool bank, int delta)
2096 MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key(patch.patch());
2098 key.set_bank(key.bank() + delta);
2100 key.set_program(key.program() + delta);
2102 change_patch_change(patch, key);
2106 MidiRegionView::maybe_remove_deleted_note_from_selection (NoteBase* cne)
2108 if (_selection.empty()) {
2112 _selection.erase (cne);
2116 MidiRegionView::delete_selection()
2118 if (_selection.empty()) {
2122 start_note_diff_command (_("delete selection"));
2124 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2125 if ((*i)->selected()) {
2126 _note_diff_command->remove((*i)->note());
2136 MidiRegionView::delete_note (boost::shared_ptr<NoteType> n)
2138 start_note_diff_command (_("delete note"));
2139 _note_diff_command->remove (n);
2142 trackview.editor().verbose_cursor()->hide ();
2146 MidiRegionView::clear_selection_except (NoteBase* ev, bool signal)
2148 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2150 Selection::iterator tmp = i;
2153 (*i)->set_selected (false);
2154 (*i)->hide_velocity ();
2155 _selection.erase (i);
2163 if (!ev && _entered) {
2164 // Clearing selection entirely, ungrab keyboard
2165 Keyboard::magic_widget_drop_focus();
2166 _grabbed_keyboard = false;
2169 /* this does not change the status of this regionview w.r.t the editor
2174 SelectionCleared (this); /* EMIT SIGNAL */
2179 MidiRegionView::unique_select(NoteBase* ev)
2181 const bool selection_was_empty = _selection.empty();
2183 clear_selection_except (ev);
2185 /* don't bother with checking to see if we should remove this
2186 regionview from the editor selection, since we're about to add
2187 another note, and thus put/keep this regionview in the editor
2191 if (!ev->selected()) {
2192 add_to_selection (ev);
2193 if (selection_was_empty && _entered) {
2194 // Grab keyboard for moving notes with arrow keys
2195 Keyboard::magic_widget_grab_focus();
2196 _grabbed_keyboard = true;
2202 MidiRegionView::select_all_notes ()
2206 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2207 add_to_selection (*i);
2212 MidiRegionView::select_range (framepos_t start, framepos_t end)
2216 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2217 framepos_t t = source_beats_to_absolute_frames((*i)->note()->time());
2218 if (t >= start && t <= end) {
2219 add_to_selection (*i);
2225 MidiRegionView::invert_selection ()
2227 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2228 if ((*i)->selected()) {
2229 remove_from_selection(*i);
2231 add_to_selection (*i);
2236 /** Used for selection undo/redo.
2237 The requested notes most likely won't exist in the view until the next model redisplay.
2240 MidiRegionView::select_notes (list<boost::shared_ptr<NoteType> > notes)
2243 list<boost::shared_ptr<NoteType> >::iterator n;
2245 for (n = notes.begin(); n != notes.end(); ++n) {
2246 if ((cne = find_canvas_note(*(*n))) != 0) {
2247 add_to_selection (cne);
2249 _pending_note_selection.insert(*n);
2255 MidiRegionView::select_matching_notes (uint8_t notenum, uint16_t channel_mask, bool add, bool extend)
2257 bool have_selection = !_selection.empty();
2258 uint8_t low_note = 127;
2259 uint8_t high_note = 0;
2260 MidiModel::Notes& notes (_model->notes());
2261 _optimization_iterator = _events.begin();
2263 if (extend && !have_selection) {
2267 /* scan existing selection to get note range */
2269 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2270 if ((*i)->note()->note() < low_note) {
2271 low_note = (*i)->note()->note();
2273 if ((*i)->note()->note() > high_note) {
2274 high_note = (*i)->note()->note();
2281 if (!extend && (low_note == high_note) && (high_note == notenum)) {
2282 /* only note previously selected is the one we are
2283 * reselecting. treat this as cancelling the selection.
2290 low_note = min (low_note, notenum);
2291 high_note = max (high_note, notenum);
2294 _no_sound_notes = true;
2296 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
2298 boost::shared_ptr<NoteType> note (*n);
2300 bool select = false;
2302 if (((1 << note->channel()) & channel_mask) != 0) {
2304 if ((note->note() >= low_note && note->note() <= high_note)) {
2307 } else if (note->note() == notenum) {
2313 if ((cne = find_canvas_note (note)) != 0) {
2314 // extend is false because we've taken care of it,
2315 // since it extends by time range, not pitch.
2316 note_selected (cne, add, false);
2320 add = true; // we need to add all remaining matching notes, even if the passed in value was false (for "set")
2324 _no_sound_notes = false;
2328 MidiRegionView::toggle_matching_notes (uint8_t notenum, uint16_t channel_mask)
2330 MidiModel::Notes& notes (_model->notes());
2331 _optimization_iterator = _events.begin();
2333 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
2335 boost::shared_ptr<NoteType> note (*n);
2338 if (note->note() == notenum && (((0x0001 << note->channel()) & channel_mask) != 0)) {
2339 if ((cne = find_canvas_note (note)) != 0) {
2340 if (cne->selected()) {
2341 note_deselected (cne);
2343 note_selected (cne, true, false);
2351 MidiRegionView::note_selected (NoteBase* ev, bool add, bool extend)
2354 clear_selection_except (ev);
2355 if (!_selection.empty()) {
2356 PublicEditor& editor (trackview.editor());
2357 editor.get_selection().add (this);
2363 if (!ev->selected()) {
2364 add_to_selection (ev);
2368 /* find end of latest note selected, select all between that and the start of "ev" */
2370 Evoral::Beats earliest = Evoral::MaxBeats;
2371 Evoral::Beats latest = Evoral::Beats();
2373 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2374 if ((*i)->note()->end_time() > latest) {
2375 latest = (*i)->note()->end_time();
2377 if ((*i)->note()->time() < earliest) {
2378 earliest = (*i)->note()->time();
2382 if (ev->note()->end_time() > latest) {
2383 latest = ev->note()->end_time();
2386 if (ev->note()->time() < earliest) {
2387 earliest = ev->note()->time();
2390 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2392 /* find notes entirely within OR spanning the earliest..latest range */
2394 if (((*i)->note()->time() >= earliest && (*i)->note()->end_time() <= latest) ||
2395 ((*i)->note()->time() <= earliest && (*i)->note()->end_time() >= latest)) {
2396 add_to_selection (*i);
2404 MidiRegionView::note_deselected(NoteBase* ev)
2406 remove_from_selection (ev);
2410 MidiRegionView::update_drag_selection(framepos_t start, framepos_t end, double gy0, double gy1, bool extend)
2412 PublicEditor& editor = trackview.editor();
2414 // Convert to local coordinates
2415 const framepos_t p = _region->position();
2416 const double y = midi_view()->y_position();
2417 const double x0 = editor.sample_to_pixel(max((framepos_t)0, start - p));
2418 const double x1 = editor.sample_to_pixel(max((framepos_t)0, end - p));
2419 const double y0 = max(0.0, gy0 - y);
2420 const double y1 = max(0.0, gy1 - y);
2422 // TODO: Make this faster by storing the last updated selection rect, and only
2423 // adjusting things that are in the area that appears/disappeared.
2424 // We probably need a tree to be able to find events in O(log(n)) time.
2426 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2427 if ((*i)->x0() < x1 && (*i)->x1() > x0 && (*i)->y0() < y1 && (*i)->y1() > y0) {
2428 // Rectangles intersect
2429 if (!(*i)->selected()) {
2430 add_to_selection (*i);
2432 } else if ((*i)->selected() && !extend) {
2433 // Rectangles do not intersect
2434 remove_from_selection (*i);
2438 typedef RouteTimeAxisView::AutomationTracks ATracks;
2439 typedef std::list<Selectable*> Selectables;
2441 /* Add control points to selection. */
2442 const ATracks& atracks = midi_view()->automation_tracks();
2443 Selectables selectables;
2444 editor.get_selection().clear_points();
2445 for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) {
2446 a->second->get_selectables(start, end, gy0, gy1, selectables);
2447 for (Selectables::const_iterator s = selectables.begin(); s != selectables.end(); ++s) {
2448 ControlPoint* cp = dynamic_cast<ControlPoint*>(*s);
2450 editor.get_selection().add(cp);
2453 a->second->set_selected_points(editor.get_selection().points);
2458 MidiRegionView::update_vertical_drag_selection (double y1, double y2, bool extend)
2464 // TODO: Make this faster by storing the last updated selection rect, and only
2465 // adjusting things that are in the area that appears/disappeared.
2466 // We probably need a tree to be able to find events in O(log(n)) time.
2468 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2469 if (((*i)->y1() >= y1 && (*i)->y1() <= y2)) {
2470 // within y- (note-) range
2471 if (!(*i)->selected()) {
2472 add_to_selection (*i);
2474 } else if ((*i)->selected() && !extend) {
2475 remove_from_selection (*i);
2481 MidiRegionView::remove_from_selection (NoteBase* ev)
2483 Selection::iterator i = _selection.find (ev);
2485 if (i != _selection.end()) {
2486 _selection.erase (i);
2487 if (_selection.empty() && _grabbed_keyboard) {
2489 Keyboard::magic_widget_drop_focus();
2490 _grabbed_keyboard = false;
2494 ev->set_selected (false);
2495 ev->hide_velocity ();
2497 if (_selection.empty()) {
2498 PublicEditor& editor (trackview.editor());
2499 editor.get_selection().remove (this);
2504 MidiRegionView::add_to_selection (NoteBase* ev)
2506 const bool selection_was_empty = _selection.empty();
2508 if (_selection.insert (ev).second) {
2509 ev->set_selected (true);
2510 start_playing_midi_note ((ev)->note());
2511 if (selection_was_empty && _entered) {
2512 // Grab keyboard for moving notes with arrow keys
2513 Keyboard::magic_widget_grab_focus();
2514 _grabbed_keyboard = true;
2518 if (selection_was_empty) {
2519 PublicEditor& editor (trackview.editor());
2520 editor.get_selection().add (this);
2525 MidiRegionView::move_selection(double dx, double dy, double cumulative_dy)
2527 typedef vector<boost::shared_ptr<NoteType> > PossibleChord;
2528 PossibleChord to_play;
2529 Evoral::Beats earliest = Evoral::MaxBeats;
2531 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2532 if ((*i)->note()->time() < earliest) {
2533 earliest = (*i)->note()->time();
2537 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2538 if ((*i)->note()->time() == earliest) {
2539 to_play.push_back ((*i)->note());
2541 (*i)->move_event(dx, dy);
2544 if (dy && !_selection.empty() && !_no_sound_notes && ARDOUR_UI::config()->get_sound_midi_notes()) {
2546 if (to_play.size() > 1) {
2548 PossibleChord shifted;
2550 for (PossibleChord::iterator n = to_play.begin(); n != to_play.end(); ++n) {
2551 boost::shared_ptr<NoteType> moved_note (new NoteType (**n));
2552 moved_note->set_note (moved_note->note() + cumulative_dy);
2553 shifted.push_back (moved_note);
2556 start_playing_midi_chord (shifted);
2558 } else if (!to_play.empty()) {
2560 boost::shared_ptr<NoteType> moved_note (new NoteType (*to_play.front()));
2561 moved_note->set_note (moved_note->note() + cumulative_dy);
2562 start_playing_midi_note (moved_note);
2568 MidiRegionView::note_dropped(NoteBase *, frameoffset_t dt, int8_t dnote)
2570 uint8_t lowest_note_in_selection = 127;
2571 uint8_t highest_note_in_selection = 0;
2572 uint8_t highest_note_difference = 0;
2574 // find highest and lowest notes first
2576 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2577 uint8_t pitch = (*i)->note()->note();
2578 lowest_note_in_selection = std::min(lowest_note_in_selection, pitch);
2579 highest_note_in_selection = std::max(highest_note_in_selection, pitch);
2583 cerr << "dnote: " << (int) dnote << endl;
2584 cerr << "lowest note (streamview): " << int(midi_stream_view()->lowest_note())
2585 << " highest note (streamview): " << int(midi_stream_view()->highest_note()) << endl;
2586 cerr << "lowest note (selection): " << int(lowest_note_in_selection) << " highest note(selection): "
2587 << int(highest_note_in_selection) << endl;
2588 cerr << "selection size: " << _selection.size() << endl;
2589 cerr << "Highest note in selection: " << (int) highest_note_in_selection << endl;
2592 // Make sure the note pitch does not exceed the MIDI standard range
2593 if (highest_note_in_selection + dnote > 127) {
2594 highest_note_difference = highest_note_in_selection - 127;
2597 start_note_diff_command (_("move notes"));
2599 for (Selection::iterator i = _selection.begin(); i != _selection.end() ; ++i) {
2601 framepos_t new_frames = source_beats_to_absolute_frames ((*i)->note()->time()) + dt;
2602 Evoral::Beats new_time = absolute_frames_to_source_beats (new_frames);
2608 note_diff_add_change (*i, MidiModel::NoteDiffCommand::StartTime, new_time);
2610 uint8_t original_pitch = (*i)->note()->note();
2611 uint8_t new_pitch = original_pitch + dnote - highest_note_difference;
2613 // keep notes in standard midi range
2614 clamp_to_0_127(new_pitch);
2616 lowest_note_in_selection = std::min(lowest_note_in_selection, new_pitch);
2617 highest_note_in_selection = std::max(highest_note_in_selection, new_pitch);
2619 note_diff_add_change (*i, MidiModel::NoteDiffCommand::NoteNumber, new_pitch);
2624 // care about notes being moved beyond the upper/lower bounds on the canvas
2625 if (lowest_note_in_selection < midi_stream_view()->lowest_note() ||
2626 highest_note_in_selection > midi_stream_view()->highest_note()) {
2627 midi_stream_view()->set_note_range(MidiStreamView::ContentsRange);
2631 /** @param x Pixel relative to the region position.
2632 * @param ensure_snap defaults to false. true = snap always, ignoring snap mode and magnetic snap.
2633 * Used for inverting the snap logic with key modifiers and snap delta calculation.
2634 * @return Snapped frame relative to the region position.
2637 MidiRegionView::snap_pixel_to_sample(double x, bool ensure_snap)
2639 PublicEditor& editor (trackview.editor());
2640 return snap_frame_to_frame (editor.pixel_to_sample (x), ensure_snap);
2643 /** @param x Pixel relative to the region position.
2644 * @param ensure_snap defaults to false. true = ignore magnetic snap and snap mode (used for snap delta calculation).
2645 * @return Snapped pixel relative to the region position.
2648 MidiRegionView::snap_to_pixel(double x, bool ensure_snap)
2650 return (double) trackview.editor().sample_to_pixel(snap_pixel_to_sample(x, ensure_snap));
2654 MidiRegionView::get_position_pixels()
2656 framepos_t region_frame = get_position();
2657 return trackview.editor().sample_to_pixel(region_frame);
2661 MidiRegionView::get_end_position_pixels()
2663 framepos_t frame = get_position() + get_duration ();
2664 return trackview.editor().sample_to_pixel(frame);
2668 MidiRegionView::source_beats_to_absolute_frames(Evoral::Beats beats) const
2670 /* the time converter will return the frame corresponding to `beats'
2671 relative to the start of the source. The start of the source
2672 is an implied position given by region->position - region->start
2674 const framepos_t source_start = _region->position() - _region->start();
2675 return source_start + _source_relative_time_converter.to (beats);
2679 MidiRegionView::absolute_frames_to_source_beats(framepos_t frames) const
2681 /* the `frames' argument needs to be converted into a frame count
2682 relative to the start of the source before being passed in to the
2685 const framepos_t source_start = _region->position() - _region->start();
2686 return _source_relative_time_converter.from (frames - source_start);
2690 MidiRegionView::region_beats_to_region_frames(Evoral::Beats beats) const
2692 return _region_relative_time_converter.to(beats);
2696 MidiRegionView::region_frames_to_region_beats(framepos_t frames) const
2698 return _region_relative_time_converter.from(frames);
2702 MidiRegionView::region_frames_to_region_beats_double (framepos_t frames) const
2704 return _region_relative_time_converter_double.from(frames);
2708 MidiRegionView::begin_resizing (bool /*at_front*/)
2710 _resize_data.clear();
2712 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2713 Note *note = dynamic_cast<Note*> (*i);
2715 // only insert CanvasNotes into the map
2717 NoteResizeData *resize_data = new NoteResizeData();
2718 resize_data->note = note;
2720 // create a new SimpleRect from the note which will be the resize preview
2721 ArdourCanvas::Rectangle *resize_rect = new ArdourCanvas::Rectangle (_note_group,
2722 ArdourCanvas::Rect (note->x0(), note->y0(), note->x0(), note->y1()));
2724 // calculate the colors: get the color settings
2725 uint32_t fill_color = UINT_RGBA_CHANGE_A(
2726 ARDOUR_UI::config()->color ("midi note selected"),
2729 // make the resize preview notes more transparent and bright
2730 fill_color = UINT_INTERPOLATE(fill_color, 0xFFFFFF40, 0.5);
2732 // calculate color based on note velocity
2733 resize_rect->set_fill_color (UINT_INTERPOLATE(
2734 NoteBase::meter_style_fill_color(note->note()->velocity(), note->selected()),
2738 resize_rect->set_outline_color (NoteBase::calculate_outline (
2739 ARDOUR_UI::config()->color ("midi note selected")));
2741 resize_data->resize_rect = resize_rect;
2742 _resize_data.push_back(resize_data);
2747 /** Update resizing notes while user drags.
2748 * @param primary `primary' note for the drag; ie the one that is used as the reference in non-relative mode.
2749 * @param at_front which end of the note (true == note on, false == note off)
2750 * @param delta_x change in mouse position since the start of the drag
2751 * @param relative true if relative resizing is taking place, false if absolute resizing. This only makes
2752 * a difference when multiple notes are being resized; in relative mode, each note's length is changed by the
2753 * amount of the drag. In non-relative mode, all selected notes are set to have the same start or end point
2754 * as the \a primary note.
2755 * @param snap_delta snap offset of the primary note in pixels. used in SnapRelative SnapDelta mode.
2756 * @param with_snap true if snap is to be used to determine the position, false if no snap is to be used.
2759 MidiRegionView::update_resizing (NoteBase* primary, bool at_front, double delta_x, bool relative, double snap_delta, bool with_snap)
2761 bool cursor_set = false;
2763 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2764 ArdourCanvas::Rectangle* resize_rect = (*i)->resize_rect;
2765 Note* canvas_note = (*i)->note;
2770 current_x = canvas_note->x0() + delta_x + snap_delta;
2772 current_x = primary->x0() + delta_x + snap_delta;
2776 current_x = canvas_note->x1() + delta_x + snap_delta;
2778 current_x = primary->x1() + delta_x + snap_delta;
2782 if (current_x < 0) {
2783 // This works even with snapping because RegionView::snap_frame_to_frame()
2784 // snaps forward if the snapped sample is before the beginning of the region
2787 if (current_x > trackview.editor().sample_to_pixel(_region->length())) {
2788 current_x = trackview.editor().sample_to_pixel(_region->length());
2793 resize_rect->set_x0 (snap_to_pixel(current_x) - snap_delta);
2795 resize_rect->set_x0 (current_x - snap_delta);
2797 resize_rect->set_x1 (canvas_note->x1());
2800 resize_rect->set_x1 (snap_to_pixel(current_x) - snap_delta);
2802 resize_rect->set_x1 (current_x - snap_delta);
2804 resize_rect->set_x0 (canvas_note->x0());
2808 /* Convert snap delta from pixels to beats. */
2809 framepos_t snap_delta_samps = trackview.editor().pixel_to_sample (snap_delta);
2810 double snap_delta_beats = 0.0;
2813 /* negative beat offsets aren't allowed */
2814 if (snap_delta_samps > 0) {
2815 snap_delta_beats = region_frames_to_region_beats_double (snap_delta_samps);
2816 } else if (snap_delta_samps < 0) {
2817 snap_delta_beats = region_frames_to_region_beats_double ( - snap_delta_samps);
2821 const double snapped_x = (with_snap ? snap_pixel_to_sample (current_x) : trackview.editor ().pixel_to_sample (current_x));
2822 Evoral::Beats beats = region_frames_to_region_beats (snapped_x);
2823 Evoral::Beats len = Evoral::Beats();
2826 if (beats < canvas_note->note()->end_time()) {
2827 len = canvas_note->note()->time() - beats + (sign * snap_delta_beats);
2828 len += canvas_note->note()->length();
2831 if (beats >= canvas_note->note()->time()) {
2832 len = beats - canvas_note->note()->time() - (sign * snap_delta_beats);
2836 len = std::max(Evoral::Beats(1 / 512.0), len);
2839 snprintf (buf, sizeof (buf), "%.3g beats", len.to_double());
2840 show_verbose_cursor (buf, 0, 0);
2849 /** Finish resizing notes when the user releases the mouse button.
2850 * Parameters the same as for \a update_resizing().
2853 MidiRegionView::commit_resizing (NoteBase* primary, bool at_front, double delta_x, bool relative, double snap_delta, bool with_snap)
2855 _note_diff_command = _model->new_note_diff_command (_("resize notes"));
2857 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2858 Note* canvas_note = (*i)->note;
2859 ArdourCanvas::Rectangle* resize_rect = (*i)->resize_rect;
2861 /* Get the new x position for this resize, which is in pixels relative
2862 * to the region position.
2869 current_x = canvas_note->x0() + delta_x + snap_delta;
2871 current_x = primary->x0() + delta_x + snap_delta;
2875 current_x = canvas_note->x1() + delta_x + snap_delta;
2877 current_x = primary->x1() + delta_x + snap_delta;
2881 if (current_x < 0) {
2884 if (current_x > trackview.editor().sample_to_pixel(_region->length())) {
2885 current_x = trackview.editor().sample_to_pixel(_region->length());
2888 /* Convert snap delta from pixels to beats with sign. */
2889 framepos_t snap_delta_samps = trackview.editor().pixel_to_sample (snap_delta);
2890 double snap_delta_beats = 0.0;
2893 if (snap_delta_samps > 0) {
2894 snap_delta_beats = region_frames_to_region_beats_double (snap_delta_samps);
2895 } else if (snap_delta_samps < 0) {
2896 snap_delta_beats = region_frames_to_region_beats_double ( - snap_delta_samps);
2900 /* Convert the new x position to a frame within the source */
2901 framepos_t current_fr;
2903 current_fr = snap_pixel_to_sample (current_x) + _region->start ();
2905 current_fr = trackview.editor().pixel_to_sample (current_x) + _region->start ();
2908 /* and then to beats */
2909 const Evoral::Beats x_beats = region_frames_to_region_beats (current_fr);
2911 if (at_front && x_beats < canvas_note->note()->end_time()) {
2912 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::StartTime, x_beats - (sign * snap_delta_beats));
2913 Evoral::Beats len = canvas_note->note()->time() - x_beats + (sign * snap_delta_beats);
2914 len += canvas_note->note()->length();
2917 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
2922 Evoral::Beats len = std::max(Evoral::Beats(1 / 512.0),
2923 x_beats - canvas_note->note()->time() - (sign * snap_delta_beats));
2924 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
2931 _resize_data.clear();
2936 MidiRegionView::abort_resizing ()
2938 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2939 delete (*i)->resize_rect;
2943 _resize_data.clear ();
2947 MidiRegionView::change_note_velocity(NoteBase* event, int8_t velocity, bool relative)
2949 uint8_t new_velocity;
2952 new_velocity = event->note()->velocity() + velocity;
2953 clamp_to_0_127(new_velocity);
2955 new_velocity = velocity;
2958 event->set_selected (event->selected()); // change color
2960 note_diff_add_change (event, MidiModel::NoteDiffCommand::Velocity, new_velocity);
2964 MidiRegionView::change_note_note (NoteBase* event, int8_t note, bool relative)
2969 new_note = event->note()->note() + note;
2974 clamp_to_0_127 (new_note);
2975 note_diff_add_change (event, MidiModel::NoteDiffCommand::NoteNumber, new_note);
2979 MidiRegionView::trim_note (NoteBase* event, Evoral::Beats front_delta, Evoral::Beats end_delta)
2981 bool change_start = false;
2982 bool change_length = false;
2983 Evoral::Beats new_start;
2984 Evoral::Beats new_length;
2986 /* NOTE: the semantics of the two delta arguments are slightly subtle:
2988 front_delta: if positive - move the start of the note later in time (shortening it)
2989 if negative - move the start of the note earlier in time (lengthening it)
2991 end_delta: if positive - move the end of the note later in time (lengthening it)
2992 if negative - move the end of the note earlier in time (shortening it)
2995 if (!!front_delta) {
2996 if (front_delta < 0) {
2998 if (event->note()->time() < -front_delta) {
2999 new_start = Evoral::Beats();
3001 new_start = event->note()->time() + front_delta; // moves earlier
3004 /* start moved toward zero, so move the end point out to where it used to be.
3005 Note that front_delta is negative, so this increases the length.
3008 new_length = event->note()->length() - front_delta;
3009 change_start = true;
3010 change_length = true;
3014 Evoral::Beats new_pos = event->note()->time() + front_delta;
3016 if (new_pos < event->note()->end_time()) {
3017 new_start = event->note()->time() + front_delta;
3018 /* start moved toward the end, so move the end point back to where it used to be */
3019 new_length = event->note()->length() - front_delta;
3020 change_start = true;
3021 change_length = true;
3028 bool can_change = true;
3029 if (end_delta < 0) {
3030 if (event->note()->length() < -end_delta) {
3036 new_length = event->note()->length() + end_delta;
3037 change_length = true;
3042 note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_start);
3045 if (change_length) {
3046 note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, new_length);
3051 MidiRegionView::change_note_channel (NoteBase* event, int8_t chn, bool relative)
3053 uint8_t new_channel;
3057 if (event->note()->channel() < -chn) {
3060 new_channel = event->note()->channel() + chn;
3063 new_channel = event->note()->channel() + chn;
3066 new_channel = (uint8_t) chn;
3069 note_diff_add_change (event, MidiModel::NoteDiffCommand::Channel, new_channel);
3073 MidiRegionView::change_note_time (NoteBase* event, Evoral::Beats delta, bool relative)
3075 Evoral::Beats new_time;
3079 if (event->note()->time() < -delta) {
3080 new_time = Evoral::Beats();
3082 new_time = event->note()->time() + delta;
3085 new_time = event->note()->time() + delta;
3091 note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_time);
3095 MidiRegionView::change_note_length (NoteBase* event, Evoral::Beats t)
3097 note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, t);
3101 MidiRegionView::change_velocities (bool up, bool fine, bool allow_smush, bool all_together)
3106 if (_selection.empty()) {
3121 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3122 if ((*i)->note()->velocity() < -delta || (*i)->note()->velocity() + delta > 127) {
3128 start_note_diff_command (_("change velocities"));
3130 for (Selection::iterator i = _selection.begin(); i != _selection.end();) {
3131 Selection::iterator next = i;
3135 if (i == _selection.begin()) {
3136 change_note_velocity (*i, delta, true);
3137 value = (*i)->note()->velocity() + delta;
3139 change_note_velocity (*i, value, false);
3143 change_note_velocity (*i, delta, true);
3152 if (!_selection.empty()) {
3154 snprintf (buf, sizeof (buf), "Vel %d",
3155 (int) (*_selection.begin())->note()->velocity());
3156 show_verbose_cursor (buf, 10, 10);
3162 MidiRegionView::transpose (bool up, bool fine, bool allow_smush)
3164 if (_selection.empty()) {
3181 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3183 if ((int8_t) (*i)->note()->note() + delta <= 0) {
3187 if ((int8_t) (*i)->note()->note() + delta > 127) {
3194 start_note_diff_command (_("transpose"));
3196 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3197 Selection::iterator next = i;
3199 change_note_note (*i, delta, true);
3207 MidiRegionView::change_note_lengths (bool fine, bool shorter, Evoral::Beats delta, bool start, bool end)
3211 delta = Evoral::Beats(1.0/128.0);
3213 /* grab the current grid distance */
3214 delta = get_grid_beats(_region->position());
3222 start_note_diff_command (_("change note lengths"));
3224 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3225 Selection::iterator next = i;
3228 /* note the negation of the delta for start */
3231 (start ? -delta : Evoral::Beats()),
3232 (end ? delta : Evoral::Beats()));
3241 MidiRegionView::nudge_notes (bool forward, bool fine)
3243 if (_selection.empty()) {
3247 /* pick a note as the point along the timeline to get the nudge distance.
3248 its not necessarily the earliest note, so we may want to pull the notes out
3249 into a vector and sort before using the first one.
3252 const framepos_t ref_point = source_beats_to_absolute_frames ((*(_selection.begin()))->note()->time());
3253 Evoral::Beats delta;
3257 /* non-fine, move by 1 bar regardless of snap */
3258 delta = Evoral::Beats(trackview.session()->tempo_map().meter_at(ref_point).divisions_per_bar());
3260 } else if (trackview.editor().snap_mode() == Editing::SnapOff) {
3262 /* grid is off - use nudge distance */
3265 const framecnt_t distance = trackview.editor().get_nudge_distance (ref_point, unused);
3266 delta = region_frames_to_region_beats (fabs ((double)distance));
3272 framepos_t next_pos = ref_point;
3275 if (max_framepos - 1 < next_pos) {
3279 if (next_pos == 0) {
3285 trackview.editor().snap_to (next_pos, (forward ? RoundUpAlways : RoundDownAlways), false);
3286 const framecnt_t distance = ref_point - next_pos;
3287 delta = region_frames_to_region_beats (fabs ((double)distance));
3298 start_note_diff_command (_("nudge"));
3300 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3301 Selection::iterator next = i;
3303 change_note_time (*i, delta, true);
3311 MidiRegionView::change_channel(uint8_t channel)
3313 start_note_diff_command(_("change channel"));
3314 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3315 note_diff_add_change (*i, MidiModel::NoteDiffCommand::Channel, channel);
3323 MidiRegionView::note_entered(NoteBase* ev)
3325 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3327 if (_mouse_state == SelectTouchDragging) {
3328 note_selected (ev, true);
3329 } else if (editor->current_mouse_mode() == MouseContent) {
3330 show_verbose_cursor (ev->note ());
3331 } else if (editor->current_mouse_mode() == MouseDraw) {
3332 show_verbose_cursor (ev->note ());
3337 MidiRegionView::note_left (NoteBase*)
3339 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3341 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3342 (*i)->hide_velocity ();
3345 editor->verbose_cursor()->hide ();
3349 MidiRegionView::patch_entered (PatchChange* p)
3352 /* XXX should get patch name if we can */
3353 s << _("Bank ") << (p->patch()->bank() + MIDI_BP_ZERO) << '\n'
3354 << _("Program ") << ((int) p->patch()->program()) + MIDI_BP_ZERO << '\n'
3355 << _("Channel ") << ((int) p->patch()->channel() + 1);
3356 show_verbose_cursor (s.str(), 10, 20);
3357 p->item().grab_focus();
3361 MidiRegionView::patch_left (PatchChange *)
3363 trackview.editor().verbose_cursor()->hide ();
3364 /* focus will transfer back via the enter-notify event sent to this
3370 MidiRegionView::sysex_entered (SysEx* p)
3374 // need a way to extract text from p->_flag->_text
3376 // show_verbose_cursor (s.str(), 10, 20);
3377 p->item().grab_focus();
3381 MidiRegionView::sysex_left (SysEx *)
3383 trackview.editor().verbose_cursor()->hide ();
3384 /* focus will transfer back via the enter-notify event sent to this
3390 MidiRegionView::note_mouse_position (float x_fraction, float /*y_fraction*/, bool can_set_cursor)
3392 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3393 Editing::MouseMode mm = editor->current_mouse_mode();
3394 bool trimmable = (mm == MouseContent || mm == MouseTimeFX || mm == MouseDraw);
3396 Editor::EnterContext* ctx = editor->get_enter_context(NoteItem);
3397 if (can_set_cursor && ctx) {
3398 if (trimmable && x_fraction > 0.0 && x_fraction < 0.2) {
3399 ctx->cursor_ctx->change(editor->cursors()->left_side_trim);
3400 } else if (trimmable && x_fraction >= 0.8 && x_fraction < 1.0) {
3401 ctx->cursor_ctx->change(editor->cursors()->right_side_trim);
3403 ctx->cursor_ctx->change(editor->cursors()->grabber_note);
3409 MidiRegionView::get_fill_color() const
3411 const std::string mod_name = (_dragging ? "dragging region" :
3412 trackview.editor().internal_editing() ? "editable region" :
3415 return ARDOUR_UI::config()->color_mod ("selected region base", mod_name);
3416 } else if ((!ARDOUR_UI::config()->get_show_name_highlight() || high_enough_for_name) &&
3417 !ARDOUR_UI::config()->get_color_regions_using_track_color()) {
3418 return ARDOUR_UI::config()->color_mod ("midi frame base", mod_name);
3420 return ARDOUR_UI::config()->color_mod (fill_color, mod_name);
3424 MidiRegionView::midi_channel_mode_changed ()
3426 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3427 uint16_t mask = mtv->midi_track()->get_playback_channel_mask();
3428 ChannelMode mode = mtv->midi_track()->get_playback_channel_mode ();
3430 if (mode == ForceChannel) {
3431 mask = 0xFFFF; // Show all notes as active (below)
3434 // Update notes for selection
3435 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3436 (*i)->on_channel_selection_change (mask);
3439 _patch_changes.clear ();
3440 display_patch_changes ();
3444 MidiRegionView::instrument_settings_changed ()
3450 MidiRegionView::cut_copy_clear (Editing::CutCopyOp op)
3452 if (_selection.empty()) {
3456 PublicEditor& editor (trackview.editor());
3460 /* XXX what to do ? */
3464 editor.get_cut_buffer().add (selection_as_cut_buffer());
3472 start_note_diff_command();
3474 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3481 note_diff_remove_note (*i);
3491 MidiRegionView::selection_as_cut_buffer () const
3495 for (Selection::const_iterator i = _selection.begin(); i != _selection.end(); ++i) {
3496 NoteType* n = (*i)->note().get();
3497 notes.insert (boost::shared_ptr<NoteType> (new NoteType (*n)));
3500 MidiCutBuffer* cb = new MidiCutBuffer (trackview.session());
3506 /** This method handles undo */
3508 MidiRegionView::paste (framepos_t pos, const ::Selection& selection, PasteContext& ctx)
3510 bool commit = false;
3511 // Paste notes, if available
3512 MidiNoteSelection::const_iterator m = selection.midi_notes.get_nth(ctx.counts.n_notes());
3513 if (m != selection.midi_notes.end()) {
3514 ctx.counts.increase_n_notes();
3515 if (!(*m)->empty()) { commit = true; }
3516 paste_internal(pos, ctx.count, ctx.times, **m);
3519 // Paste control points to automation children, if available
3520 typedef RouteTimeAxisView::AutomationTracks ATracks;
3521 const ATracks& atracks = midi_view()->automation_tracks();
3522 for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) {
3523 if (a->second->paste(pos, selection, ctx)) {
3529 trackview.editor().commit_reversible_command ();
3534 /** This method handles undo */
3536 MidiRegionView::paste_internal (framepos_t pos, unsigned paste_count, float times, const MidiCutBuffer& mcb)
3542 start_note_diff_command (_("paste"));
3544 const Evoral::Beats snap_beats = get_grid_beats(pos);
3545 const Evoral::Beats first_time = (*mcb.notes().begin())->time();
3546 const Evoral::Beats last_time = (*mcb.notes().rbegin())->end_time();
3547 const Evoral::Beats duration = last_time - first_time;
3548 const Evoral::Beats snap_duration = duration.snap_to(snap_beats);
3549 const Evoral::Beats paste_offset = snap_duration * paste_count;
3550 const Evoral::Beats pos_beats = absolute_frames_to_source_beats(pos) + paste_offset;
3551 Evoral::Beats end_point = Evoral::Beats();
3553 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste data spans from %1 to %2 (%3) ; paste pos beats = %4 (based on %5 - %6)\n",
3556 duration, pos, _region->position(),
3561 for (int n = 0; n < (int) times; ++n) {
3563 for (Notes::const_iterator i = mcb.notes().begin(); i != mcb.notes().end(); ++i) {
3565 boost::shared_ptr<NoteType> copied_note (new NoteType (*((*i).get())));
3566 copied_note->set_time (pos_beats + copied_note->time() - first_time);
3568 /* make all newly added notes selected */
3570 note_diff_add_note (copied_note, true);
3571 end_point = copied_note->end_time();
3575 /* if we pasted past the current end of the region, extend the region */
3577 framepos_t end_frame = source_beats_to_absolute_frames (end_point);
3578 framepos_t region_end = _region->position() + _region->length() - 1;
3580 if (end_frame > region_end) {
3582 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste extended region from %1 to %2\n", region_end, end_frame));
3584 _region->clear_changes ();
3585 _region->set_length (end_frame - _region->position());
3586 trackview.session()->add_command (new StatefulDiffCommand (_region));
3592 struct EventNoteTimeEarlyFirstComparator {
3593 bool operator() (NoteBase* a, NoteBase* b) {
3594 return a->note()->time() < b->note()->time();
3599 MidiRegionView::time_sort_events ()
3601 if (!_sort_needed) {
3605 EventNoteTimeEarlyFirstComparator cmp;
3608 _sort_needed = false;
3612 MidiRegionView::goto_next_note (bool add_to_selection)
3614 bool use_next = false;
3616 if (_events.back()->selected()) {
3620 time_sort_events ();
3622 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3623 uint16_t const channel_mask = mtv->midi_track()->get_playback_channel_mask();
3625 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3626 if ((*i)->selected()) {
3629 } else if (use_next) {
3630 if (channel_mask & (1 << (*i)->note()->channel())) {
3631 if (!add_to_selection) {
3634 note_selected (*i, true, false);
3641 /* use the first one */
3643 if (!_events.empty() && (channel_mask & (1 << _events.front()->note()->channel ()))) {
3644 unique_select (_events.front());
3649 MidiRegionView::goto_previous_note (bool add_to_selection)
3651 bool use_next = false;
3653 if (_events.front()->selected()) {
3657 time_sort_events ();
3659 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3660 uint16_t const channel_mask = mtv->midi_track()->get_playback_channel_mask ();
3662 for (Events::reverse_iterator i = _events.rbegin(); i != _events.rend(); ++i) {
3663 if ((*i)->selected()) {
3666 } else if (use_next) {
3667 if (channel_mask & (1 << (*i)->note()->channel())) {
3668 if (!add_to_selection) {
3671 note_selected (*i, true, false);
3678 /* use the last one */
3680 if (!_events.empty() && (channel_mask & (1 << (*_events.rbegin())->note()->channel ()))) {
3681 unique_select (*(_events.rbegin()));
3686 MidiRegionView::selection_as_notelist (Notes& selected, bool allow_all_if_none_selected)
3688 bool had_selected = false;
3690 time_sort_events ();
3692 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3693 if ((*i)->selected()) {
3694 selected.insert ((*i)->note());
3695 had_selected = true;
3699 if (allow_all_if_none_selected && !had_selected) {
3700 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3701 selected.insert ((*i)->note());
3707 MidiRegionView::update_ghost_note (double x, double y)
3709 x = std::max(0.0, x);
3711 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3716 _note_group->canvas_to_item (x, y);
3718 PublicEditor& editor = trackview.editor ();
3720 framepos_t const unsnapped_frame = editor.pixel_to_sample (x);
3721 framecnt_t grid_frames;
3722 framepos_t const f = snap_frame_to_grid_underneath (unsnapped_frame, grid_frames);
3724 /* calculate time in beats relative to start of source */
3725 const Evoral::Beats length = get_grid_beats(unsnapped_frame);
3726 const Evoral::Beats time = std::max(
3728 absolute_frames_to_source_beats (f + _region->position ()));
3730 _ghost_note->note()->set_time (time);
3731 _ghost_note->note()->set_length (length);
3732 _ghost_note->note()->set_note (midi_stream_view()->y_to_note (y));
3733 _ghost_note->note()->set_channel (mtv->get_channel_for_add ());
3734 _ghost_note->note()->set_velocity (get_velocity_for_add (time));
3736 /* the ghost note does not appear in ghost regions, so pass false in here */
3737 update_note (_ghost_note, false);
3739 show_verbose_cursor (_ghost_note->note ());
3743 MidiRegionView::create_ghost_note (double x, double y)
3745 remove_ghost_note ();
3747 boost::shared_ptr<NoteType> g (new NoteType);
3748 if (midi_view()->note_mode() == Sustained) {
3749 _ghost_note = new Note (*this, _note_group, g);
3751 _ghost_note = new Hit (*this, _note_group, 10, g);
3753 _ghost_note->set_ignore_events (true);
3754 _ghost_note->set_outline_color (0x000000aa);
3755 update_ghost_note (x, y);
3756 _ghost_note->show ();
3758 show_verbose_cursor (_ghost_note->note ());
3762 MidiRegionView::remove_ghost_note ()
3769 MidiRegionView::snap_changed ()
3775 create_ghost_note (_last_ghost_x, _last_ghost_y);
3779 MidiRegionView::drop_down_keys ()
3781 _mouse_state = None;
3785 MidiRegionView::maybe_select_by_position (GdkEventButton* ev, double /*x*/, double y)
3787 /* XXX: This is dead code. What was it for? */
3789 double note = midi_stream_view()->y_to_note(y);
3791 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3793 uint16_t chn_mask = mtv->midi_track()->get_playback_channel_mask();
3795 if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
3796 get_events (e, Evoral::Sequence<Evoral::Beats>::PitchGreaterThanOrEqual, (uint8_t) floor (note), chn_mask);
3797 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
3798 get_events (e, Evoral::Sequence<Evoral::Beats>::PitchLessThanOrEqual, (uint8_t) floor (note), chn_mask);
3803 bool add_mrv_selection = false;
3805 if (_selection.empty()) {
3806 add_mrv_selection = true;
3809 for (Events::iterator i = e.begin(); i != e.end(); ++i) {
3810 if (_selection.insert (*i).second) {
3811 (*i)->set_selected (true);
3815 if (add_mrv_selection) {
3816 PublicEditor& editor (trackview.editor());
3817 editor.get_selection().add (this);
3822 MidiRegionView::color_handler ()
3824 RegionView::color_handler ();
3826 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3827 (*i)->set_selected ((*i)->selected()); // will change color
3830 /* XXX probably more to do here */
3834 MidiRegionView::enable_display (bool yn)
3836 RegionView::enable_display (yn);
3843 MidiRegionView::show_step_edit_cursor (Evoral::Beats pos)
3845 if (_step_edit_cursor == 0) {
3846 ArdourCanvas::Item* const group = get_canvas_group();
3848 _step_edit_cursor = new ArdourCanvas::Rectangle (group);
3849 _step_edit_cursor->set_y0 (0);
3850 _step_edit_cursor->set_y1 (midi_stream_view()->contents_height());
3851 _step_edit_cursor->set_fill_color (RGBA_TO_UINT (45,0,0,90));
3852 _step_edit_cursor->set_outline_color (RGBA_TO_UINT (85,0,0,90));
3855 move_step_edit_cursor (pos);
3856 _step_edit_cursor->show ();
3860 MidiRegionView::move_step_edit_cursor (Evoral::Beats pos)
3862 _step_edit_cursor_position = pos;
3864 if (_step_edit_cursor) {
3865 double pixel = trackview.editor().sample_to_pixel (region_beats_to_region_frames (pos));
3866 _step_edit_cursor->set_x0 (pixel);
3867 set_step_edit_cursor_width (_step_edit_cursor_width);
3872 MidiRegionView::hide_step_edit_cursor ()
3874 if (_step_edit_cursor) {
3875 _step_edit_cursor->hide ();
3880 MidiRegionView::set_step_edit_cursor_width (Evoral::Beats beats)
3882 _step_edit_cursor_width = beats;
3884 if (_step_edit_cursor) {
3885 _step_edit_cursor->set_x1 (_step_edit_cursor->x0() + trackview.editor().sample_to_pixel (region_beats_to_region_frames (beats)));
3889 /** Called when a diskstream on our track has received some data. Update the view, if applicable.
3890 * @param w Source that the data will end up in.
3893 MidiRegionView::data_recorded (boost::weak_ptr<MidiSource> w)
3895 if (!_active_notes) {
3896 /* we aren't actively being recorded to */
3900 boost::shared_ptr<MidiSource> src = w.lock ();
3901 if (!src || src != midi_region()->midi_source()) {
3902 /* recorded data was not destined for our source */
3906 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (&trackview);
3908 boost::shared_ptr<MidiBuffer> buf = mtv->midi_track()->get_gui_feed_buffer ();
3910 framepos_t back = max_framepos;
3912 for (MidiBuffer::iterator i = buf->begin(); i != buf->end(); ++i) {
3913 Evoral::MIDIEvent<MidiBuffer::TimeType> const ev (*i, false);
3915 if (ev.is_channel_event()) {
3916 if (get_channel_mode() == FilterChannels) {
3917 if (((uint16_t(1) << ev.channel()) & get_selected_channels()) == 0) {
3923 /* convert from session frames to source beats */
3924 Evoral::Beats const time_beats = _source_relative_time_converter.from(
3925 ev.time() - src->timeline_position() + _region->start());
3927 if (ev.type() == MIDI_CMD_NOTE_ON) {
3928 boost::shared_ptr<NoteType> note (
3929 new NoteType (ev.channel(), time_beats, Evoral::Beats(), ev.note(), ev.velocity()));
3931 add_note (note, true);
3933 /* fix up our note range */
3934 if (ev.note() < _current_range_min) {
3935 midi_stream_view()->apply_note_range (ev.note(), _current_range_max, true);
3936 } else if (ev.note() > _current_range_max) {
3937 midi_stream_view()->apply_note_range (_current_range_min, ev.note(), true);
3940 } else if (ev.type() == MIDI_CMD_NOTE_OFF) {
3941 resolve_note (ev.note (), time_beats);
3947 midi_stream_view()->check_record_layers (region(), back);
3951 MidiRegionView::trim_front_starting ()
3953 /* Reparent the note group to the region view's parent, so that it doesn't change
3954 when the region view is trimmed.
3956 _temporary_note_group = new ArdourCanvas::Container (group->parent ());
3957 _temporary_note_group->move (group->position ());
3958 _note_group->reparent (_temporary_note_group);
3962 MidiRegionView::trim_front_ending ()
3964 _note_group->reparent (group);
3965 delete _temporary_note_group;
3966 _temporary_note_group = 0;
3968 if (_region->start() < 0) {
3969 /* Trim drag made start time -ve; fix this */
3970 midi_region()->fix_negative_start ();
3975 MidiRegionView::edit_patch_change (PatchChange* pc)
3977 PatchChangeDialog d (&_source_relative_time_converter, trackview.session(), *pc->patch (), instrument_info(), Gtk::Stock::APPLY, true);
3979 int response = d.run();
3982 case Gtk::RESPONSE_ACCEPT:
3984 case Gtk::RESPONSE_REJECT:
3985 delete_patch_change (pc);
3991 change_patch_change (pc->patch(), d.patch ());
3995 MidiRegionView::delete_sysex (SysEx* /*sysex*/)
3998 // sysyex object doesn't have a pointer to a sysex event
3999 // MidiModel::SysExDiffCommand* c = _model->new_sysex_diff_command (_("delete sysex"));
4000 // c->remove (sysex->sysex());
4001 // _model->apply_command (*trackview.session(), c);
4003 //_sys_exes.clear ();
4004 // display_sysexes();
4008 MidiRegionView::show_verbose_cursor (boost::shared_ptr<NoteType> n) const
4010 using namespace MIDI::Name;
4014 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
4016 boost::shared_ptr<MasterDeviceNames> device_names(mtv->get_device_names());
4018 MIDI::Name::PatchPrimaryKey patch_key;
4019 get_patch_key_at(n->time(), n->channel(), patch_key);
4020 name = device_names->note_name(mtv->gui_property(X_("midnam-custom-device-mode")),
4023 patch_key.program(),
4026 mtv->set_note_highlight (n->note());
4030 snprintf (buf, sizeof (buf), "%d %s\nCh %d Vel %d",
4032 name.empty() ? Evoral::midi_note_name (n->note()).c_str() : name.c_str(),
4033 (int) n->channel() + 1,
4034 (int) n->velocity());
4036 show_verbose_cursor(buf, 10, 20);
4040 MidiRegionView::show_verbose_cursor (string const & text, double xoffset, double yoffset) const
4042 trackview.editor().verbose_cursor()->set (text);
4043 trackview.editor().verbose_cursor()->show ();
4044 trackview.editor().verbose_cursor()->set_offset (ArdourCanvas::Duple (xoffset, yoffset));
4048 MidiRegionView::get_velocity_for_add (MidiModel::TimeType time) const
4050 if (_model->notes().empty()) {
4051 return 0x40; // No notes, use default
4054 MidiModel::Notes::const_iterator m = _model->note_lower_bound(time);
4055 if (m == _model->notes().begin()) {
4056 // Before the start, use the velocity of the first note
4057 return (*m)->velocity();
4058 } else if (m == _model->notes().end()) {
4059 // Past the end, use the velocity of the last note
4061 return (*m)->velocity();
4064 // Interpolate velocity of surrounding notes
4065 MidiModel::Notes::const_iterator n = m;
4068 const double frac = ((time - (*n)->time()).to_double() /
4069 ((*m)->time() - (*n)->time()).to_double());
4071 return (*n)->velocity() + (frac * ((*m)->velocity() - (*n)->velocity()));
4074 /** @param p A session framepos.
4075 * @param grid_frames Filled in with the number of frames that a grid interval is at p.
4076 * @return p snapped to the grid subdivision underneath it.
4079 MidiRegionView::snap_frame_to_grid_underneath (framepos_t p, framecnt_t& grid_frames) const
4081 PublicEditor& editor = trackview.editor ();
4083 const Evoral::Beats grid_beats = get_grid_beats(p);
4085 grid_frames = region_beats_to_region_frames (grid_beats);
4087 /* Hack so that we always snap to the note that we are over, instead of snapping
4088 to the next one if we're more than halfway through the one we're over.
4090 if (editor.snap_mode() == SnapNormal && p >= grid_frames / 2) {
4091 p -= grid_frames / 2;
4094 return snap_frame_to_frame (p);
4097 /** Called when the selection has been cleared in any MidiRegionView.
4098 * @param rv MidiRegionView that the selection was cleared in.
4101 MidiRegionView::selection_cleared (MidiRegionView* rv)
4107 /* Clear our selection in sympathy; but don't signal the fact */
4108 clear_selection (false);
4112 MidiRegionView::get_channel_mode () const
4114 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (&trackview);
4115 return rtav->midi_track()->get_playback_channel_mode();
4119 MidiRegionView::get_selected_channels () const
4121 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (&trackview);
4122 return rtav->midi_track()->get_playback_channel_mask();
4127 MidiRegionView::get_grid_beats(framepos_t pos) const
4129 PublicEditor& editor = trackview.editor();
4130 bool success = false;
4131 Evoral::Beats beats = editor.get_grid_type_as_beats(success, pos);
4133 beats = Evoral::Beats(1);