2 Copyright (C) 2001-2011 Paul Davis
3 Author: David Robillard
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 #include "gtkmm2ext/gtk_ui.h"
28 #include <sigc++/signal.h>
30 #include "midi++/midnam_patch.h"
32 #include "pbd/memento_command.h"
33 #include "pbd/stateful_diff_command.h"
35 #include "ardour/midi_model.h"
36 #include "ardour/midi_region.h"
37 #include "ardour/midi_source.h"
38 #include "ardour/midi_track.h"
39 #include "ardour/operations.h"
40 #include "ardour/session.h"
42 #include "evoral/Parameter.hpp"
43 #include "evoral/MIDIEvent.hpp"
44 #include "evoral/Control.hpp"
45 #include "evoral/midi_util.h"
47 #include "canvas/debug.h"
48 #include "canvas/text.h"
50 #include "automation_region_view.h"
51 #include "automation_time_axis.h"
52 #include "control_point.h"
55 #include "editor_drag.h"
56 #include "ghostregion.h"
57 #include "gui_thread.h"
58 #include "item_counts.h"
60 #include "midi_channel_dialog.h"
61 #include "midi_cut_buffer.h"
62 #include "midi_list_editor.h"
63 #include "midi_region_view.h"
64 #include "midi_streamview.h"
65 #include "midi_time_axis.h"
66 #include "midi_util.h"
67 #include "midi_velocity_dialog.h"
68 #include "mouse_cursors.h"
69 #include "note_player.h"
70 #include "paste_context.h"
71 #include "public_editor.h"
72 #include "route_time_axis.h"
73 #include "rgb_macros.h"
74 #include "selection.h"
75 #include "streamview.h"
76 #include "patch_change_dialog.h"
77 #include "verbose_cursor.h"
78 #include "ardour_ui.h"
81 #include "patch_change.h"
86 using namespace ARDOUR;
88 using namespace Editing;
90 using Gtkmm2ext::Keyboard;
92 PBD::Signal1<void, MidiRegionView *> MidiRegionView::SelectionCleared;
94 #define MIDI_BP_ZERO ((Config->get_first_midi_bank_is_zero())?0:1)
96 MidiRegionView::MidiRegionView (ArdourCanvas::Container* parent,
97 RouteTimeAxisView& tv,
98 boost::shared_ptr<MidiRegion> r,
100 uint32_t basic_color)
101 : RegionView (parent, tv, r, spu, basic_color)
102 , _current_range_min(0)
103 , _current_range_max(0)
104 , _region_relative_time_converter(r->session().tempo_map(), r->position())
105 , _source_relative_time_converter(r->session().tempo_map(), r->position() - r->start())
107 , _note_group (new ArdourCanvas::Container (group))
108 , _note_diff_command (0)
110 , _step_edit_cursor (0)
111 , _step_edit_cursor_width (1.0)
112 , _step_edit_cursor_position (0.0)
113 , _channel_selection_scoped_note (0)
114 , _temporary_note_group (0)
117 , _sort_needed (true)
118 , _optimization_iterator (_events.end())
120 , _no_sound_notes (false)
121 , _last_display_zoom (0)
124 , _grabbed_keyboard (false)
126 , _mouse_changed_selection (false)
128 CANVAS_DEBUG_NAME (_note_group, string_compose ("note group for %1", get_item_name()));
129 _note_group->raise_to_top();
130 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
132 Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&MidiRegionView::parameter_changed, this, _1), gui_context());
133 connect_to_diskstream ();
135 SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), boost::bind (&MidiRegionView::selection_cleared, this, _1), gui_context ());
137 PublicEditor& editor (trackview.editor());
138 editor.get_selection().ClearMidiNoteSelection.connect (_clear_midi_selection_connection, invalidator (*this), boost::bind (&MidiRegionView::clear_midi_selection, this), gui_context ());
141 MidiRegionView::MidiRegionView (ArdourCanvas::Container* parent,
142 RouteTimeAxisView& tv,
143 boost::shared_ptr<MidiRegion> r,
145 uint32_t basic_color,
147 TimeAxisViewItem::Visibility visibility)
148 : RegionView (parent, tv, r, spu, basic_color, recording, visibility)
149 , _current_range_min(0)
150 , _current_range_max(0)
151 , _region_relative_time_converter(r->session().tempo_map(), r->position())
152 , _source_relative_time_converter(r->session().tempo_map(), r->position() - r->start())
154 , _note_group (new ArdourCanvas::Container (group))
155 , _note_diff_command (0)
157 , _step_edit_cursor (0)
158 , _step_edit_cursor_width (1.0)
159 , _step_edit_cursor_position (0.0)
160 , _channel_selection_scoped_note (0)
161 , _temporary_note_group (0)
164 , _sort_needed (true)
165 , _optimization_iterator (_events.end())
167 , _no_sound_notes (false)
168 , _last_display_zoom (0)
171 , _grabbed_keyboard (false)
173 , _mouse_changed_selection (false)
175 CANVAS_DEBUG_NAME (_note_group, string_compose ("note group for %1", get_item_name()));
176 _note_group->raise_to_top();
178 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
180 connect_to_diskstream ();
182 SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), boost::bind (&MidiRegionView::selection_cleared, this, _1), gui_context ());
184 PublicEditor& editor (trackview.editor());
185 editor.get_selection().ClearMidiNoteSelection.connect (_clear_midi_selection_connection, invalidator (*this), boost::bind (&MidiRegionView::clear_midi_selection, this), gui_context ());
189 MidiRegionView::parameter_changed (std::string const & p)
191 if (p == "display-first-midi-bank-as-zero") {
192 if (_enable_display) {
198 MidiRegionView::MidiRegionView (const MidiRegionView& other)
199 : sigc::trackable(other)
201 , _current_range_min(0)
202 , _current_range_max(0)
203 , _region_relative_time_converter(other.region_relative_time_converter())
204 , _source_relative_time_converter(other.source_relative_time_converter())
206 , _note_group (new ArdourCanvas::Container (get_canvas_group()))
207 , _note_diff_command (0)
209 , _step_edit_cursor (0)
210 , _step_edit_cursor_width (1.0)
211 , _step_edit_cursor_position (0.0)
212 , _channel_selection_scoped_note (0)
213 , _temporary_note_group (0)
216 , _sort_needed (true)
217 , _optimization_iterator (_events.end())
219 , _no_sound_notes (false)
220 , _last_display_zoom (0)
223 , _grabbed_keyboard (false)
225 , _mouse_changed_selection (false)
230 MidiRegionView::MidiRegionView (const MidiRegionView& other, boost::shared_ptr<MidiRegion> region)
231 : RegionView (other, boost::shared_ptr<Region> (region))
232 , _current_range_min(0)
233 , _current_range_max(0)
234 , _region_relative_time_converter(other.region_relative_time_converter())
235 , _source_relative_time_converter(other.source_relative_time_converter())
237 , _note_group (new ArdourCanvas::Container (get_canvas_group()))
238 , _note_diff_command (0)
240 , _step_edit_cursor (0)
241 , _step_edit_cursor_width (1.0)
242 , _step_edit_cursor_position (0.0)
243 , _channel_selection_scoped_note (0)
244 , _temporary_note_group (0)
247 , _sort_needed (true)
248 , _optimization_iterator (_events.end())
250 , _no_sound_notes (false)
251 , _last_display_zoom (0)
254 , _grabbed_keyboard (false)
256 , _mouse_changed_selection (false)
262 MidiRegionView::init (bool wfd)
264 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
266 NoteBase::NoteBaseDeleted.connect (note_delete_connection, MISSING_INVALIDATOR,
267 boost::bind (&MidiRegionView::maybe_remove_deleted_note_from_selection, this, _1),
271 Glib::Threads::Mutex::Lock lm(midi_region()->midi_source(0)->mutex());
272 midi_region()->midi_source(0)->load_model(lm);
275 _model = midi_region()->midi_source(0)->model();
276 _enable_display = false;
277 fill_color_name = "midi frame base";
279 RegionView::init (false);
281 set_height (trackview.current_height());
284 region_sync_changed ();
285 region_resized (ARDOUR::bounds_change);
290 _enable_display = true;
293 display_model (_model);
297 reset_width_dependent_items (_pixel_width);
299 group->raise_to_top();
301 midi_view()->midi_track()->PlaybackChannelModeChanged.connect (_channel_mode_changed_connection, invalidator (*this),
302 boost::bind (&MidiRegionView::midi_channel_mode_changed, this),
305 instrument_info().Changed.connect (_instrument_changed_connection, invalidator (*this),
306 boost::bind (&MidiRegionView::instrument_settings_changed, this), gui_context());
308 trackview.editor().SnapChanged.connect(snap_changed_connection, invalidator(*this),
309 boost::bind (&MidiRegionView::snap_changed, this),
312 trackview.editor().MouseModeChanged.connect(_mouse_mode_connection, invalidator (*this),
313 boost::bind (&MidiRegionView::mouse_mode_changed, this),
316 Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&MidiRegionView::parameter_changed, this, _1), gui_context());
317 connect_to_diskstream ();
319 SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), boost::bind (&MidiRegionView::selection_cleared, this, _1), gui_context ());
321 PublicEditor& editor (trackview.editor());
322 editor.get_selection().ClearMidiNoteSelection.connect (_clear_midi_selection_connection, invalidator (*this), boost::bind (&MidiRegionView::clear_midi_selection, this), gui_context ());
326 MidiRegionView::instrument_info () const
328 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
329 return route_ui->route()->instrument_info();
332 const boost::shared_ptr<ARDOUR::MidiRegion>
333 MidiRegionView::midi_region() const
335 return boost::dynamic_pointer_cast<ARDOUR::MidiRegion>(_region);
339 MidiRegionView::connect_to_diskstream ()
341 midi_view()->midi_track()->DataRecorded.connect(
342 *this, invalidator(*this),
343 boost::bind (&MidiRegionView::data_recorded, this, _1),
348 MidiRegionView::canvas_group_event(GdkEvent* ev)
350 if (in_destructor || _recregion) {
354 if (!trackview.editor().internal_editing()) {
355 // not in internal edit mode, so just act like a normal region
356 return RegionView::canvas_group_event (ev);
362 case GDK_ENTER_NOTIFY:
363 _last_event_x = ev->crossing.x;
364 _last_event_y = ev->crossing.y;
365 enter_notify(&ev->crossing);
366 // set entered_regionview (among other things)
367 return RegionView::canvas_group_event (ev);
369 case GDK_LEAVE_NOTIFY:
370 _last_event_x = ev->crossing.x;
371 _last_event_y = ev->crossing.y;
372 leave_notify(&ev->crossing);
373 // reset entered_regionview (among other things)
374 return RegionView::canvas_group_event (ev);
377 if (scroll (&ev->scroll)) {
383 return key_press (&ev->key);
385 case GDK_KEY_RELEASE:
386 return key_release (&ev->key);
388 case GDK_BUTTON_PRESS:
389 return button_press (&ev->button);
391 case GDK_BUTTON_RELEASE:
392 r = button_release (&ev->button);
393 _note_player.reset();
396 case GDK_MOTION_NOTIFY:
397 _last_event_x = ev->motion.x;
398 _last_event_y = ev->motion.y;
399 return motion (&ev->motion);
405 return RegionView::canvas_group_event (ev);
409 MidiRegionView::enter_notify (GdkEventCrossing* ev)
418 MidiRegionView::leave_notify (GdkEventCrossing*)
427 MidiRegionView::mouse_mode_changed ()
429 // Adjust frame colour (become more transparent for internal tools)
433 if (trackview.editor().internal_editing()) {
434 // Switched in to internal editing mode while entered
437 // Switched out of internal editing mode while entered
444 MidiRegionView::enter_internal()
446 if (trackview.editor().current_mouse_mode() == MouseDraw && _mouse_state != AddDragging) {
447 // Show ghost note under pencil
448 create_ghost_note(_last_event_x, _last_event_y);
451 if (!_selection.empty()) {
452 // Grab keyboard for moving selected notes with arrow keys
453 Keyboard::magic_widget_grab_focus();
454 _grabbed_keyboard = true;
457 // Lower frame handles below notes so they don't steal events
458 if (frame_handle_start) {
459 frame_handle_start->lower_to_bottom();
461 if (frame_handle_end) {
462 frame_handle_end->lower_to_bottom();
467 MidiRegionView::leave_internal()
469 trackview.editor().verbose_cursor()->hide ();
470 remove_ghost_note ();
472 if (_grabbed_keyboard) {
473 Keyboard::magic_widget_drop_focus();
474 _grabbed_keyboard = false;
477 // Raise frame handles above notes so they catch events
478 if (frame_handle_start) {
479 frame_handle_start->raise_to_top();
481 if (frame_handle_end) {
482 frame_handle_end->raise_to_top();
487 MidiRegionView::button_press (GdkEventButton* ev)
489 if (ev->button != 1) {
493 Editor* editor = dynamic_cast<Editor *> (&trackview.editor());
494 MouseMode m = editor->current_mouse_mode();
496 if (m == MouseContent && Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())) {
497 _press_cursor_ctx = CursorContext::create(*editor, editor->cursors()->midi_pencil);
500 if (_mouse_state != SelectTouchDragging) {
502 _pressed_button = ev->button;
503 _mouse_state = Pressed;
508 _pressed_button = ev->button;
509 _mouse_changed_selection = false;
515 MidiRegionView::button_release (GdkEventButton* ev)
517 double event_x, event_y;
519 if (ev->button != 1) {
526 group->canvas_to_item (event_x, event_y);
529 PublicEditor& editor = trackview.editor ();
531 _press_cursor_ctx.reset();
533 switch (_mouse_state) {
534 case Pressed: // Clicked
536 switch (editor.current_mouse_mode()) {
538 /* no motion occured - simple click */
540 _mouse_changed_selection = true;
547 _mouse_changed_selection = true;
549 if (Keyboard::is_insert_note_event(ev)) {
551 double event_x, event_y;
555 group->canvas_to_item (event_x, event_y);
557 Evoral::Beats beats = get_grid_beats(editor.pixel_to_sample(event_x));
559 /* Shorten the length by 1 tick so that we can add a new note at the next
560 grid snap without it overlapping this one.
562 beats -= Evoral::Beats::tick();
564 create_note_at (editor.pixel_to_sample (event_x), event_y, beats, true);
571 Evoral::Beats beats = get_grid_beats(editor.pixel_to_sample(event_x));
573 /* Shorten the length by 1 tick so that we can add a new note at the next
574 grid snap without it overlapping this one.
576 beats -= Evoral::Beats::tick();
578 create_note_at (editor.pixel_to_sample (event_x), event_y, beats, true);
589 case SelectRectDragging:
591 editor.drags()->end_grab ((GdkEvent *) ev);
593 create_ghost_note (ev->x, ev->y);
601 if (_mouse_changed_selection) {
602 trackview.editor().begin_reversible_selection_op (_("Mouse Selection Change"));
603 trackview.editor().commit_reversible_selection_op ();
610 MidiRegionView::motion (GdkEventMotion* ev)
612 PublicEditor& editor = trackview.editor ();
614 if (!_ghost_note && editor.current_mouse_mode() == MouseContent &&
615 Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier()) &&
616 _mouse_state != AddDragging) {
618 create_ghost_note (ev->x, ev->y);
620 } else if (_ghost_note && editor.current_mouse_mode() == MouseContent &&
621 Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())) {
623 update_ghost_note (ev->x, ev->y);
625 } else if (_ghost_note && editor.current_mouse_mode() == MouseContent) {
627 remove_ghost_note ();
628 editor.verbose_cursor()->hide ();
630 } else if (_ghost_note && editor.current_mouse_mode() == MouseDraw) {
632 update_ghost_note (ev->x, ev->y);
635 /* any motion immediately hides velocity text that may have been visible */
637 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
638 (*i)->hide_velocity ();
641 switch (_mouse_state) {
644 if (_pressed_button == 1) {
646 MouseMode m = editor.current_mouse_mode();
648 if (m == MouseDraw || (m == MouseContent && Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier()))) {
649 editor.drags()->set (new NoteCreateDrag (dynamic_cast<Editor *> (&editor), group, this), (GdkEvent *) ev);
650 _mouse_state = AddDragging;
651 remove_ghost_note ();
652 editor.verbose_cursor()->hide ();
654 } else if (m == MouseContent) {
655 editor.drags()->set (new MidiRubberbandSelectDrag (dynamic_cast<Editor *> (&editor), this), (GdkEvent *) ev);
656 if (!Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
658 _mouse_changed_selection = true;
660 _mouse_state = SelectRectDragging;
662 } else if (m == MouseRange) {
663 editor.drags()->set (new MidiVerticalSelectDrag (dynamic_cast<Editor *> (&editor), this), (GdkEvent *) ev);
664 _mouse_state = SelectVerticalDragging;
671 case SelectRectDragging:
672 case SelectVerticalDragging:
674 editor.drags()->motion_handler ((GdkEvent *) ev, false);
677 case SelectTouchDragging:
685 /* we may be dragging some non-note object (eg. patch-change, sysex)
688 return editor.drags()->motion_handler ((GdkEvent *) ev, false);
693 MidiRegionView::scroll (GdkEventScroll* ev)
695 if (_selection.empty()) {
699 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
700 /* XXX: bit of a hack; allow PrimaryModifier scroll through so that
701 it still works for zoom.
706 trackview.editor().verbose_cursor()->hide ();
708 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
709 bool together = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
711 if (ev->direction == GDK_SCROLL_UP) {
712 change_velocities (true, fine, false, together);
713 } else if (ev->direction == GDK_SCROLL_DOWN) {
714 change_velocities (false, fine, false, together);
716 /* left, right: we don't use them */
724 MidiRegionView::key_press (GdkEventKey* ev)
726 /* since GTK bindings are generally activated on press, and since
727 detectable auto-repeat is the name of the game and only sends
728 repeated presses, carry out key actions at key press, not release.
731 bool unmodified = Keyboard::no_modifier_keys_pressed (ev);
733 if (unmodified && (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R)) {
734 _mouse_state = SelectTouchDragging;
737 } else if (ev->keyval == GDK_Escape && unmodified) {
741 } else if (ev->keyval == GDK_comma || ev->keyval == GDK_period) {
743 bool start = (ev->keyval == GDK_comma);
744 bool end = (ev->keyval == GDK_period);
745 bool shorter = Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier);
746 bool fine = Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
748 change_note_lengths (fine, shorter, Evoral::Beats(), start, end);
752 } else if ((ev->keyval == GDK_BackSpace || ev->keyval == GDK_Delete) && unmodified) {
754 if (_selection.empty()) {
761 } else if (ev->keyval == GDK_Tab || ev->keyval == GDK_ISO_Left_Tab) {
763 trackview.editor().begin_reversible_selection_op (_("Select Adjacent Note"));
765 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
766 goto_previous_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
768 goto_next_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
771 trackview.editor().commit_reversible_selection_op();
775 } else if (ev->keyval == GDK_Up) {
777 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
778 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
779 bool together = Keyboard::modifier_state_contains (ev->state, Keyboard::Level4Modifier);
781 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
782 change_velocities (true, fine, allow_smush, together);
784 transpose (true, fine, allow_smush);
788 } else if (ev->keyval == GDK_Down) {
790 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
791 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
792 bool together = Keyboard::modifier_state_contains (ev->state, Keyboard::Level4Modifier);
794 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
795 change_velocities (false, fine, allow_smush, together);
797 transpose (false, fine, allow_smush);
801 } else if (ev->keyval == GDK_Left) {
803 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
804 nudge_notes (false, fine);
807 } else if (ev->keyval == GDK_Right) {
809 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
810 nudge_notes (true, fine);
813 } else if (ev->keyval == GDK_c && unmodified) {
817 } else if (ev->keyval == GDK_v && unmodified) {
826 MidiRegionView::key_release (GdkEventKey* ev)
828 if ((_mouse_state == SelectTouchDragging) && (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R)) {
836 MidiRegionView::channel_edit ()
838 if (_selection.empty()) {
842 /* pick a note somewhat at random (since Selection is a set<>) to
843 * provide the "current" channel for the dialog.
846 uint8_t current_channel = (*_selection.begin())->note()->channel ();
847 MidiChannelDialog channel_dialog (current_channel);
848 int ret = channel_dialog.run ();
851 case Gtk::RESPONSE_OK:
857 uint8_t new_channel = channel_dialog.active_channel ();
859 start_note_diff_command (_("channel edit"));
861 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
862 Selection::iterator next = i;
864 change_note_channel (*i, new_channel);
872 MidiRegionView::velocity_edit ()
874 if (_selection.empty()) {
878 /* pick a note somewhat at random (since Selection is a set<>) to
879 * provide the "current" velocity for the dialog.
882 uint8_t current_velocity = (*_selection.begin())->note()->velocity ();
883 MidiVelocityDialog velocity_dialog (current_velocity);
884 int ret = velocity_dialog.run ();
887 case Gtk::RESPONSE_OK:
893 uint8_t new_velocity = velocity_dialog.velocity ();
895 start_note_diff_command (_("velocity edit"));
897 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
898 Selection::iterator next = i;
900 change_note_velocity (*i, new_velocity, false);
908 MidiRegionView::show_list_editor ()
911 _list_editor = new MidiListEditor (trackview.session(), midi_region(), midi_view()->midi_track());
913 _list_editor->present ();
916 /** Add a note to the model, and the view, at a canvas (click) coordinate.
917 * \param t time in frames relative to the position of the region
918 * \param y vertical position in pixels
919 * \param length duration of the note in beats
920 * \param snap_t true to snap t to the grid, otherwise false.
923 MidiRegionView::create_note_at (framepos_t t, double y, Evoral::Beats length, bool snap_t)
925 if (length < 2 * DBL_EPSILON) {
929 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
930 MidiStreamView* const view = mtv->midi_view();
932 // Start of note in frames relative to region start
934 framecnt_t grid_frames;
935 t = snap_frame_to_grid_underneath (t, grid_frames);
938 const MidiModel::TimeType beat_time = region_frames_to_region_beats(
939 t + _region->start());
941 const double note = view->y_to_note(y);
942 const uint8_t chan = mtv->get_channel_for_add();
943 const uint8_t velocity = get_velocity_for_add(beat_time);
945 const boost::shared_ptr<NoteType> new_note(
946 new NoteType (chan, beat_time, length, (uint8_t)note, velocity));
948 if (_model->contains (new_note)) {
952 view->update_note_range(new_note->note());
954 start_note_diff_command(_("add note"));
957 note_diff_add_note (new_note, true, false);
961 play_midi_note (new_note);
965 MidiRegionView::clear_events (bool with_selection_signal)
967 clear_selection (with_selection_signal);
970 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
971 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
976 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
981 _patch_changes.clear();
983 _optimization_iterator = _events.end();
987 MidiRegionView::display_model(boost::shared_ptr<MidiModel> model)
991 content_connection.disconnect ();
992 _model->ContentsChanged.connect (content_connection, invalidator (*this), boost::bind (&MidiRegionView::redisplay_model, this), gui_context());
993 /* Don't signal as nobody else needs to know until selection has been altered. */
994 clear_events (false);
996 if (_enable_display) {
1002 MidiRegionView::start_note_diff_command (string name)
1004 if (!_note_diff_command) {
1005 trackview.editor().begin_reversible_command (name);
1006 _note_diff_command = _model->new_note_diff_command (name);
1011 MidiRegionView::note_diff_add_note (const boost::shared_ptr<NoteType> note, bool selected, bool show_velocity)
1013 if (_note_diff_command) {
1014 _note_diff_command->add (note);
1017 _marked_for_selection.insert(note);
1019 if (show_velocity) {
1020 _marked_for_velocity.insert(note);
1025 MidiRegionView::note_diff_remove_note (NoteBase* ev)
1027 if (_note_diff_command && ev->note()) {
1028 _note_diff_command->remove(ev->note());
1033 MidiRegionView::note_diff_add_change (NoteBase* ev,
1034 MidiModel::NoteDiffCommand::Property property,
1037 if (_note_diff_command) {
1038 _note_diff_command->change (ev->note(), property, val);
1043 MidiRegionView::note_diff_add_change (NoteBase* ev,
1044 MidiModel::NoteDiffCommand::Property property,
1047 if (_note_diff_command) {
1048 _note_diff_command->change (ev->note(), property, val);
1053 MidiRegionView::apply_diff (bool as_subcommand)
1056 bool commit = false;
1058 if (!_note_diff_command) {
1062 if ((add_or_remove = _note_diff_command->adds_or_removes())) {
1063 // Mark all selected notes for selection when model reloads
1064 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1065 _marked_for_selection.insert((*i)->note());
1069 if (as_subcommand) {
1070 _model->apply_command_as_subcommand (*trackview.session(), _note_diff_command);
1072 _model->apply_command (*trackview.session(), _note_diff_command);
1076 _note_diff_command = 0;
1077 midi_view()->midi_track()->playlist_modified();
1079 if (add_or_remove) {
1080 _marked_for_selection.clear();
1083 _marked_for_velocity.clear();
1085 trackview.editor().commit_reversible_command ();
1090 MidiRegionView::abort_command()
1092 delete _note_diff_command;
1093 _note_diff_command = 0;
1098 MidiRegionView::find_canvas_note (boost::shared_ptr<NoteType> note)
1100 if (_optimization_iterator != _events.end()) {
1101 ++_optimization_iterator;
1104 if (_optimization_iterator != _events.end() && (*_optimization_iterator)->note() == note) {
1105 return *_optimization_iterator;
1108 for (_optimization_iterator = _events.begin(); _optimization_iterator != _events.end(); ++_optimization_iterator) {
1109 if ((*_optimization_iterator)->note() == note) {
1110 return *_optimization_iterator;
1117 /** This version finds any canvas note matching the supplied note. */
1119 MidiRegionView::find_canvas_note (NoteType note)
1121 Events::iterator it;
1123 for (it = _events.begin(); it != _events.end(); ++it) {
1124 if (*((*it)->note()) == note) {
1133 MidiRegionView::get_events (Events& e, Evoral::Sequence<Evoral::Beats>::NoteOperator op, uint8_t val, int chan_mask)
1135 MidiModel::Notes notes;
1136 _model->get_notes (notes, op, val, chan_mask);
1138 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1139 NoteBase* cne = find_canvas_note (*n);
1147 MidiRegionView::redisplay_model()
1149 if (_active_notes) {
1150 // Currently recording
1151 const framecnt_t zoom = trackview.editor().get_current_zoom();
1152 if (zoom != _last_display_zoom) {
1153 /* Update resolved canvas notes to reflect changes in zoom without
1154 touching model. Leave active notes (with length 0) alone since
1155 they are being extended. */
1156 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1157 if ((*i)->note()->length() > 0) {
1161 _last_display_zoom = zoom;
1170 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1171 (*i)->invalidate ();
1174 MidiModel::ReadLock lock(_model->read_lock());
1176 MidiModel::Notes& notes (_model->notes());
1177 _optimization_iterator = _events.begin();
1179 bool empty_when_starting = _events.empty();
1181 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1183 boost::shared_ptr<NoteType> note (*n);
1187 if (note_in_region_range (note, visible)) {
1189 if (!empty_when_starting && (cne = find_canvas_note (note)) != 0) {
1202 cne = add_note (note, visible);
1205 set<boost::shared_ptr<NoteType> >::iterator it;
1206 for (it = _pending_note_selection.begin(); it != _pending_note_selection.end(); ++it) {
1207 if (*(*it) == *note) {
1208 add_to_selection (cne);
1214 if (!empty_when_starting && (cne = find_canvas_note (note)) != 0) {
1221 /* remove note items that are no longer valid */
1223 if (!empty_when_starting) {
1224 for (Events::iterator i = _events.begin(); i != _events.end(); ) {
1225 if (!(*i)->valid ()) {
1227 for (vector<GhostRegion*>::iterator j = ghosts.begin(); j != ghosts.end(); ++j) {
1228 MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*j);
1230 gr->remove_note (*i);
1235 i = _events.erase (i);
1243 _patch_changes.clear();
1247 display_patch_changes ();
1249 _marked_for_selection.clear ();
1250 _marked_for_velocity.clear ();
1251 _pending_note_selection.clear ();
1253 /* we may have caused _events to contain things out of order (e.g. if a note
1254 moved earlier or later). we don't generally need them in time order, but
1255 make a note that a sort is required for those cases that require it.
1258 _sort_needed = true;
1262 MidiRegionView::display_patch_changes ()
1264 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1265 uint16_t chn_mask = mtv->midi_track()->get_playback_channel_mask();
1267 for (uint8_t i = 0; i < 16; ++i) {
1268 display_patch_changes_on_channel (i, chn_mask & (1 << i));
1272 /** @param active_channel true to display patch changes fully, false to display
1273 * them `greyed-out' (as on an inactive channel)
1276 MidiRegionView::display_patch_changes_on_channel (uint8_t channel, bool active_channel)
1278 for (MidiModel::PatchChanges::const_iterator i = _model->patch_changes().begin(); i != _model->patch_changes().end(); ++i) {
1280 if ((*i)->channel() != channel) {
1284 const string patch_name = instrument_info().get_patch_name ((*i)->bank(), (*i)->program(), channel);
1285 add_canvas_patch_change (*i, patch_name, active_channel);
1290 MidiRegionView::display_sysexes()
1292 bool have_periodic_system_messages = false;
1293 bool display_periodic_messages = true;
1295 if (!ARDOUR_UI::config()->get_never_display_periodic_midi()) {
1297 for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
1298 const boost::shared_ptr<const Evoral::MIDIEvent<Evoral::Beats> > mev =
1299 boost::static_pointer_cast<const Evoral::MIDIEvent<Evoral::Beats> > (*i);
1302 if (mev->is_spp() || mev->is_mtc_quarter() || mev->is_mtc_full()) {
1303 have_periodic_system_messages = true;
1309 if (have_periodic_system_messages) {
1310 double zoom = trackview.editor().get_current_zoom (); // frames per pixel
1312 /* get an approximate value for the number of samples per video frame */
1314 double video_frame = trackview.session()->frame_rate() * (1.0/30);
1316 /* if we are zoomed out beyond than the cutoff (i.e. more
1317 * frames per pixel than frames per 4 video frames), don't
1318 * show periodic sysex messages.
1321 if (zoom > (video_frame*4)) {
1322 display_periodic_messages = false;
1326 display_periodic_messages = false;
1329 for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
1331 const boost::shared_ptr<const Evoral::MIDIEvent<Evoral::Beats> > mev =
1332 boost::static_pointer_cast<const Evoral::MIDIEvent<Evoral::Beats> > (*i);
1334 Evoral::Beats time = (*i)->time();
1337 if (mev->is_spp() || mev->is_mtc_quarter() || mev->is_mtc_full()) {
1338 if (!display_periodic_messages) {
1346 for (uint32_t b = 0; b < (*i)->size(); ++b) {
1347 str << int((*i)->buffer()[b]);
1348 if (b != (*i)->size() -1) {
1352 string text = str.str();
1354 const double x = trackview.editor().sample_to_pixel(source_beats_to_region_frames(time));
1356 double height = midi_stream_view()->contents_height();
1358 // CAIROCANVAS: no longer passing *i (the sysex event) to the
1359 // SysEx canvas object!!!
1361 boost::shared_ptr<SysEx> sysex = boost::shared_ptr<SysEx>(
1362 new SysEx (*this, _note_group, text, height, x, 1.0));
1364 // Show unless message is beyond the region bounds
1365 if (time - _region->start() >= _region->length() || time < _region->start()) {
1371 _sys_exes.push_back(sysex);
1375 MidiRegionView::~MidiRegionView ()
1377 in_destructor = true;
1379 trackview.editor().verbose_cursor()->hide ();
1381 note_delete_connection.disconnect ();
1383 delete _list_editor;
1385 RegionViewGoingAway (this); /* EMIT_SIGNAL */
1387 if (_active_notes) {
1391 _selection_cleared_connection.disconnect ();
1394 clear_events (false);
1397 delete _note_diff_command;
1398 delete _step_edit_cursor;
1399 delete _temporary_note_group;
1403 MidiRegionView::region_resized (const PropertyChange& what_changed)
1405 RegionView::region_resized(what_changed);
1407 if (what_changed.contains (ARDOUR::Properties::position)) {
1408 _region_relative_time_converter.set_origin_b(_region->position());
1409 set_duration(_region->length(), 0);
1410 if (_enable_display) {
1415 if (what_changed.contains (ARDOUR::Properties::start) ||
1416 what_changed.contains (ARDOUR::Properties::position)) {
1417 _source_relative_time_converter.set_origin_b (_region->position() - _region->start());
1422 MidiRegionView::reset_width_dependent_items (double pixel_width)
1424 RegionView::reset_width_dependent_items(pixel_width);
1426 if (_enable_display) {
1430 for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
1431 if ((*x)->canvas_item()->width() >= _pixel_width) {
1438 move_step_edit_cursor (_step_edit_cursor_position);
1439 set_step_edit_cursor_width (_step_edit_cursor_width);
1443 MidiRegionView::set_height (double height)
1445 double old_height = _height;
1446 RegionView::set_height(height);
1448 apply_note_range (midi_stream_view()->lowest_note(),
1449 midi_stream_view()->highest_note(),
1450 height != old_height);
1453 name_text->raise_to_top();
1456 for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
1457 (*x)->set_height (midi_stream_view()->contents_height());
1460 if (_step_edit_cursor) {
1461 _step_edit_cursor->set_y1 (midi_stream_view()->contents_height());
1466 /** Apply the current note range from the stream view
1467 * by repositioning/hiding notes as necessary
1470 MidiRegionView::apply_note_range (uint8_t min, uint8_t max, bool force)
1472 if (!_enable_display) {
1476 if (!force && _current_range_min == min && _current_range_max == max) {
1480 _current_range_min = min;
1481 _current_range_max = max;
1483 for (Events::const_iterator i = _events.begin(); i != _events.end(); ++i) {
1484 NoteBase* event = *i;
1485 boost::shared_ptr<NoteType> note (event->note());
1487 if (note->note() < _current_range_min ||
1488 note->note() > _current_range_max) {
1494 if (Note* cnote = dynamic_cast<Note*>(event)) {
1496 const double y0 = 1. + floor (midi_stream_view()->note_to_y(note->note()));
1497 const double y1 = y0 + std::max(1., floor(midi_stream_view()->note_height()) - 1.);
1502 } else if (Hit* chit = dynamic_cast<Hit*>(event)) {
1509 MidiRegionView::add_ghost (TimeAxisView& tv)
1511 double unit_position = _region->position () / samples_per_pixel;
1512 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(&tv);
1513 MidiGhostRegion* ghost;
1515 if (mtv && mtv->midi_view()) {
1516 /* if ghost is inserted into midi track, use a dedicated midi ghost canvas group
1517 to allow having midi notes on top of note lines and waveforms.
1519 ghost = new MidiGhostRegion (*mtv->midi_view(), trackview, unit_position);
1521 ghost = new MidiGhostRegion (tv, trackview, unit_position);
1524 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1525 ghost->add_note(*i);
1528 ghost->set_height ();
1529 ghost->set_duration (_region->length() / samples_per_pixel);
1530 ghosts.push_back (ghost);
1532 GhostRegion::CatchDeletion.connect (*this, invalidator (*this), boost::bind (&RegionView::remove_ghost, this, _1), gui_context());
1538 /** Begin tracking note state for successive calls to add_event
1541 MidiRegionView::begin_write()
1543 if (_active_notes) {
1544 delete[] _active_notes;
1546 _active_notes = new Note*[128];
1547 for (unsigned i = 0; i < 128; ++i) {
1548 _active_notes[i] = 0;
1553 /** Destroy note state for add_event
1556 MidiRegionView::end_write()
1558 delete[] _active_notes;
1560 _marked_for_selection.clear();
1561 _marked_for_velocity.clear();
1565 /** Resolve an active MIDI note (while recording).
1568 MidiRegionView::resolve_note(uint8_t note, Evoral::Beats end_time)
1570 if (midi_view()->note_mode() != Sustained) {
1574 if (_active_notes && _active_notes[note]) {
1575 /* Set note length so update_note() works. Note this is a local note
1576 for recording, not from a model, so we can safely mess with it. */
1577 _active_notes[note]->note()->set_length(
1578 end_time - _active_notes[note]->note()->time());
1580 /* End time is relative to the region being recorded. */
1581 const framepos_t end_time_frames = region_beats_to_region_frames(end_time);
1583 _active_notes[note]->set_x1 (trackview.editor().sample_to_pixel(end_time_frames));
1584 _active_notes[note]->set_outline_all ();
1585 _active_notes[note] = 0;
1590 /** Extend active notes to rightmost edge of region (if length is changed)
1593 MidiRegionView::extend_active_notes()
1595 if (!_active_notes) {
1599 for (unsigned i = 0; i < 128; ++i) {
1600 if (_active_notes[i]) {
1601 _active_notes[i]->set_x1(
1602 trackview.editor().sample_to_pixel(_region->length()));
1608 MidiRegionView::play_midi_note(boost::shared_ptr<NoteType> note)
1610 if (_no_sound_notes || !ARDOUR_UI::config()->get_sound_midi_notes()) {
1614 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1616 if (!route_ui || !route_ui->midi_track()) {
1620 NotePlayer* np = new NotePlayer (route_ui->midi_track ());
1624 /* NotePlayer deletes itself */
1628 MidiRegionView::start_playing_midi_note(boost::shared_ptr<NoteType> note)
1630 const std::vector< boost::shared_ptr<NoteType> > notes(1, note);
1631 start_playing_midi_chord(notes);
1635 MidiRegionView::start_playing_midi_chord (vector<boost::shared_ptr<NoteType> > notes)
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 _note_player = boost::shared_ptr<NotePlayer>(new NotePlayer(route_ui->midi_track()));
1649 for (vector<boost::shared_ptr<NoteType> >::iterator n = notes.begin(); n != notes.end(); ++n) {
1650 _note_player->add (*n);
1653 _note_player->on ();
1658 MidiRegionView::note_in_region_range (const boost::shared_ptr<NoteType> note, bool& visible) const
1660 /* This is imprecise due to all the conversion conversion involved, so only
1661 hide notes if they seem to start more than one tick before the start. */
1662 const framecnt_t tick_frames = Evoral::Beats::tick().to_ticks(trackview.session()->frame_rate());
1663 const framepos_t note_start_frames = source_beats_to_region_frames (note->time());
1664 const bool outside = ((note_start_frames <= -tick_frames) ||
1665 (note_start_frames > _region->last_frame()));
1667 visible = (note->note() >= midi_stream_view()->lowest_note()) &&
1668 (note->note() <= midi_stream_view()->highest_note());
1674 MidiRegionView::update_note (NoteBase* note, bool update_ghost_regions)
1678 if ((sus = dynamic_cast<Note*>(note))) {
1679 update_sustained(sus, update_ghost_regions);
1680 } else if ((hit = dynamic_cast<Hit*>(note))) {
1681 update_hit(hit, update_ghost_regions);
1685 /** Update a canvas note's size from its model note.
1686 * @param ev Canvas note to update.
1687 * @param update_ghost_regions true to update the note in any ghost regions that we have, otherwise false.
1690 MidiRegionView::update_sustained (Note* ev, bool update_ghost_regions)
1692 boost::shared_ptr<NoteType> note = ev->note();
1693 const double x = trackview.editor().sample_to_pixel (source_beats_to_region_frames (note->time()));
1694 const double y0 = 1 + floor(midi_stream_view()->note_to_y(note->note()));
1699 /* trim note display to not overlap the end of its region */
1701 if (note->length() > 0) {
1702 const framepos_t note_end_frames = min (source_beats_to_region_frames (note->end_time()), _region->length());
1703 ev->set_x1 (trackview.editor().sample_to_pixel (note_end_frames));
1705 ev->set_x1 (trackview.editor().sample_to_pixel (_region->length()));
1708 ev->set_y1 (y0 + std::max(1., floor(midi_stream_view()->note_height()) - 1));
1710 if (!note->length()) {
1711 if (_active_notes && note->note() < 128) {
1712 Note* const old_rect = _active_notes[note->note()];
1714 /* There is an active note on this key, so we have a stuck
1715 note. Finish the old rectangle here. */
1716 old_rect->set_x1 (x);
1717 old_rect->set_outline_all ();
1719 _active_notes[note->note()] = ev;
1721 /* outline all but right edge */
1722 ev->set_outline_what (ArdourCanvas::Rectangle::What (
1723 ArdourCanvas::Rectangle::TOP|
1724 ArdourCanvas::Rectangle::LEFT|
1725 ArdourCanvas::Rectangle::BOTTOM));
1727 /* outline all edges */
1728 ev->set_outline_all ();
1731 // Update color in case velocity has changed
1732 ev->set_fill_color(ev->base_color());
1733 ev->set_outline_color(ev->calculate_outline(ev->base_color(), ev->selected()));
1735 if (update_ghost_regions) {
1736 for (std::vector<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
1737 MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*i);
1739 gr->update_note (ev);
1746 MidiRegionView::update_hit (Hit* ev, bool update_ghost_regions)
1748 boost::shared_ptr<NoteType> note = ev->note();
1750 const framepos_t note_start_frames = source_beats_to_region_frames(note->time());
1751 const double x = trackview.editor().sample_to_pixel(note_start_frames);
1752 const double diamond_size = std::max(1., floor(midi_stream_view()->note_height()) - 2.);
1753 const double y = 1.5 + floor(midi_stream_view()->note_to_y(note->note())) + diamond_size * .5;
1755 ev->set_position (ArdourCanvas::Duple (x, y));
1756 ev->set_height (diamond_size);
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);
1772 /** Add a MIDI note to the view (with length).
1774 * If in sustained mode, notes with length 0 will be considered active
1775 * notes, and resolve_note should be called when the corresponding note off
1776 * event arrives, to properly display the note.
1779 MidiRegionView::add_note(const boost::shared_ptr<NoteType> note, bool visible)
1781 NoteBase* event = 0;
1783 if (midi_view()->note_mode() == Sustained) {
1785 Note* ev_rect = new Note (*this, _note_group, note);
1787 update_sustained (ev_rect);
1791 } else if (midi_view()->note_mode() == Percussive) {
1793 const double diamond_size = std::max(1., floor(midi_stream_view()->note_height()) - 2.);
1795 Hit* ev_diamond = new Hit (*this, _note_group, diamond_size, note);
1797 update_hit (ev_diamond);
1806 MidiGhostRegion* gr;
1808 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
1809 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
1810 gr->add_note(event);
1814 if (_marked_for_selection.find(note) != _marked_for_selection.end()) {
1815 note_selected(event, true);
1818 if (_marked_for_velocity.find(note) != _marked_for_velocity.end()) {
1819 event->show_velocity();
1822 event->on_channel_selection_change (get_selected_channels());
1823 _events.push_back(event);
1832 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1833 MidiStreamView* const view = mtv->midi_view();
1835 view->update_note_range (note->note());
1840 MidiRegionView::step_add_note (uint8_t channel, uint8_t number, uint8_t velocity,
1841 Evoral::Beats pos, Evoral::Beats len)
1843 boost::shared_ptr<NoteType> new_note (new NoteType (channel, pos, len, number, velocity));
1845 /* potentially extend region to hold new note */
1847 framepos_t end_frame = source_beats_to_absolute_frames (new_note->end_time());
1848 framepos_t region_end = _region->last_frame();
1850 if (end_frame > region_end) {
1851 _region->set_length (end_frame - _region->position());
1854 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1855 MidiStreamView* const view = mtv->midi_view();
1857 view->update_note_range(new_note->note());
1859 _marked_for_selection.clear ();
1861 start_note_diff_command (_("step add"));
1864 note_diff_add_note (new_note, true, false);
1868 // last_step_edit_note = new_note;
1872 MidiRegionView::step_sustain (Evoral::Beats beats)
1874 change_note_lengths (false, false, beats, false, true);
1877 /** Add a new patch change flag to the canvas.
1878 * @param patch the patch change to add
1879 * @param the text to display in the flag
1880 * @param active_channel true to display the flag as on an active channel, false to grey it out for an inactive channel.
1883 MidiRegionView::add_canvas_patch_change (MidiModel::PatchChangePtr patch, const string& displaytext, bool /*active_channel*/)
1885 framecnt_t region_frames = source_beats_to_region_frames (patch->time());
1886 const double x = trackview.editor().sample_to_pixel (region_frames);
1888 double const height = midi_stream_view()->contents_height();
1890 // CAIROCANVAS: active_channel info removed from PatcChange constructor
1891 // so we need to do something more sophisticated to keep its color
1892 // appearance (MidiPatchChangeFill/MidiPatchChangeInactiveChannelFill)
1895 boost::shared_ptr<PatchChange> patch_change = boost::shared_ptr<PatchChange>(
1896 new PatchChange(*this, group,
1903 if (patch_change->item().width() < _pixel_width) {
1904 // Show unless patch change is beyond the region bounds
1905 if (region_frames < 0 || region_frames >= _region->length()) {
1906 patch_change->hide();
1908 patch_change->show();
1911 patch_change->hide ();
1914 _patch_changes.push_back (patch_change);
1917 MIDI::Name::PatchPrimaryKey
1918 MidiRegionView::patch_change_to_patch_key (MidiModel::PatchChangePtr p)
1920 return MIDI::Name::PatchPrimaryKey (p->program(), p->bank());
1923 /// Return true iff @p pc applies to the given time on the given channel.
1925 patch_applies (const ARDOUR::MidiModel::constPatchChangePtr pc, Evoral::Beats time, uint8_t channel)
1927 return pc->time() <= time && pc->channel() == channel;
1931 MidiRegionView::get_patch_key_at (Evoral::Beats time, uint8_t channel, MIDI::Name::PatchPrimaryKey& key) const
1933 // The earliest event not before time
1934 MidiModel::PatchChanges::iterator i = _model->patch_change_lower_bound (time);
1936 // Go backwards until we find the latest PC for this channel, or the start
1937 while (i != _model->patch_changes().begin() &&
1938 (i == _model->patch_changes().end() ||
1939 !patch_applies(*i, time, channel))) {
1943 if (i != _model->patch_changes().end() && patch_applies(*i, time, channel)) {
1944 key.set_bank((*i)->bank());
1945 key.set_program((*i)->program ());
1953 MidiRegionView::change_patch_change (PatchChange& pc, const MIDI::Name::PatchPrimaryKey& new_patch)
1955 string name = _("alter patch change");
1956 trackview.editor().begin_reversible_command (name);
1957 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (name);
1959 if (pc.patch()->program() != new_patch.program()) {
1960 c->change_program (pc.patch (), new_patch.program());
1963 int const new_bank = new_patch.bank();
1964 if (pc.patch()->bank() != new_bank) {
1965 c->change_bank (pc.patch (), new_bank);
1968 _model->apply_command (*trackview.session(), c);
1969 trackview.editor().commit_reversible_command ();
1971 _patch_changes.clear ();
1972 display_patch_changes ();
1976 MidiRegionView::change_patch_change (MidiModel::PatchChangePtr old_change, const Evoral::PatchChange<Evoral::Beats> & new_change)
1978 string name = _("alter patch change");
1979 trackview.editor().begin_reversible_command (name);
1980 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (name);
1982 if (old_change->time() != new_change.time()) {
1983 c->change_time (old_change, new_change.time());
1986 if (old_change->channel() != new_change.channel()) {
1987 c->change_channel (old_change, new_change.channel());
1990 if (old_change->program() != new_change.program()) {
1991 c->change_program (old_change, new_change.program());
1994 if (old_change->bank() != new_change.bank()) {
1995 c->change_bank (old_change, new_change.bank());
1998 _model->apply_command (*trackview.session(), c);
1999 trackview.editor().commit_reversible_command ();
2001 _patch_changes.clear ();
2002 display_patch_changes ();
2005 /** Add a patch change to the region.
2006 * @param t Time in frames relative to region position
2007 * @param patch Patch to add; time and channel are ignored (time is converted from t, and channel comes from
2008 * MidiTimeAxisView::get_channel_for_add())
2011 MidiRegionView::add_patch_change (framecnt_t t, Evoral::PatchChange<Evoral::Beats> const & patch)
2013 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
2014 string name = _("add patch change");
2016 trackview.editor().begin_reversible_command (name);
2017 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (name);
2018 c->add (MidiModel::PatchChangePtr (
2019 new Evoral::PatchChange<Evoral::Beats> (
2020 absolute_frames_to_source_beats (_region->position() + t),
2021 mtv->get_channel_for_add(), patch.program(), patch.bank()
2026 _model->apply_command (*trackview.session(), c);
2027 trackview.editor().commit_reversible_command ();
2029 _patch_changes.clear ();
2030 display_patch_changes ();
2034 MidiRegionView::move_patch_change (PatchChange& pc, Evoral::Beats t)
2036 trackview.editor().begin_reversible_command (_("move patch change"));
2037 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("move patch change"));
2038 c->change_time (pc.patch (), t);
2039 _model->apply_command (*trackview.session(), c);
2040 trackview.editor().commit_reversible_command ();
2042 _patch_changes.clear ();
2043 display_patch_changes ();
2047 MidiRegionView::delete_patch_change (PatchChange* pc)
2049 trackview.editor().begin_reversible_command (_("delete patch change"));
2050 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("delete patch change"));
2051 c->remove (pc->patch ());
2052 _model->apply_command (*trackview.session(), c);
2053 trackview.editor().commit_reversible_command ();
2055 _patch_changes.clear ();
2056 display_patch_changes ();
2060 MidiRegionView::step_patch (PatchChange& patch, bool bank, int delta)
2062 MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key(patch.patch());
2064 key.set_bank(key.bank() + delta);
2066 key.set_program(key.program() + delta);
2068 change_patch_change(patch, key);
2072 MidiRegionView::maybe_remove_deleted_note_from_selection (NoteBase* cne)
2074 if (_selection.empty()) {
2078 _selection.erase (cne);
2082 MidiRegionView::delete_selection()
2084 if (_selection.empty()) {
2088 start_note_diff_command (_("delete selection"));
2090 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2091 if ((*i)->selected()) {
2092 _note_diff_command->remove((*i)->note());
2102 MidiRegionView::delete_note (boost::shared_ptr<NoteType> n)
2104 start_note_diff_command (_("delete note"));
2105 _note_diff_command->remove (n);
2108 trackview.editor().verbose_cursor()->hide ();
2112 MidiRegionView::clear_selection_except (NoteBase* ev, bool signal)
2114 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2116 Selection::iterator tmp = i;
2119 (*i)->set_selected (false);
2120 (*i)->hide_velocity ();
2121 _selection.erase (i);
2129 if (!ev && _entered) {
2130 // Clearing selection entirely, ungrab keyboard
2131 Keyboard::magic_widget_drop_focus();
2132 _grabbed_keyboard = false;
2135 /* this does not change the status of this regionview w.r.t the editor
2140 SelectionCleared (this); /* EMIT SIGNAL */
2145 MidiRegionView::unique_select(NoteBase* ev)
2147 const bool selection_was_empty = _selection.empty();
2149 clear_selection_except (ev);
2151 /* don't bother with checking to see if we should remove this
2152 regionview from the editor selection, since we're about to add
2153 another note, and thus put/keep this regionview in the editor
2157 if (!ev->selected()) {
2158 add_to_selection (ev);
2159 if (selection_was_empty && _entered) {
2160 // Grab keyboard for moving notes with arrow keys
2161 Keyboard::magic_widget_grab_focus();
2162 _grabbed_keyboard = true;
2168 MidiRegionView::select_all_notes ()
2172 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2173 add_to_selection (*i);
2178 MidiRegionView::select_range (framepos_t start, framepos_t end)
2182 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2183 framepos_t t = source_beats_to_absolute_frames((*i)->note()->time());
2184 if (t >= start && t <= end) {
2185 add_to_selection (*i);
2191 MidiRegionView::invert_selection ()
2193 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2194 if ((*i)->selected()) {
2195 remove_from_selection(*i);
2197 add_to_selection (*i);
2202 /** Used for selection undo/redo.
2203 The requested notes most likely won't exist in the view until the next model redisplay.
2206 MidiRegionView::select_notes (list<boost::shared_ptr<NoteType> > notes)
2209 list<boost::shared_ptr<NoteType> >::iterator n;
2211 for (n = notes.begin(); n != notes.end(); ++n) {
2212 if ((cne = find_canvas_note(*(*n))) != 0) {
2213 add_to_selection (cne);
2215 _pending_note_selection.insert(*n);
2221 MidiRegionView::select_matching_notes (uint8_t notenum, uint16_t channel_mask, bool add, bool extend)
2223 bool have_selection = !_selection.empty();
2224 uint8_t low_note = 127;
2225 uint8_t high_note = 0;
2226 MidiModel::Notes& notes (_model->notes());
2227 _optimization_iterator = _events.begin();
2229 if (extend && !have_selection) {
2233 /* scan existing selection to get note range */
2235 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2236 if ((*i)->note()->note() < low_note) {
2237 low_note = (*i)->note()->note();
2239 if ((*i)->note()->note() > high_note) {
2240 high_note = (*i)->note()->note();
2247 if (!extend && (low_note == high_note) && (high_note == notenum)) {
2248 /* only note previously selected is the one we are
2249 * reselecting. treat this as cancelling the selection.
2256 low_note = min (low_note, notenum);
2257 high_note = max (high_note, notenum);
2260 _no_sound_notes = true;
2262 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
2264 boost::shared_ptr<NoteType> note (*n);
2266 bool select = false;
2268 if (((1 << note->channel()) & channel_mask) != 0) {
2270 if ((note->note() >= low_note && note->note() <= high_note)) {
2273 } else if (note->note() == notenum) {
2279 if ((cne = find_canvas_note (note)) != 0) {
2280 // extend is false because we've taken care of it,
2281 // since it extends by time range, not pitch.
2282 note_selected (cne, add, false);
2286 add = true; // we need to add all remaining matching notes, even if the passed in value was false (for "set")
2290 _no_sound_notes = false;
2294 MidiRegionView::toggle_matching_notes (uint8_t notenum, uint16_t channel_mask)
2296 MidiModel::Notes& notes (_model->notes());
2297 _optimization_iterator = _events.begin();
2299 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
2301 boost::shared_ptr<NoteType> note (*n);
2304 if (note->note() == notenum && (((0x0001 << note->channel()) & channel_mask) != 0)) {
2305 if ((cne = find_canvas_note (note)) != 0) {
2306 if (cne->selected()) {
2307 note_deselected (cne);
2309 note_selected (cne, true, false);
2317 MidiRegionView::note_selected (NoteBase* ev, bool add, bool extend)
2320 clear_selection_except (ev);
2321 if (!_selection.empty()) {
2322 PublicEditor& editor (trackview.editor());
2323 editor.get_selection().add (this);
2329 if (!ev->selected()) {
2330 add_to_selection (ev);
2334 /* find end of latest note selected, select all between that and the start of "ev" */
2336 Evoral::Beats earliest = Evoral::MaxBeats;
2337 Evoral::Beats latest = Evoral::Beats();
2339 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2340 if ((*i)->note()->end_time() > latest) {
2341 latest = (*i)->note()->end_time();
2343 if ((*i)->note()->time() < earliest) {
2344 earliest = (*i)->note()->time();
2348 if (ev->note()->end_time() > latest) {
2349 latest = ev->note()->end_time();
2352 if (ev->note()->time() < earliest) {
2353 earliest = ev->note()->time();
2356 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2358 /* find notes entirely within OR spanning the earliest..latest range */
2360 if (((*i)->note()->time() >= earliest && (*i)->note()->end_time() <= latest) ||
2361 ((*i)->note()->time() <= earliest && (*i)->note()->end_time() >= latest)) {
2362 add_to_selection (*i);
2370 MidiRegionView::note_deselected(NoteBase* ev)
2372 remove_from_selection (ev);
2376 MidiRegionView::update_drag_selection(framepos_t start, framepos_t end, double gy0, double gy1, bool extend)
2378 PublicEditor& editor = trackview.editor();
2380 // Convert to local coordinates
2381 const framepos_t p = _region->position();
2382 const double y = midi_view()->y_position();
2383 const double x0 = editor.sample_to_pixel(max((framepos_t)0, start - p));
2384 const double x1 = editor.sample_to_pixel(max((framepos_t)0, end - p));
2385 const double y0 = max(0.0, gy0 - y);
2386 const double y1 = max(0.0, gy1 - y);
2388 // TODO: Make this faster by storing the last updated selection rect, and only
2389 // adjusting things that are in the area that appears/disappeared.
2390 // We probably need a tree to be able to find events in O(log(n)) time.
2392 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2393 if ((*i)->x0() < x1 && (*i)->x1() > x0 && (*i)->y0() < y1 && (*i)->y1() > y0) {
2394 // Rectangles intersect
2395 if (!(*i)->selected()) {
2396 add_to_selection (*i);
2398 } else if ((*i)->selected() && !extend) {
2399 // Rectangles do not intersect
2400 remove_from_selection (*i);
2404 typedef RouteTimeAxisView::AutomationTracks ATracks;
2405 typedef std::list<Selectable*> Selectables;
2407 /* Add control points to selection. */
2408 const ATracks& atracks = midi_view()->automation_tracks();
2409 Selectables selectables;
2410 editor.get_selection().clear_points();
2411 for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) {
2412 a->second->get_selectables(start, end, gy0, gy1, selectables);
2413 for (Selectables::const_iterator s = selectables.begin(); s != selectables.end(); ++s) {
2414 ControlPoint* cp = dynamic_cast<ControlPoint*>(*s);
2416 editor.get_selection().add(cp);
2419 a->second->set_selected_points(editor.get_selection().points);
2424 MidiRegionView::update_vertical_drag_selection (double y1, double y2, bool extend)
2430 // TODO: Make this faster by storing the last updated selection rect, and only
2431 // adjusting things that are in the area that appears/disappeared.
2432 // We probably need a tree to be able to find events in O(log(n)) time.
2434 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2435 if (((*i)->y1() >= y1 && (*i)->y1() <= y2)) {
2436 // within y- (note-) range
2437 if (!(*i)->selected()) {
2438 add_to_selection (*i);
2440 } else if ((*i)->selected() && !extend) {
2441 remove_from_selection (*i);
2447 MidiRegionView::remove_from_selection (NoteBase* ev)
2449 Selection::iterator i = _selection.find (ev);
2451 if (i != _selection.end()) {
2452 _selection.erase (i);
2453 if (_selection.empty() && _grabbed_keyboard) {
2455 Keyboard::magic_widget_drop_focus();
2456 _grabbed_keyboard = false;
2460 ev->set_selected (false);
2461 ev->hide_velocity ();
2463 if (_selection.empty()) {
2464 PublicEditor& editor (trackview.editor());
2465 editor.get_selection().remove (this);
2470 MidiRegionView::add_to_selection (NoteBase* ev)
2472 const bool selection_was_empty = _selection.empty();
2474 if (_selection.insert (ev).second) {
2475 ev->set_selected (true);
2476 start_playing_midi_note ((ev)->note());
2477 if (selection_was_empty && _entered) {
2478 // Grab keyboard for moving notes with arrow keys
2479 Keyboard::magic_widget_grab_focus();
2480 _grabbed_keyboard = true;
2484 if (selection_was_empty) {
2485 PublicEditor& editor (trackview.editor());
2486 editor.get_selection().add (this);
2491 MidiRegionView::move_selection(double dx, double dy, double cumulative_dy)
2493 typedef vector<boost::shared_ptr<NoteType> > PossibleChord;
2494 PossibleChord to_play;
2495 Evoral::Beats earliest = Evoral::MaxBeats;
2497 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2498 if ((*i)->note()->time() < earliest) {
2499 earliest = (*i)->note()->time();
2503 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2504 if ((*i)->note()->time() == earliest) {
2505 to_play.push_back ((*i)->note());
2507 (*i)->move_event(dx, dy);
2510 if (dy && !_selection.empty() && !_no_sound_notes && ARDOUR_UI::config()->get_sound_midi_notes()) {
2512 if (to_play.size() > 1) {
2514 PossibleChord shifted;
2516 for (PossibleChord::iterator n = to_play.begin(); n != to_play.end(); ++n) {
2517 boost::shared_ptr<NoteType> moved_note (new NoteType (**n));
2518 moved_note->set_note (moved_note->note() + cumulative_dy);
2519 shifted.push_back (moved_note);
2522 start_playing_midi_chord (shifted);
2524 } else if (!to_play.empty()) {
2526 boost::shared_ptr<NoteType> moved_note (new NoteType (*to_play.front()));
2527 moved_note->set_note (moved_note->note() + cumulative_dy);
2528 start_playing_midi_note (moved_note);
2534 MidiRegionView::note_dropped(NoteBase *, frameoffset_t dt, int8_t dnote)
2536 uint8_t lowest_note_in_selection = 127;
2537 uint8_t highest_note_in_selection = 0;
2538 uint8_t highest_note_difference = 0;
2540 // find highest and lowest notes first
2542 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2543 uint8_t pitch = (*i)->note()->note();
2544 lowest_note_in_selection = std::min(lowest_note_in_selection, pitch);
2545 highest_note_in_selection = std::max(highest_note_in_selection, pitch);
2549 cerr << "dnote: " << (int) dnote << endl;
2550 cerr << "lowest note (streamview): " << int(midi_stream_view()->lowest_note())
2551 << " highest note (streamview): " << int(midi_stream_view()->highest_note()) << endl;
2552 cerr << "lowest note (selection): " << int(lowest_note_in_selection) << " highest note(selection): "
2553 << int(highest_note_in_selection) << endl;
2554 cerr << "selection size: " << _selection.size() << endl;
2555 cerr << "Highest note in selection: " << (int) highest_note_in_selection << endl;
2558 // Make sure the note pitch does not exceed the MIDI standard range
2559 if (highest_note_in_selection + dnote > 127) {
2560 highest_note_difference = highest_note_in_selection - 127;
2563 start_note_diff_command (_("move notes"));
2565 for (Selection::iterator i = _selection.begin(); i != _selection.end() ; ++i) {
2567 framepos_t new_frames = source_beats_to_absolute_frames ((*i)->note()->time()) + dt;
2568 Evoral::Beats new_time = absolute_frames_to_source_beats (new_frames);
2574 note_diff_add_change (*i, MidiModel::NoteDiffCommand::StartTime, new_time);
2576 uint8_t original_pitch = (*i)->note()->note();
2577 uint8_t new_pitch = original_pitch + dnote - highest_note_difference;
2579 // keep notes in standard midi range
2580 clamp_to_0_127(new_pitch);
2582 lowest_note_in_selection = std::min(lowest_note_in_selection, new_pitch);
2583 highest_note_in_selection = std::max(highest_note_in_selection, new_pitch);
2585 note_diff_add_change (*i, MidiModel::NoteDiffCommand::NoteNumber, new_pitch);
2590 // care about notes being moved beyond the upper/lower bounds on the canvas
2591 if (lowest_note_in_selection < midi_stream_view()->lowest_note() ||
2592 highest_note_in_selection > midi_stream_view()->highest_note()) {
2593 midi_stream_view()->set_note_range(MidiStreamView::ContentsRange);
2597 /** @param x Pixel relative to the region position.
2598 * @return Snapped frame relative to the region position.
2601 MidiRegionView::snap_pixel_to_sample(double x)
2603 PublicEditor& editor (trackview.editor());
2604 return snap_frame_to_frame (editor.pixel_to_sample (x));
2607 /** @param x Pixel relative to the region position.
2608 * @return Snapped pixel relative to the region position.
2611 MidiRegionView::snap_to_pixel(double x)
2613 return (double) trackview.editor().sample_to_pixel(snap_pixel_to_sample(x));
2617 MidiRegionView::get_position_pixels()
2619 framepos_t region_frame = get_position();
2620 return trackview.editor().sample_to_pixel(region_frame);
2624 MidiRegionView::get_end_position_pixels()
2626 framepos_t frame = get_position() + get_duration ();
2627 return trackview.editor().sample_to_pixel(frame);
2631 MidiRegionView::source_beats_to_absolute_frames(Evoral::Beats beats) const
2633 /* the time converter will return the frame corresponding to `beats'
2634 relative to the start of the source. The start of the source
2635 is an implied position given by region->position - region->start
2637 const framepos_t source_start = _region->position() - _region->start();
2638 return source_start + _source_relative_time_converter.to (beats);
2642 MidiRegionView::absolute_frames_to_source_beats(framepos_t frames) const
2644 /* the `frames' argument needs to be converted into a frame count
2645 relative to the start of the source before being passed in to the
2648 const framepos_t source_start = _region->position() - _region->start();
2649 return _source_relative_time_converter.from (frames - source_start);
2653 MidiRegionView::region_beats_to_region_frames(Evoral::Beats beats) const
2655 return _region_relative_time_converter.to(beats);
2659 MidiRegionView::region_frames_to_region_beats(framepos_t frames) const
2661 return _region_relative_time_converter.from(frames);
2665 MidiRegionView::begin_resizing (bool /*at_front*/)
2667 _resize_data.clear();
2669 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2670 Note *note = dynamic_cast<Note*> (*i);
2672 // only insert CanvasNotes into the map
2674 NoteResizeData *resize_data = new NoteResizeData();
2675 resize_data->note = note;
2677 // create a new SimpleRect from the note which will be the resize preview
2678 ArdourCanvas::Rectangle *resize_rect = new ArdourCanvas::Rectangle (_note_group,
2679 ArdourCanvas::Rect (note->x0(), note->y0(), note->x0(), note->y1()));
2681 // calculate the colors: get the color settings
2682 uint32_t fill_color = UINT_RGBA_CHANGE_A(
2683 ARDOUR_UI::config()->color ("midi note selected"),
2686 // make the resize preview notes more transparent and bright
2687 fill_color = UINT_INTERPOLATE(fill_color, 0xFFFFFF40, 0.5);
2689 // calculate color based on note velocity
2690 resize_rect->set_fill_color (UINT_INTERPOLATE(
2691 NoteBase::meter_style_fill_color(note->note()->velocity(), note->selected()),
2695 resize_rect->set_outline_color (NoteBase::calculate_outline (
2696 ARDOUR_UI::config()->color ("midi note selected")));
2698 resize_data->resize_rect = resize_rect;
2699 _resize_data.push_back(resize_data);
2704 /** Update resizing notes while user drags.
2705 * @param primary `primary' note for the drag; ie the one that is used as the reference in non-relative mode.
2706 * @param at_front which end of the note (true == note on, false == note off)
2707 * @param delta_x change in mouse position since the start of the drag
2708 * @param relative true if relative resizing is taking place, false if absolute resizing. This only makes
2709 * a difference when multiple notes are being resized; in relative mode, each note's length is changed by the
2710 * amount of the drag. In non-relative mode, all selected notes are set to have the same start or end point
2711 * as the \a primary note.
2714 MidiRegionView::update_resizing (NoteBase* primary, bool at_front, double delta_x, bool relative)
2716 bool cursor_set = false;
2718 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2719 ArdourCanvas::Rectangle* resize_rect = (*i)->resize_rect;
2720 Note* canvas_note = (*i)->note;
2725 current_x = canvas_note->x0() + delta_x;
2727 current_x = primary->x0() + delta_x;
2731 current_x = canvas_note->x1() + delta_x;
2733 current_x = primary->x1() + delta_x;
2737 if (current_x < 0) {
2738 // This works even with snapping because RegionView::snap_frame_to_frame()
2739 // snaps forward if the snapped sample is before the beginning of the region
2742 if (current_x > trackview.editor().sample_to_pixel(_region->length())) {
2743 current_x = trackview.editor().sample_to_pixel(_region->length());
2747 resize_rect->set_x0 (snap_to_pixel(current_x));
2748 resize_rect->set_x1 (canvas_note->x1());
2750 resize_rect->set_x1 (snap_to_pixel(current_x));
2751 resize_rect->set_x0 (canvas_note->x0());
2755 const double snapped_x = snap_pixel_to_sample (current_x);
2756 Evoral::Beats beats = region_frames_to_region_beats (snapped_x);
2757 Evoral::Beats len = Evoral::Beats();
2760 if (beats < canvas_note->note()->end_time()) {
2761 len = canvas_note->note()->time() - beats;
2762 len += canvas_note->note()->length();
2765 if (beats >= canvas_note->note()->time()) {
2766 len = beats - canvas_note->note()->time();
2770 len = std::max(Evoral::Beats(1 / 512.0), len);
2773 snprintf (buf, sizeof (buf), "%.3g beats", len.to_double());
2774 show_verbose_cursor (buf, 0, 0);
2783 /** Finish resizing notes when the user releases the mouse button.
2784 * Parameters the same as for \a update_resizing().
2787 MidiRegionView::commit_resizing (NoteBase* primary, bool at_front, double delta_x, bool relative)
2789 _note_diff_command = _model->new_note_diff_command (_("resize notes"));
2791 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2792 Note* canvas_note = (*i)->note;
2793 ArdourCanvas::Rectangle* resize_rect = (*i)->resize_rect;
2795 /* Get the new x position for this resize, which is in pixels relative
2796 * to the region position.
2803 current_x = canvas_note->x0() + delta_x;
2805 current_x = primary->x0() + delta_x;
2809 current_x = canvas_note->x1() + delta_x;
2811 current_x = primary->x1() + delta_x;
2815 if (current_x < 0) {
2818 if (current_x > trackview.editor().sample_to_pixel(_region->length())) {
2819 current_x = trackview.editor().sample_to_pixel(_region->length());
2822 /* Convert that to a frame within the source */
2823 current_x = snap_pixel_to_sample (current_x) + _region->start ();
2825 /* and then to beats */
2826 const Evoral::Beats x_beats = region_frames_to_region_beats (current_x);
2828 if (at_front && x_beats < canvas_note->note()->end_time()) {
2829 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::StartTime, x_beats);
2831 Evoral::Beats len = canvas_note->note()->time() - x_beats;
2832 len += canvas_note->note()->length();
2835 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
2840 const Evoral::Beats len = std::max(Evoral::Beats(1 / 512.0),
2841 x_beats - canvas_note->note()->time());
2842 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
2849 _resize_data.clear();
2854 MidiRegionView::abort_resizing ()
2856 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2857 delete (*i)->resize_rect;
2861 _resize_data.clear ();
2865 MidiRegionView::change_note_velocity(NoteBase* event, int8_t velocity, bool relative)
2867 uint8_t new_velocity;
2870 new_velocity = event->note()->velocity() + velocity;
2871 clamp_to_0_127(new_velocity);
2873 new_velocity = velocity;
2876 event->set_selected (event->selected()); // change color
2878 note_diff_add_change (event, MidiModel::NoteDiffCommand::Velocity, new_velocity);
2882 MidiRegionView::change_note_note (NoteBase* event, int8_t note, bool relative)
2887 new_note = event->note()->note() + note;
2892 clamp_to_0_127 (new_note);
2893 note_diff_add_change (event, MidiModel::NoteDiffCommand::NoteNumber, new_note);
2897 MidiRegionView::trim_note (NoteBase* event, Evoral::Beats front_delta, Evoral::Beats end_delta)
2899 bool change_start = false;
2900 bool change_length = false;
2901 Evoral::Beats new_start;
2902 Evoral::Beats new_length;
2904 /* NOTE: the semantics of the two delta arguments are slightly subtle:
2906 front_delta: if positive - move the start of the note later in time (shortening it)
2907 if negative - move the start of the note earlier in time (lengthening it)
2909 end_delta: if positive - move the end of the note later in time (lengthening it)
2910 if negative - move the end of the note earlier in time (shortening it)
2913 if (!!front_delta) {
2914 if (front_delta < 0) {
2916 if (event->note()->time() < -front_delta) {
2917 new_start = Evoral::Beats();
2919 new_start = event->note()->time() + front_delta; // moves earlier
2922 /* start moved toward zero, so move the end point out to where it used to be.
2923 Note that front_delta is negative, so this increases the length.
2926 new_length = event->note()->length() - front_delta;
2927 change_start = true;
2928 change_length = true;
2932 Evoral::Beats new_pos = event->note()->time() + front_delta;
2934 if (new_pos < event->note()->end_time()) {
2935 new_start = event->note()->time() + front_delta;
2936 /* start moved toward the end, so move the end point back to where it used to be */
2937 new_length = event->note()->length() - front_delta;
2938 change_start = true;
2939 change_length = true;
2946 bool can_change = true;
2947 if (end_delta < 0) {
2948 if (event->note()->length() < -end_delta) {
2954 new_length = event->note()->length() + end_delta;
2955 change_length = true;
2960 note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_start);
2963 if (change_length) {
2964 note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, new_length);
2969 MidiRegionView::change_note_channel (NoteBase* event, int8_t chn, bool relative)
2971 uint8_t new_channel;
2975 if (event->note()->channel() < -chn) {
2978 new_channel = event->note()->channel() + chn;
2981 new_channel = event->note()->channel() + chn;
2984 new_channel = (uint8_t) chn;
2987 note_diff_add_change (event, MidiModel::NoteDiffCommand::Channel, new_channel);
2991 MidiRegionView::change_note_time (NoteBase* event, Evoral::Beats delta, bool relative)
2993 Evoral::Beats new_time;
2997 if (event->note()->time() < -delta) {
2998 new_time = Evoral::Beats();
3000 new_time = event->note()->time() + delta;
3003 new_time = event->note()->time() + delta;
3009 note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_time);
3013 MidiRegionView::change_note_length (NoteBase* event, Evoral::Beats t)
3015 note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, t);
3019 MidiRegionView::change_velocities (bool up, bool fine, bool allow_smush, bool all_together)
3024 if (_selection.empty()) {
3039 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3040 if ((*i)->note()->velocity() < -delta || (*i)->note()->velocity() + delta > 127) {
3046 start_note_diff_command (_("change velocities"));
3048 for (Selection::iterator i = _selection.begin(); i != _selection.end();) {
3049 Selection::iterator next = i;
3053 if (i == _selection.begin()) {
3054 change_note_velocity (*i, delta, true);
3055 value = (*i)->note()->velocity() + delta;
3057 change_note_velocity (*i, value, false);
3061 change_note_velocity (*i, delta, true);
3070 if (!_selection.empty()) {
3072 snprintf (buf, sizeof (buf), "Vel %d",
3073 (int) (*_selection.begin())->note()->velocity());
3074 show_verbose_cursor (buf, 10, 10);
3080 MidiRegionView::transpose (bool up, bool fine, bool allow_smush)
3082 if (_selection.empty()) {
3099 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3101 if ((int8_t) (*i)->note()->note() + delta <= 0) {
3105 if ((int8_t) (*i)->note()->note() + delta > 127) {
3112 start_note_diff_command (_("transpose"));
3114 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3115 Selection::iterator next = i;
3117 change_note_note (*i, delta, true);
3125 MidiRegionView::change_note_lengths (bool fine, bool shorter, Evoral::Beats delta, bool start, bool end)
3129 delta = Evoral::Beats(1.0/128.0);
3131 /* grab the current grid distance */
3132 delta = get_grid_beats(_region->position());
3140 start_note_diff_command (_("change note lengths"));
3142 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3143 Selection::iterator next = i;
3146 /* note the negation of the delta for start */
3149 (start ? -delta : Evoral::Beats()),
3150 (end ? delta : Evoral::Beats()));
3159 MidiRegionView::nudge_notes (bool forward, bool fine)
3161 if (_selection.empty()) {
3165 /* pick a note as the point along the timeline to get the nudge distance.
3166 its not necessarily the earliest note, so we may want to pull the notes out
3167 into a vector and sort before using the first one.
3170 const framepos_t ref_point = source_beats_to_absolute_frames ((*(_selection.begin()))->note()->time());
3171 Evoral::Beats delta;
3175 /* non-fine, move by 1 bar regardless of snap */
3176 delta = Evoral::Beats(trackview.session()->tempo_map().meter_at(ref_point).divisions_per_bar());
3178 } else if (trackview.editor().snap_mode() == Editing::SnapOff) {
3180 /* grid is off - use nudge distance */
3183 const framecnt_t distance = trackview.editor().get_nudge_distance (ref_point, unused);
3184 delta = region_frames_to_region_beats (fabs ((double)distance));
3190 framepos_t next_pos = ref_point;
3193 if (max_framepos - 1 < next_pos) {
3197 if (next_pos == 0) {
3203 trackview.editor().snap_to (next_pos, (forward ? RoundUpAlways : RoundDownAlways), false);
3204 const framecnt_t distance = ref_point - next_pos;
3205 delta = region_frames_to_region_beats (fabs ((double)distance));
3216 start_note_diff_command (_("nudge"));
3218 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3219 Selection::iterator next = i;
3221 change_note_time (*i, delta, true);
3229 MidiRegionView::change_channel(uint8_t channel)
3231 start_note_diff_command(_("change channel"));
3232 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3233 note_diff_add_change (*i, MidiModel::NoteDiffCommand::Channel, channel);
3241 MidiRegionView::note_entered(NoteBase* ev)
3243 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3245 if (_mouse_state == SelectTouchDragging) {
3246 note_selected (ev, true);
3247 } else if (editor->current_mouse_mode() == MouseContent) {
3248 show_verbose_cursor (ev->note ());
3249 } else if (editor->current_mouse_mode() == MouseDraw) {
3250 show_verbose_cursor (ev->note ());
3255 MidiRegionView::note_left (NoteBase*)
3257 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3259 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3260 (*i)->hide_velocity ();
3263 editor->verbose_cursor()->hide ();
3267 MidiRegionView::patch_entered (PatchChange* p)
3270 /* XXX should get patch name if we can */
3271 s << _("Bank ") << (p->patch()->bank() + MIDI_BP_ZERO) << '\n'
3272 << _("Program ") << ((int) p->patch()->program()) + MIDI_BP_ZERO << '\n'
3273 << _("Channel ") << ((int) p->patch()->channel() + 1);
3274 show_verbose_cursor (s.str(), 10, 20);
3275 p->item().grab_focus();
3279 MidiRegionView::patch_left (PatchChange *)
3281 trackview.editor().verbose_cursor()->hide ();
3282 /* focus will transfer back via the enter-notify event sent to this
3288 MidiRegionView::sysex_entered (SysEx* p)
3292 // need a way to extract text from p->_flag->_text
3294 // show_verbose_cursor (s.str(), 10, 20);
3295 p->item().grab_focus();
3299 MidiRegionView::sysex_left (SysEx *)
3301 trackview.editor().verbose_cursor()->hide ();
3302 /* focus will transfer back via the enter-notify event sent to this
3308 MidiRegionView::note_mouse_position (float x_fraction, float /*y_fraction*/, bool can_set_cursor)
3310 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3311 Editing::MouseMode mm = editor->current_mouse_mode();
3312 bool trimmable = (mm == MouseContent || mm == MouseTimeFX || mm == MouseDraw);
3314 Editor::EnterContext* ctx = editor->get_enter_context(NoteItem);
3315 if (can_set_cursor && ctx) {
3316 if (trimmable && x_fraction > 0.0 && x_fraction < 0.2) {
3317 ctx->cursor_ctx->change(editor->cursors()->left_side_trim);
3318 } else if (trimmable && x_fraction >= 0.8 && x_fraction < 1.0) {
3319 ctx->cursor_ctx->change(editor->cursors()->right_side_trim);
3321 ctx->cursor_ctx->change(editor->cursors()->grabber_note);
3327 MidiRegionView::get_fill_color() const
3329 const std::string mod_name = (_dragging ? "dragging region" :
3330 trackview.editor().internal_editing() ? "editable region" :
3333 return ARDOUR_UI::config()->color_mod ("selected region base", mod_name);
3334 } else if ((!ARDOUR_UI::config()->get_show_name_highlight() || high_enough_for_name) &&
3335 !ARDOUR_UI::config()->get_color_regions_using_track_color()) {
3336 return ARDOUR_UI::config()->color_mod ("midi frame base", mod_name);
3338 return ARDOUR_UI::config()->color_mod (fill_color, mod_name);
3342 MidiRegionView::midi_channel_mode_changed ()
3344 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3345 uint16_t mask = mtv->midi_track()->get_playback_channel_mask();
3346 ChannelMode mode = mtv->midi_track()->get_playback_channel_mode ();
3348 if (mode == ForceChannel) {
3349 mask = 0xFFFF; // Show all notes as active (below)
3352 // Update notes for selection
3353 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3354 (*i)->on_channel_selection_change (mask);
3357 _patch_changes.clear ();
3358 display_patch_changes ();
3362 MidiRegionView::instrument_settings_changed ()
3368 MidiRegionView::cut_copy_clear (Editing::CutCopyOp op)
3370 if (_selection.empty()) {
3374 PublicEditor& editor (trackview.editor());
3378 /* XXX what to do ? */
3382 editor.get_cut_buffer().add (selection_as_cut_buffer());
3390 start_note_diff_command();
3392 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3399 note_diff_remove_note (*i);
3409 MidiRegionView::selection_as_cut_buffer () const
3413 for (Selection::const_iterator i = _selection.begin(); i != _selection.end(); ++i) {
3414 NoteType* n = (*i)->note().get();
3415 notes.insert (boost::shared_ptr<NoteType> (new NoteType (*n)));
3418 MidiCutBuffer* cb = new MidiCutBuffer (trackview.session());
3424 /** This method handles undo */
3426 MidiRegionView::paste (framepos_t pos, const ::Selection& selection, PasteContext& ctx)
3428 bool commit = false;
3429 // Paste notes, if available
3430 MidiNoteSelection::const_iterator m = selection.midi_notes.get_nth(ctx.counts.n_notes());
3431 if (m != selection.midi_notes.end()) {
3432 ctx.counts.increase_n_notes();
3433 if (!(*m)->empty()) { commit = true; }
3434 paste_internal(pos, ctx.count, ctx.times, **m);
3437 // Paste control points to automation children, if available
3438 typedef RouteTimeAxisView::AutomationTracks ATracks;
3439 const ATracks& atracks = midi_view()->automation_tracks();
3440 for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) {
3441 if (a->second->paste(pos, selection, ctx)) {
3447 trackview.editor().commit_reversible_command ();
3452 /** This method handles undo */
3454 MidiRegionView::paste_internal (framepos_t pos, unsigned paste_count, float times, const MidiCutBuffer& mcb)
3460 start_note_diff_command (_("paste"));
3462 const Evoral::Beats snap_beats = get_grid_beats(pos);
3463 const Evoral::Beats first_time = (*mcb.notes().begin())->time();
3464 const Evoral::Beats last_time = (*mcb.notes().rbegin())->end_time();
3465 const Evoral::Beats duration = last_time - first_time;
3466 const Evoral::Beats snap_duration = duration.snap_to(snap_beats);
3467 const Evoral::Beats paste_offset = snap_duration * paste_count;
3468 const Evoral::Beats pos_beats = absolute_frames_to_source_beats(pos) + paste_offset;
3469 Evoral::Beats end_point = Evoral::Beats();
3471 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste data spans from %1 to %2 (%3) ; paste pos beats = %4 (based on %5 - %6)\n",
3474 duration, pos, _region->position(),
3479 for (int n = 0; n < (int) times; ++n) {
3481 for (Notes::const_iterator i = mcb.notes().begin(); i != mcb.notes().end(); ++i) {
3483 boost::shared_ptr<NoteType> copied_note (new NoteType (*((*i).get())));
3484 copied_note->set_time (pos_beats + copied_note->time() - first_time);
3486 /* make all newly added notes selected */
3488 note_diff_add_note (copied_note, true);
3489 end_point = copied_note->end_time();
3493 /* if we pasted past the current end of the region, extend the region */
3495 framepos_t end_frame = source_beats_to_absolute_frames (end_point);
3496 framepos_t region_end = _region->position() + _region->length() - 1;
3498 if (end_frame > region_end) {
3500 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste extended region from %1 to %2\n", region_end, end_frame));
3502 _region->clear_changes ();
3503 _region->set_length (end_frame - _region->position());
3504 trackview.session()->add_command (new StatefulDiffCommand (_region));
3510 struct EventNoteTimeEarlyFirstComparator {
3511 bool operator() (NoteBase* a, NoteBase* b) {
3512 return a->note()->time() < b->note()->time();
3517 MidiRegionView::time_sort_events ()
3519 if (!_sort_needed) {
3523 EventNoteTimeEarlyFirstComparator cmp;
3526 _sort_needed = false;
3530 MidiRegionView::goto_next_note (bool add_to_selection)
3532 bool use_next = false;
3534 if (_events.back()->selected()) {
3538 time_sort_events ();
3540 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3541 uint16_t const channel_mask = mtv->midi_track()->get_playback_channel_mask();
3543 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3544 if ((*i)->selected()) {
3547 } else if (use_next) {
3548 if (channel_mask & (1 << (*i)->note()->channel())) {
3549 if (!add_to_selection) {
3552 note_selected (*i, true, false);
3559 /* use the first one */
3561 if (!_events.empty() && (channel_mask & (1 << _events.front()->note()->channel ()))) {
3562 unique_select (_events.front());
3567 MidiRegionView::goto_previous_note (bool add_to_selection)
3569 bool use_next = false;
3571 if (_events.front()->selected()) {
3575 time_sort_events ();
3577 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3578 uint16_t const channel_mask = mtv->midi_track()->get_playback_channel_mask ();
3580 for (Events::reverse_iterator i = _events.rbegin(); i != _events.rend(); ++i) {
3581 if ((*i)->selected()) {
3584 } else if (use_next) {
3585 if (channel_mask & (1 << (*i)->note()->channel())) {
3586 if (!add_to_selection) {
3589 note_selected (*i, true, false);
3596 /* use the last one */
3598 if (!_events.empty() && (channel_mask & (1 << (*_events.rbegin())->note()->channel ()))) {
3599 unique_select (*(_events.rbegin()));
3604 MidiRegionView::selection_as_notelist (Notes& selected, bool allow_all_if_none_selected)
3606 bool had_selected = false;
3608 time_sort_events ();
3610 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3611 if ((*i)->selected()) {
3612 selected.insert ((*i)->note());
3613 had_selected = true;
3617 if (allow_all_if_none_selected && !had_selected) {
3618 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3619 selected.insert ((*i)->note());
3625 MidiRegionView::update_ghost_note (double x, double y)
3627 x = std::max(0.0, x);
3629 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3634 _note_group->canvas_to_item (x, y);
3636 PublicEditor& editor = trackview.editor ();
3638 framepos_t const unsnapped_frame = editor.pixel_to_sample (x);
3639 framecnt_t grid_frames;
3640 framepos_t const f = snap_frame_to_grid_underneath (unsnapped_frame, grid_frames);
3642 /* calculate time in beats relative to start of source */
3643 const Evoral::Beats length = get_grid_beats(unsnapped_frame);
3644 const Evoral::Beats time = std::max(
3646 absolute_frames_to_source_beats (f + _region->position ()));
3648 _ghost_note->note()->set_time (time);
3649 _ghost_note->note()->set_length (length);
3650 _ghost_note->note()->set_note (midi_stream_view()->y_to_note (y));
3651 _ghost_note->note()->set_channel (mtv->get_channel_for_add ());
3652 _ghost_note->note()->set_velocity (get_velocity_for_add (time));
3654 /* the ghost note does not appear in ghost regions, so pass false in here */
3655 update_note (_ghost_note, false);
3657 show_verbose_cursor (_ghost_note->note ());
3661 MidiRegionView::create_ghost_note (double x, double y)
3663 remove_ghost_note ();
3665 boost::shared_ptr<NoteType> g (new NoteType);
3666 if (midi_view()->note_mode() == Sustained) {
3667 _ghost_note = new Note (*this, _note_group, g);
3669 _ghost_note = new Hit (*this, _note_group, 10, g);
3671 _ghost_note->set_ignore_events (true);
3672 _ghost_note->set_outline_color (0x000000aa);
3673 update_ghost_note (x, y);
3674 _ghost_note->show ();
3676 show_verbose_cursor (_ghost_note->note ());
3680 MidiRegionView::remove_ghost_note ()
3687 MidiRegionView::snap_changed ()
3693 create_ghost_note (_last_ghost_x, _last_ghost_y);
3697 MidiRegionView::drop_down_keys ()
3699 _mouse_state = None;
3703 MidiRegionView::maybe_select_by_position (GdkEventButton* ev, double /*x*/, double y)
3705 /* XXX: This is dead code. What was it for? */
3707 double note = midi_stream_view()->y_to_note(y);
3709 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3711 uint16_t chn_mask = mtv->midi_track()->get_playback_channel_mask();
3713 if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
3714 get_events (e, Evoral::Sequence<Evoral::Beats>::PitchGreaterThanOrEqual, (uint8_t) floor (note), chn_mask);
3715 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
3716 get_events (e, Evoral::Sequence<Evoral::Beats>::PitchLessThanOrEqual, (uint8_t) floor (note), chn_mask);
3721 bool add_mrv_selection = false;
3723 if (_selection.empty()) {
3724 add_mrv_selection = true;
3727 for (Events::iterator i = e.begin(); i != e.end(); ++i) {
3728 if (_selection.insert (*i).second) {
3729 (*i)->set_selected (true);
3733 if (add_mrv_selection) {
3734 PublicEditor& editor (trackview.editor());
3735 editor.get_selection().add (this);
3740 MidiRegionView::color_handler ()
3742 RegionView::color_handler ();
3744 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3745 (*i)->set_selected ((*i)->selected()); // will change color
3748 /* XXX probably more to do here */
3752 MidiRegionView::enable_display (bool yn)
3754 RegionView::enable_display (yn);
3761 MidiRegionView::show_step_edit_cursor (Evoral::Beats pos)
3763 if (_step_edit_cursor == 0) {
3764 ArdourCanvas::Item* const group = get_canvas_group();
3766 _step_edit_cursor = new ArdourCanvas::Rectangle (group);
3767 _step_edit_cursor->set_y0 (0);
3768 _step_edit_cursor->set_y1 (midi_stream_view()->contents_height());
3769 _step_edit_cursor->set_fill_color (RGBA_TO_UINT (45,0,0,90));
3770 _step_edit_cursor->set_outline_color (RGBA_TO_UINT (85,0,0,90));
3773 move_step_edit_cursor (pos);
3774 _step_edit_cursor->show ();
3778 MidiRegionView::move_step_edit_cursor (Evoral::Beats pos)
3780 _step_edit_cursor_position = pos;
3782 if (_step_edit_cursor) {
3783 double pixel = trackview.editor().sample_to_pixel (region_beats_to_region_frames (pos));
3784 _step_edit_cursor->set_x0 (pixel);
3785 set_step_edit_cursor_width (_step_edit_cursor_width);
3790 MidiRegionView::hide_step_edit_cursor ()
3792 if (_step_edit_cursor) {
3793 _step_edit_cursor->hide ();
3798 MidiRegionView::set_step_edit_cursor_width (Evoral::Beats beats)
3800 _step_edit_cursor_width = beats;
3802 if (_step_edit_cursor) {
3803 _step_edit_cursor->set_x1 (_step_edit_cursor->x0() + trackview.editor().sample_to_pixel (region_beats_to_region_frames (beats)));
3807 /** Called when a diskstream on our track has received some data. Update the view, if applicable.
3808 * @param w Source that the data will end up in.
3811 MidiRegionView::data_recorded (boost::weak_ptr<MidiSource> w)
3813 if (!_active_notes) {
3814 /* we aren't actively being recorded to */
3818 boost::shared_ptr<MidiSource> src = w.lock ();
3819 if (!src || src != midi_region()->midi_source()) {
3820 /* recorded data was not destined for our source */
3824 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (&trackview);
3826 boost::shared_ptr<MidiBuffer> buf = mtv->midi_track()->get_gui_feed_buffer ();
3828 framepos_t back = max_framepos;
3830 for (MidiBuffer::iterator i = buf->begin(); i != buf->end(); ++i) {
3831 Evoral::MIDIEvent<MidiBuffer::TimeType> const ev (*i, false);
3833 if (ev.is_channel_event()) {
3834 if (get_channel_mode() == FilterChannels) {
3835 if (((uint16_t(1) << ev.channel()) & get_selected_channels()) == 0) {
3841 /* convert from session frames to source beats */
3842 Evoral::Beats const time_beats = _source_relative_time_converter.from(
3843 ev.time() - src->timeline_position() + _region->start());
3845 if (ev.type() == MIDI_CMD_NOTE_ON) {
3846 boost::shared_ptr<NoteType> note (
3847 new NoteType (ev.channel(), time_beats, Evoral::Beats(), ev.note(), ev.velocity()));
3849 add_note (note, true);
3851 /* fix up our note range */
3852 if (ev.note() < _current_range_min) {
3853 midi_stream_view()->apply_note_range (ev.note(), _current_range_max, true);
3854 } else if (ev.note() > _current_range_max) {
3855 midi_stream_view()->apply_note_range (_current_range_min, ev.note(), true);
3858 } else if (ev.type() == MIDI_CMD_NOTE_OFF) {
3859 resolve_note (ev.note (), time_beats);
3865 midi_stream_view()->check_record_layers (region(), back);
3869 MidiRegionView::trim_front_starting ()
3871 /* Reparent the note group to the region view's parent, so that it doesn't change
3872 when the region view is trimmed.
3874 _temporary_note_group = new ArdourCanvas::Container (group->parent ());
3875 _temporary_note_group->move (group->position ());
3876 _note_group->reparent (_temporary_note_group);
3880 MidiRegionView::trim_front_ending ()
3882 _note_group->reparent (group);
3883 delete _temporary_note_group;
3884 _temporary_note_group = 0;
3886 if (_region->start() < 0) {
3887 /* Trim drag made start time -ve; fix this */
3888 midi_region()->fix_negative_start ();
3893 MidiRegionView::edit_patch_change (PatchChange* pc)
3895 PatchChangeDialog d (&_source_relative_time_converter, trackview.session(), *pc->patch (), instrument_info(), Gtk::Stock::APPLY, true);
3897 int response = d.run();
3900 case Gtk::RESPONSE_ACCEPT:
3902 case Gtk::RESPONSE_REJECT:
3903 delete_patch_change (pc);
3909 change_patch_change (pc->patch(), d.patch ());
3913 MidiRegionView::delete_sysex (SysEx* /*sysex*/)
3916 // sysyex object doesn't have a pointer to a sysex event
3917 // MidiModel::SysExDiffCommand* c = _model->new_sysex_diff_command (_("delete sysex"));
3918 // c->remove (sysex->sysex());
3919 // _model->apply_command (*trackview.session(), c);
3921 //_sys_exes.clear ();
3922 // display_sysexes();
3926 MidiRegionView::show_verbose_cursor (boost::shared_ptr<NoteType> n) const
3928 using namespace MIDI::Name;
3932 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3934 boost::shared_ptr<MasterDeviceNames> device_names(mtv->get_device_names());
3936 MIDI::Name::PatchPrimaryKey patch_key;
3937 get_patch_key_at(n->time(), n->channel(), patch_key);
3938 name = device_names->note_name(mtv->gui_property(X_("midnam-custom-device-mode")),
3941 patch_key.program(),
3947 snprintf (buf, sizeof (buf), "%d %s\nCh %d Vel %d",
3949 name.empty() ? Evoral::midi_note_name (n->note()).c_str() : name.c_str(),
3950 (int) n->channel() + 1,
3951 (int) n->velocity());
3953 show_verbose_cursor(buf, 10, 20);
3957 MidiRegionView::show_verbose_cursor (string const & text, double xoffset, double yoffset) const
3959 trackview.editor().verbose_cursor()->set (text);
3960 trackview.editor().verbose_cursor()->show ();
3961 trackview.editor().verbose_cursor()->set_offset (ArdourCanvas::Duple (xoffset, yoffset));
3965 MidiRegionView::get_velocity_for_add (MidiModel::TimeType time) const
3967 if (_model->notes().empty()) {
3968 return 0x40; // No notes, use default
3971 MidiModel::Notes::const_iterator m = _model->note_lower_bound(time);
3972 if (m == _model->notes().begin()) {
3973 // Before the start, use the velocity of the first note
3974 return (*m)->velocity();
3975 } else if (m == _model->notes().end()) {
3976 // Past the end, use the velocity of the last note
3978 return (*m)->velocity();
3981 // Interpolate velocity of surrounding notes
3982 MidiModel::Notes::const_iterator n = m;
3985 const double frac = ((time - (*n)->time()).to_double() /
3986 ((*m)->time() - (*n)->time()).to_double());
3988 return (*n)->velocity() + (frac * ((*m)->velocity() - (*n)->velocity()));
3991 /** @param p A session framepos.
3992 * @param grid_frames Filled in with the number of frames that a grid interval is at p.
3993 * @return p snapped to the grid subdivision underneath it.
3996 MidiRegionView::snap_frame_to_grid_underneath (framepos_t p, framecnt_t& grid_frames) const
3998 PublicEditor& editor = trackview.editor ();
4000 const Evoral::Beats grid_beats = get_grid_beats(p);
4002 grid_frames = region_beats_to_region_frames (grid_beats);
4004 /* Hack so that we always snap to the note that we are over, instead of snapping
4005 to the next one if we're more than halfway through the one we're over.
4007 if (editor.snap_mode() == SnapNormal && p >= grid_frames / 2) {
4008 p -= grid_frames / 2;
4011 return snap_frame_to_frame (p);
4014 /** Called when the selection has been cleared in any MidiRegionView.
4015 * @param rv MidiRegionView that the selection was cleared in.
4018 MidiRegionView::selection_cleared (MidiRegionView* rv)
4024 /* Clear our selection in sympathy; but don't signal the fact */
4025 clear_selection (false);
4029 MidiRegionView::note_button_release ()
4031 _note_player.reset();
4035 MidiRegionView::get_channel_mode () const
4037 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (&trackview);
4038 return rtav->midi_track()->get_playback_channel_mode();
4042 MidiRegionView::get_selected_channels () const
4044 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (&trackview);
4045 return rtav->midi_track()->get_playback_channel_mask();
4050 MidiRegionView::get_grid_beats(framepos_t pos) const
4052 PublicEditor& editor = trackview.editor();
4053 bool success = false;
4054 Evoral::Beats beats = editor.get_grid_type_as_beats(success, pos);
4056 beats = Evoral::Beats(1);