2 Copyright (C) 2001-2011 Paul Davis
3 Author: David Robillard
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 #include "gtkmm2ext/gtk_ui.h"
28 #include <sigc++/signal.h>
30 #include "midi++/midnam_patch.h"
32 #include "pbd/memento_command.h"
33 #include "pbd/stateful_diff_command.h"
35 #include "ardour/midi_model.h"
36 #include "ardour/midi_playlist.h"
37 #include "ardour/midi_region.h"
38 #include "ardour/midi_source.h"
39 #include "ardour/midi_track.h"
40 #include "ardour/operations.h"
41 #include "ardour/session.h"
43 #include "evoral/Parameter.hpp"
44 #include "evoral/Event.hpp"
45 #include "evoral/Control.hpp"
46 #include "evoral/midi_util.h"
48 #include "canvas/debug.h"
49 #include "canvas/text.h"
51 #include "automation_region_view.h"
52 #include "automation_time_axis.h"
53 #include "control_point.h"
56 #include "editor_drag.h"
57 #include "ghostregion.h"
58 #include "gui_thread.h"
59 #include "item_counts.h"
61 #include "midi_channel_dialog.h"
62 #include "midi_cut_buffer.h"
63 #include "midi_list_editor.h"
64 #include "midi_region_view.h"
65 #include "midi_streamview.h"
66 #include "midi_time_axis.h"
67 #include "midi_util.h"
68 #include "midi_velocity_dialog.h"
69 #include "mouse_cursors.h"
70 #include "note_player.h"
71 #include "paste_context.h"
72 #include "public_editor.h"
73 #include "route_time_axis.h"
74 #include "rgb_macros.h"
75 #include "selection.h"
76 #include "streamview.h"
77 #include "patch_change_dialog.h"
78 #include "verbose_cursor.h"
81 #include "patch_change.h"
83 #include "ui_config.h"
87 using namespace ARDOUR;
89 using namespace Editing;
91 using Gtkmm2ext::Keyboard;
93 #define MIDI_BP_ZERO ((Config->get_first_midi_bank_is_zero())?0:1)
95 MidiRegionView::MidiRegionView (ArdourCanvas::Container* parent,
96 RouteTimeAxisView& tv,
97 boost::shared_ptr<MidiRegion> r,
100 : RegionView (parent, tv, r, spu, basic_color)
101 , _current_range_min(0)
102 , _current_range_max(0)
103 , _region_relative_time_converter(r->session().tempo_map(), r->position())
104 , _source_relative_time_converter(r->session().tempo_map(), r->position() - r->start())
105 , _region_relative_time_converter_double(r->session().tempo_map(), r->position())
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)
116 , _optimization_iterator (_events.end())
118 , _no_sound_notes (false)
119 , _last_display_zoom (0)
122 , _grabbed_keyboard (false)
125 , _mouse_changed_selection (false)
127 CANVAS_DEBUG_NAME (_note_group, string_compose ("note group for %1", get_item_name()));
129 _patch_change_outline = UIConfiguration::instance().color ("midi patch change outline");
130 _patch_change_fill = UIConfiguration::instance().color_mod ("midi patch change fill", "midi patch change fill");
132 _note_group->raise_to_top();
133 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
135 Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&MidiRegionView::parameter_changed, this, _1), gui_context());
136 connect_to_diskstream ();
139 MidiRegionView::MidiRegionView (ArdourCanvas::Container* parent,
140 RouteTimeAxisView& tv,
141 boost::shared_ptr<MidiRegion> r,
143 uint32_t basic_color,
145 TimeAxisViewItem::Visibility visibility)
146 : RegionView (parent, tv, r, spu, basic_color, recording, visibility)
147 , _current_range_min(0)
148 , _current_range_max(0)
149 , _region_relative_time_converter(r->session().tempo_map(), r->position())
150 , _source_relative_time_converter(r->session().tempo_map(), r->position() - r->start())
151 , _region_relative_time_converter_double(r->session().tempo_map(), r->position())
153 , _note_group (new ArdourCanvas::Container (group))
154 , _note_diff_command (0)
156 , _step_edit_cursor (0)
157 , _step_edit_cursor_width (1.0)
158 , _step_edit_cursor_position (0.0)
159 , _channel_selection_scoped_note (0)
162 , _optimization_iterator (_events.end())
164 , _no_sound_notes (false)
165 , _last_display_zoom (0)
168 , _grabbed_keyboard (false)
171 , _mouse_changed_selection (false)
173 CANVAS_DEBUG_NAME (_note_group, string_compose ("note group for %1", get_item_name()));
175 _patch_change_outline = UIConfiguration::instance().color ("midi patch change outline");
176 _patch_change_fill = UIConfiguration::instance().color_mod ("midi patch change fill", "midi patch change fill");
178 _note_group->raise_to_top();
180 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
182 connect_to_diskstream ();
186 MidiRegionView::parameter_changed (std::string const & p)
188 if (p == "display-first-midi-bank-as-zero") {
189 if (_enable_display) {
192 } else if (p == "color-regions-using-track-color") {
197 MidiRegionView::MidiRegionView (const MidiRegionView& other)
198 : sigc::trackable(other)
200 , _current_range_min(0)
201 , _current_range_max(0)
202 , _region_relative_time_converter(other.region_relative_time_converter())
203 , _source_relative_time_converter(other.source_relative_time_converter())
204 , _region_relative_time_converter_double(other.region_relative_time_converter_double())
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)
215 , _optimization_iterator (_events.end())
217 , _no_sound_notes (false)
218 , _last_display_zoom (0)
221 , _grabbed_keyboard (false)
224 , _mouse_changed_selection (false)
229 MidiRegionView::MidiRegionView (const MidiRegionView& other, boost::shared_ptr<MidiRegion> region)
230 : RegionView (other, boost::shared_ptr<Region> (region))
231 , _current_range_min(0)
232 , _current_range_max(0)
233 , _region_relative_time_converter(other.region_relative_time_converter())
234 , _source_relative_time_converter(other.source_relative_time_converter())
235 , _region_relative_time_converter_double(other.region_relative_time_converter_double())
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)
246 , _optimization_iterator (_events.end())
248 , _no_sound_notes (false)
249 , _last_display_zoom (0)
252 , _grabbed_keyboard (false)
255 , _mouse_changed_selection (false)
261 MidiRegionView::init (bool wfd)
263 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
266 Glib::Threads::Mutex::Lock lm(midi_region()->midi_source(0)->mutex());
267 midi_region()->midi_source(0)->load_model(lm);
270 _model = midi_region()->midi_source(0)->model();
271 _enable_display = false;
272 fill_color_name = "midi frame base";
274 RegionView::init (false);
276 //set_height (trackview.current_height());
279 region_sync_changed ();
280 region_resized (ARDOUR::bounds_change);
285 _enable_display = true;
288 display_model (_model);
292 reset_width_dependent_items (_pixel_width);
294 group->raise_to_top();
296 midi_view()->midi_track()->playback_filter().ChannelModeChanged.connect (_channel_mode_changed_connection, invalidator (*this),
297 boost::bind (&MidiRegionView::midi_channel_mode_changed, this),
300 instrument_info().Changed.connect (_instrument_changed_connection, invalidator (*this),
301 boost::bind (&MidiRegionView::instrument_settings_changed, this), gui_context());
303 trackview.editor().SnapChanged.connect(snap_changed_connection, invalidator(*this),
304 boost::bind (&MidiRegionView::snap_changed, this),
307 trackview.editor().MouseModeChanged.connect(_mouse_mode_connection, invalidator (*this),
308 boost::bind (&MidiRegionView::mouse_mode_changed, this),
311 Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&MidiRegionView::parameter_changed, this, _1), gui_context());
312 UIConfiguration::instance().ParameterChanged.connect (sigc::mem_fun (*this, &MidiRegionView::parameter_changed));
313 connect_to_diskstream ();
317 MidiRegionView::instrument_info () const
319 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
320 return route_ui->route()->instrument_info();
323 const boost::shared_ptr<ARDOUR::MidiRegion>
324 MidiRegionView::midi_region() const
326 return boost::dynamic_pointer_cast<ARDOUR::MidiRegion>(_region);
330 MidiRegionView::connect_to_diskstream ()
332 midi_view()->midi_track()->DataRecorded.connect(
333 *this, invalidator(*this),
334 boost::bind (&MidiRegionView::data_recorded, this, _1),
339 MidiRegionView::canvas_group_event(GdkEvent* ev)
341 if (in_destructor || _recregion) {
345 if (!trackview.editor().internal_editing()) {
346 // not in internal edit mode, so just act like a normal region
347 return RegionView::canvas_group_event (ev);
353 case GDK_ENTER_NOTIFY:
354 _last_event_x = ev->crossing.x;
355 _last_event_y = ev->crossing.y;
356 enter_notify(&ev->crossing);
357 // set entered_regionview (among other things)
358 return RegionView::canvas_group_event (ev);
360 case GDK_LEAVE_NOTIFY:
361 _last_event_x = ev->crossing.x;
362 _last_event_y = ev->crossing.y;
363 leave_notify(&ev->crossing);
364 // reset entered_regionview (among other things)
365 return RegionView::canvas_group_event (ev);
368 if (scroll (&ev->scroll)) {
374 return key_press (&ev->key);
376 case GDK_KEY_RELEASE:
377 return key_release (&ev->key);
379 case GDK_BUTTON_PRESS:
380 return button_press (&ev->button);
382 case GDK_BUTTON_RELEASE:
383 r = button_release (&ev->button);
386 case GDK_MOTION_NOTIFY:
387 _last_event_x = ev->motion.x;
388 _last_event_y = ev->motion.y;
389 return motion (&ev->motion);
395 return RegionView::canvas_group_event (ev);
399 MidiRegionView::enter_notify (GdkEventCrossing* ev)
401 enter_internal (ev->state);
408 MidiRegionView::leave_notify (GdkEventCrossing*)
417 MidiRegionView::mouse_mode_changed ()
419 // Adjust frame colour (become more transparent for internal tools)
423 if (!trackview.editor().internal_editing()) {
424 /* Switched out of internal editing mode while entered.
425 Only necessary for leave as a mouse_mode_change over a region
426 automatically triggers an enter event. */
429 else if (trackview.editor().current_mouse_mode() == MouseContent) {
430 // hide cursor and ghost note after changing to internal edit mode
431 remove_ghost_note ();
433 /* XXX This is problematic as the function is executed for every region
434 and only for one region _entered_note can be true. Still it's
435 necessary as to hide the verbose cursor when we're changing from
436 draw mode to internal edit mode. These lines are the reason why
437 in some situations no verbose cursor is shown when we enter internal
438 edit mode over a note. */
439 if (!_entered_note) {
440 hide_verbose_cursor ();
447 MidiRegionView::enter_internal (uint32_t state)
449 if (trackview.editor().current_mouse_mode() == MouseDraw && _mouse_state != AddDragging) {
450 // Show ghost note under pencil
451 create_ghost_note(_last_event_x, _last_event_y, state);
454 if (!_selection.empty()) {
455 // Grab keyboard for moving selected notes with arrow keys
456 Keyboard::magic_widget_grab_focus();
457 _grabbed_keyboard = true;
460 // Lower frame handles below notes so they don't steal events
461 if (frame_handle_start) {
462 frame_handle_start->lower_to_bottom();
464 if (frame_handle_end) {
465 frame_handle_end->lower_to_bottom();
470 MidiRegionView::leave_internal()
472 hide_verbose_cursor ();
473 remove_ghost_note ();
476 if (_grabbed_keyboard) {
477 Keyboard::magic_widget_drop_focus();
478 _grabbed_keyboard = false;
481 // Raise frame handles above notes so they catch events
482 if (frame_handle_start) {
483 frame_handle_start->raise_to_top();
485 if (frame_handle_end) {
486 frame_handle_end->raise_to_top();
491 MidiRegionView::button_press (GdkEventButton* ev)
493 if (ev->button != 1) {
497 Editor* editor = dynamic_cast<Editor *> (&trackview.editor());
498 MouseMode m = editor->current_mouse_mode();
500 if (m == MouseContent && Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())) {
501 _press_cursor_ctx = CursorContext::create(*editor, editor->cursors()->midi_pencil);
504 if (_mouse_state != SelectTouchDragging) {
506 _pressed_button = ev->button;
508 if (m == MouseDraw || (m == MouseContent && Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier()))) {
510 if (midi_view()->note_mode() == Percussive) {
511 editor->drags()->set (new HitCreateDrag (dynamic_cast<Editor *> (editor), group, this), (GdkEvent *) ev);
513 editor->drags()->set (new NoteCreateDrag (dynamic_cast<Editor *> (editor), group, this), (GdkEvent *) ev);
516 _mouse_state = AddDragging;
517 remove_ghost_note ();
518 hide_verbose_cursor ();
520 _mouse_state = Pressed;
526 _pressed_button = ev->button;
527 _mouse_changed_selection = false;
533 MidiRegionView::button_release (GdkEventButton* ev)
535 double event_x, event_y;
537 if (ev->button != 1) {
544 group->canvas_to_item (event_x, event_y);
547 PublicEditor& editor = trackview.editor ();
549 _press_cursor_ctx.reset();
551 switch (_mouse_state) {
552 case Pressed: // Clicked
554 switch (editor.current_mouse_mode()) {
556 /* no motion occurred - simple click */
557 clear_editor_note_selection ();
558 _mouse_changed_selection = true;
564 _mouse_changed_selection = true;
565 clear_editor_note_selection ();
580 /* Don't a ghost note when we added a note - wait until motion to avoid visual confusion.
581 we don't want one when we were drag-selecting either. */
582 case SelectRectDragging:
583 editor.drags()->end_grab ((GdkEvent *) ev);
592 if (_mouse_changed_selection) {
593 trackview.editor().begin_reversible_selection_op (X_("Mouse Selection Change"));
594 trackview.editor().commit_reversible_selection_op ();
601 MidiRegionView::motion (GdkEventMotion* ev)
603 PublicEditor& editor = trackview.editor ();
605 if (!_entered_note) {
607 if (_mouse_state == AddDragging) {
609 remove_ghost_note ();
612 } else if (!_ghost_note && editor.current_mouse_mode() == MouseContent &&
613 Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier()) &&
614 _mouse_state != AddDragging) {
616 create_ghost_note (ev->x, ev->y, ev->state);
618 } else if (_ghost_note && editor.current_mouse_mode() == MouseContent &&
619 Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())) {
621 update_ghost_note (ev->x, ev->y, ev->state);
623 } else if (_ghost_note && editor.current_mouse_mode() == MouseContent) {
625 remove_ghost_note ();
626 hide_verbose_cursor ();
628 } else if (editor.current_mouse_mode() == MouseDraw) {
631 update_ghost_note (ev->x, ev->y, ev->state);
634 create_ghost_note (ev->x, ev->y, ev->state);
639 /* any motion immediately hides velocity text that may have been visible */
641 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
642 (*i)->hide_velocity ();
645 switch (_mouse_state) {
648 if (_pressed_button == 1) {
650 MouseMode m = editor.current_mouse_mode();
652 if (m == MouseContent && !Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())) {
653 editor.drags()->set (new MidiRubberbandSelectDrag (dynamic_cast<Editor *> (&editor), this), (GdkEvent *) ev);
654 if (!Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
655 clear_editor_note_selection ();
656 _mouse_changed_selection = true;
658 _mouse_state = SelectRectDragging;
660 } else if (m == MouseRange) {
661 editor.drags()->set (new MidiVerticalSelectDrag (dynamic_cast<Editor *> (&editor), this), (GdkEvent *) ev);
662 _mouse_state = SelectVerticalDragging;
669 case SelectRectDragging:
670 case SelectVerticalDragging:
672 editor.drags()->motion_handler ((GdkEvent *) ev, false);
675 case SelectTouchDragging:
683 /* we may be dragging some non-note object (eg. patch-change, sysex)
686 return editor.drags()->motion_handler ((GdkEvent *) ev, false);
691 MidiRegionView::scroll (GdkEventScroll* ev)
693 if (_selection.empty()) {
697 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier) ||
698 Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
699 /* XXX: bit of a hack; allow PrimaryModifier and TertiaryModifier scroll
700 * through so that it still works for navigation.
705 hide_verbose_cursor ();
707 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
708 Keyboard::ModifierMask mask_together(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier);
709 bool together = Keyboard::modifier_state_contains (ev->state, mask_together);
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.
730 bool unmodified = Keyboard::no_modifier_keys_pressed (ev);
732 if (unmodified && (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R)) {
734 if (_mouse_state != AddDragging) {
735 _mouse_state = SelectTouchDragging;
740 } else if (ev->keyval == GDK_Escape && unmodified) {
741 clear_editor_note_selection ();
744 } else if (ev->keyval == GDK_comma || ev->keyval == GDK_period) {
746 bool start = (ev->keyval == GDK_comma);
747 bool end = (ev->keyval == GDK_period);
748 bool shorter = Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier);
749 bool fine = Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
751 change_note_lengths (fine, shorter, Evoral::Beats(), start, end);
755 } else if ((ev->keyval == GDK_BackSpace || ev->keyval == GDK_Delete) && unmodified) {
757 if (_selection.empty()) {
764 } else if (ev->keyval == GDK_Tab || ev->keyval == GDK_ISO_Left_Tab) {
766 trackview.editor().begin_reversible_selection_op (X_("Select Adjacent Note"));
768 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
769 goto_previous_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
771 goto_next_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
774 trackview.editor().commit_reversible_selection_op();
778 } else if (ev->keyval == GDK_Up) {
780 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
781 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
782 bool together = Keyboard::modifier_state_contains (ev->state, Keyboard::Level4Modifier);
784 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
785 change_velocities (true, fine, allow_smush, together);
787 transpose (true, fine, allow_smush);
791 } else if (ev->keyval == GDK_Down) {
793 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
794 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
795 bool together = Keyboard::modifier_state_contains (ev->state, Keyboard::Level4Modifier);
797 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
798 change_velocities (false, fine, allow_smush, together);
800 transpose (false, fine, allow_smush);
804 } else if (ev->keyval == GDK_Left) {
806 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
807 nudge_notes (false, fine);
810 } else if (ev->keyval == GDK_Right) {
812 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
813 nudge_notes (true, fine);
816 } else if (ev->keyval == GDK_c && unmodified) {
820 } else if (ev->keyval == GDK_v && unmodified) {
829 MidiRegionView::key_release (GdkEventKey* ev)
831 if ((_mouse_state == SelectTouchDragging) && (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R)) {
839 MidiRegionView::channel_edit ()
841 if (_selection.empty()) {
845 /* pick a note somewhat at random (since Selection is a set<>) to
846 * provide the "current" channel for the dialog.
849 uint8_t current_channel = (*_selection.begin())->note()->channel ();
850 MidiChannelDialog channel_dialog (current_channel);
851 int ret = channel_dialog.run ();
854 case Gtk::RESPONSE_OK:
860 uint8_t new_channel = channel_dialog.active_channel ();
862 start_note_diff_command (_("channel edit"));
864 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
865 Selection::iterator next = i;
867 change_note_channel (*i, new_channel);
875 MidiRegionView::velocity_edit ()
877 if (_selection.empty()) {
881 /* pick a note somewhat at random (since Selection is a set<>) to
882 * provide the "current" velocity for the dialog.
885 uint8_t current_velocity = (*_selection.begin())->note()->velocity ();
886 MidiVelocityDialog velocity_dialog (current_velocity);
887 int ret = velocity_dialog.run ();
890 case Gtk::RESPONSE_OK:
896 uint8_t new_velocity = velocity_dialog.velocity ();
898 start_note_diff_command (_("velocity edit"));
900 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
901 Selection::iterator next = i;
903 change_note_velocity (*i, new_velocity, false);
911 MidiRegionView::show_list_editor ()
914 _list_editor = new MidiListEditor (trackview.session(), midi_region(), midi_view()->midi_track());
916 _list_editor->present ();
919 /** Add a note to the model, and the view, at a canvas (click) coordinate.
920 * \param t time in frames relative to the position of the region
921 * \param y vertical position in pixels
922 * \param length duration of the note in beats
923 * \param snap_t true to snap t to the grid, otherwise false.
926 MidiRegionView::create_note_at (framepos_t t, double y, Evoral::Beats length, uint32_t state, bool shift_snap)
928 if (length < 2 * DBL_EPSILON) {
932 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
933 MidiStreamView* const view = mtv->midi_view();
934 boost::shared_ptr<MidiRegion> mr = boost::dynamic_pointer_cast<MidiRegion> (_region);
940 // Start of note in frames relative to region start
941 const int32_t divisions = trackview.editor().get_grid_music_divisions (state);
942 Evoral::Beats beat_time = snap_frame_to_grid_underneath (t, divisions, shift_snap);
944 const double note = view->y_to_note(y);
945 const uint8_t chan = mtv->get_channel_for_add();
946 const uint8_t velocity = get_velocity_for_add(beat_time);
948 const boost::shared_ptr<NoteType> new_note(
949 new NoteType (chan, beat_time, length, (uint8_t)note, velocity));
951 if (_model->contains (new_note)) {
955 view->update_note_range(new_note->note());
957 start_note_diff_command(_("add note"));
959 note_diff_add_note (new_note, true, false);
963 play_midi_note (new_note);
967 MidiRegionView::clear_events ()
969 // clear selection without signaling
970 clear_selection_internal ();
973 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
974 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
980 _note_group->clear (true);
982 _patch_changes.clear();
984 _optimization_iterator = _events.end();
988 MidiRegionView::display_model(boost::shared_ptr<MidiModel> model)
992 content_connection.disconnect ();
993 _model->ContentsChanged.connect (content_connection, invalidator (*this), boost::bind (&MidiRegionView::redisplay_model, this), gui_context());
994 /* Don't signal as nobody else needs to know until selection has been altered. */
997 if (_enable_display) {
1003 MidiRegionView::start_note_diff_command (string name)
1005 if (!_note_diff_command) {
1006 trackview.editor().begin_reversible_command (name);
1007 _note_diff_command = _model->new_note_diff_command (name);
1012 MidiRegionView::note_diff_add_note (const boost::shared_ptr<NoteType> note, bool selected, bool show_velocity)
1014 if (_note_diff_command) {
1015 _note_diff_command->add (note);
1018 _marked_for_selection.insert(note);
1020 if (show_velocity) {
1021 _marked_for_velocity.insert(note);
1026 MidiRegionView::note_diff_remove_note (NoteBase* ev)
1028 if (_note_diff_command && ev->note()) {
1029 _note_diff_command->remove(ev->note());
1034 MidiRegionView::note_diff_add_change (NoteBase* ev,
1035 MidiModel::NoteDiffCommand::Property property,
1038 if (_note_diff_command) {
1039 _note_diff_command->change (ev->note(), property, val);
1044 MidiRegionView::note_diff_add_change (NoteBase* ev,
1045 MidiModel::NoteDiffCommand::Property property,
1048 if (_note_diff_command) {
1049 _note_diff_command->change (ev->note(), property, val);
1054 MidiRegionView::apply_diff (bool as_subcommand, bool was_copy)
1057 bool commit = false;
1059 if (!_note_diff_command) {
1063 if (!was_copy && (add_or_remove = _note_diff_command->adds_or_removes())) {
1064 // Mark all selected notes for selection when model reloads
1065 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1066 _marked_for_selection.insert((*i)->note());
1070 midi_view()->midi_track()->midi_playlist()->region_edited (_region, _note_diff_command);
1072 if (as_subcommand) {
1073 _model->apply_command_as_subcommand (*trackview.session(), _note_diff_command);
1075 _model->apply_command (*trackview.session(), _note_diff_command);
1079 _note_diff_command = 0;
1081 if (add_or_remove) {
1082 _marked_for_selection.clear();
1085 _marked_for_velocity.clear();
1087 trackview.editor().commit_reversible_command ();
1092 MidiRegionView::abort_command()
1094 delete _note_diff_command;
1095 _note_diff_command = 0;
1096 clear_editor_note_selection();
1100 MidiRegionView::find_canvas_note (boost::shared_ptr<NoteType> note)
1103 if (_optimization_iterator != _events.end()) {
1104 ++_optimization_iterator;
1107 if (_optimization_iterator != _events.end() && _optimization_iterator->first == note) {
1108 return _optimization_iterator->second;
1111 _optimization_iterator = _events.find (note);
1112 if (_optimization_iterator != _events.end()) {
1113 return _optimization_iterator->second;
1119 /** This version finds any canvas note matching the supplied note. */
1121 MidiRegionView::find_canvas_note (Evoral::event_id_t id)
1123 Events::iterator it;
1125 for (it = _events.begin(); it != _events.end(); ++it) {
1126 if (it->first->id() == id) {
1134 boost::shared_ptr<PatchChange>
1135 MidiRegionView::find_canvas_patch_change (MidiModel::PatchChangePtr p)
1137 PatchChanges::const_iterator f = _patch_changes.find (p);
1139 if (f != _patch_changes.end()) {
1143 return boost::shared_ptr<PatchChange>();
1146 boost::shared_ptr<SysEx>
1147 MidiRegionView::find_canvas_sys_ex (MidiModel::SysExPtr s)
1149 SysExes::const_iterator f = _sys_exes.find (s);
1151 if (f != _sys_exes.end()) {
1155 return boost::shared_ptr<SysEx>();
1159 MidiRegionView::get_events (Events& e, Evoral::Sequence<Evoral::Beats>::NoteOperator op, uint8_t val, int chan_mask)
1161 MidiModel::Notes notes;
1162 _model->get_notes (notes, op, val, chan_mask);
1164 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1165 NoteBase* cne = find_canvas_note (*n);
1167 e.insert (make_pair (*n, cne));
1173 MidiRegionView::redisplay_model()
1175 if (_active_notes) {
1176 // Currently recording
1177 const framecnt_t zoom = trackview.editor().get_current_zoom();
1178 if (zoom != _last_display_zoom) {
1179 /* Update resolved canvas notes to reflect changes in zoom without
1180 touching model. Leave active notes (with length 0) alone since
1181 they are being extended. */
1182 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1183 if (i->second->note()->length() > 0) {
1184 update_note(i->second);
1187 _last_display_zoom = zoom;
1196 for (_optimization_iterator = _events.begin(); _optimization_iterator != _events.end(); ++_optimization_iterator) {
1197 _optimization_iterator->second->invalidate();
1200 bool empty_when_starting = _events.empty();
1201 _optimization_iterator = _events.begin();
1202 MidiModel::Notes missing_notes;
1206 MidiModel::ReadLock lock(_model->read_lock());
1207 MidiModel::Notes& notes (_model->notes());
1210 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1212 boost::shared_ptr<NoteType> note (*n);
1215 if (note_in_region_range (note, visible)) {
1216 if (!empty_when_starting && (cne = find_canvas_note (note)) != 0) {
1224 missing_notes.insert (note);
1229 if (!empty_when_starting) {
1230 MidiModel::Notes::iterator f;
1231 for (Events::iterator i = _events.begin(); i != _events.end(); ) {
1233 NoteBase* cne = i->second;
1235 /* remove note items that are no longer valid */
1236 if (!cne->valid()) {
1238 for (vector<GhostRegion*>::iterator j = ghosts.begin(); j != ghosts.end(); ++j) {
1239 MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*j);
1241 gr->remove_note (cne);
1246 i = _events.erase (i);
1249 bool visible = cne->item()->visible();
1251 if ((sus = dynamic_cast<Note*>(cne))) {
1254 update_sustained (sus);
1257 } else if ((hit = dynamic_cast<Hit*>(cne))) {
1269 for (MidiModel::Notes::iterator n = missing_notes.begin(); n != missing_notes.end(); ++n) {
1270 boost::shared_ptr<NoteType> note (*n);
1274 if (note_in_region_range (note, visible)) {
1276 cne = add_note (note, true);
1278 cne = add_note (note, false);
1281 cne = add_note (note, false);
1284 for (set<Evoral::event_id_t>::iterator it = _pending_note_selection.begin(); it != _pending_note_selection.end(); ++it) {
1285 if ((*it) == note->id()) {
1286 add_to_selection (cne);
1291 for (vector<GhostRegion*>::iterator j = ghosts.begin(); j != ghosts.end(); ++j) {
1292 MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*j);
1293 if (gr && !gr->trackview.hidden()) {
1294 gr->redisplay_model ();
1299 display_patch_changes ();
1301 _marked_for_selection.clear ();
1302 _marked_for_velocity.clear ();
1303 _pending_note_selection.clear ();
1308 MidiRegionView::display_patch_changes ()
1310 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1311 uint16_t chn_mask = mtv->midi_track()->get_playback_channel_mask();
1313 for (uint8_t i = 0; i < 16; ++i) {
1314 display_patch_changes_on_channel (i, chn_mask & (1 << i));
1318 /** @param active_channel true to display patch changes fully, false to display
1319 * them `greyed-out' (as on an inactive channel)
1322 MidiRegionView::display_patch_changes_on_channel (uint8_t channel, bool active_channel)
1324 for (MidiModel::PatchChanges::const_iterator i = _model->patch_changes().begin(); i != _model->patch_changes().end(); ++i) {
1325 boost::shared_ptr<PatchChange> p;
1327 if ((*i)->channel() != channel) {
1331 if ((p = find_canvas_patch_change (*i)) != 0) {
1333 const framecnt_t region_frames = source_beats_to_region_frames ((*i)->time());
1335 if (region_frames < 0 || region_frames >= _region->length()) {
1338 const double x = trackview.editor().sample_to_pixel (region_frames);
1339 const string patch_name = instrument_info().get_patch_name ((*i)->bank(), (*i)->program(), channel);
1340 p->canvas_item()->set_position (ArdourCanvas::Duple (x, 1.0));
1341 p->set_text (patch_name);
1347 const string patch_name = instrument_info().get_patch_name ((*i)->bank(), (*i)->program(), channel);
1348 add_canvas_patch_change (*i, patch_name, active_channel);
1354 MidiRegionView::display_sysexes()
1356 bool have_periodic_system_messages = false;
1357 bool display_periodic_messages = true;
1359 if (!UIConfiguration::instance().get_never_display_periodic_midi()) {
1361 for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
1362 if ((*i)->is_spp() || (*i)->is_mtc_quarter() || (*i)->is_mtc_full()) {
1363 have_periodic_system_messages = true;
1368 if (have_periodic_system_messages) {
1369 double zoom = trackview.editor().get_current_zoom (); // frames per pixel
1371 /* get an approximate value for the number of samples per video frame */
1373 double video_frame = trackview.session()->frame_rate() * (1.0/30);
1375 /* if we are zoomed out beyond than the cutoff (i.e. more
1376 * frames per pixel than frames per 4 video frames), don't
1377 * show periodic sysex messages.
1380 if (zoom > (video_frame*4)) {
1381 display_periodic_messages = false;
1385 display_periodic_messages = false;
1388 for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
1389 MidiModel::SysExPtr sysex_ptr = *i;
1390 Evoral::Beats time = sysex_ptr->time();
1392 if ((*i)->is_spp() || (*i)->is_mtc_quarter() || (*i)->is_mtc_full()) {
1393 if (!display_periodic_messages) {
1400 for (uint32_t b = 0; b < (*i)->size(); ++b) {
1401 str << int((*i)->buffer()[b]);
1402 if (b != (*i)->size() -1) {
1406 string text = str.str();
1408 const double x = trackview.editor().sample_to_pixel(source_beats_to_region_frames(time));
1410 double height = midi_stream_view()->contents_height();
1412 // CAIROCANVAS: no longer passing *i (the sysex event) to the
1413 // SysEx canvas object!!!
1414 boost::shared_ptr<SysEx> sysex = find_canvas_sys_ex (sysex_ptr);
1417 sysex = boost::shared_ptr<SysEx>(
1418 new SysEx (*this, _note_group, text, height, x, 1.0, sysex_ptr));
1419 _sys_exes.insert (make_pair (sysex_ptr, sysex));
1421 sysex->set_height (height);
1422 sysex->item().set_position (ArdourCanvas::Duple (x, 1.0));
1425 // Show unless message is beyond the region bounds
1426 if (time - _region->start() >= _region->length() || time < _region->start()) {
1434 MidiRegionView::~MidiRegionView ()
1436 in_destructor = true;
1438 hide_verbose_cursor ();
1440 delete _list_editor;
1442 RegionViewGoingAway (this); /* EMIT_SIGNAL */
1444 if (_active_notes) {
1451 delete _note_diff_command;
1452 delete _step_edit_cursor;
1456 MidiRegionView::region_resized (const PropertyChange& what_changed)
1458 RegionView::region_resized(what_changed); // calls RegionView::set_duration()
1460 if (what_changed.contains (ARDOUR::Properties::position)) {
1461 _region_relative_time_converter.set_origin_b(_region->position());
1462 _region_relative_time_converter_double.set_origin_b(_region->position());
1463 /* reset_width dependent_items() redisplays model */
1467 if (what_changed.contains (ARDOUR::Properties::start) ||
1468 what_changed.contains (ARDOUR::Properties::position)) {
1469 _source_relative_time_converter.set_origin_b (_region->position() - _region->start());
1471 /* catch end and start trim so we can update the view*/
1472 if (!what_changed.contains (ARDOUR::Properties::start) &&
1473 what_changed.contains (ARDOUR::Properties::length)) {
1474 enable_display (true);
1475 } else if (what_changed.contains (ARDOUR::Properties::start) &&
1476 what_changed.contains (ARDOUR::Properties::length)) {
1477 enable_display (true);
1482 MidiRegionView::reset_width_dependent_items (double pixel_width)
1484 RegionView::reset_width_dependent_items(pixel_width);
1486 if (_enable_display) {
1490 bool hide_all = false;
1491 PatchChanges::iterator x = _patch_changes.begin();
1492 if (x != _patch_changes.end()) {
1493 hide_all = x->second->width() >= _pixel_width;
1497 for (; x != _patch_changes.end(); ++x) {
1502 move_step_edit_cursor (_step_edit_cursor_position);
1503 set_step_edit_cursor_width (_step_edit_cursor_width);
1507 MidiRegionView::set_height (double height)
1509 double old_height = _height;
1510 RegionView::set_height(height);
1512 apply_note_range (midi_stream_view()->lowest_note(),
1513 midi_stream_view()->highest_note(),
1514 height != old_height);
1517 name_text->raise_to_top();
1520 for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
1521 (*x).second->set_height (midi_stream_view()->contents_height());
1524 if (_step_edit_cursor) {
1525 _step_edit_cursor->set_y1 (midi_stream_view()->contents_height());
1530 /** Apply the current note range from the stream view
1531 * by repositioning/hiding notes as necessary
1534 MidiRegionView::apply_note_range (uint8_t min, uint8_t max, bool force)
1536 if (!_enable_display) {
1540 if (!force && _current_range_min == min && _current_range_max == max) {
1544 _current_range_min = min;
1545 _current_range_max = max;
1551 MidiRegionView::add_ghost (TimeAxisView& tv)
1553 double unit_position = _region->position () / samples_per_pixel;
1554 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(&tv);
1555 MidiGhostRegion* ghost;
1557 if (mtv && mtv->midi_view()) {
1558 /* if ghost is inserted into midi track, use a dedicated midi ghost canvas group
1559 to allow having midi notes on top of note lines and waveforms.
1561 ghost = new MidiGhostRegion (*this, *mtv->midi_view(), trackview, unit_position);
1563 ghost = new MidiGhostRegion (*this, tv, trackview, unit_position);
1566 ghost->set_colors ();
1567 ghost->set_height ();
1568 ghost->set_duration (_region->length() / samples_per_pixel);
1570 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1571 ghost->add_note(i->second);
1574 ghosts.push_back (ghost);
1575 enable_display (true);
1580 /** Begin tracking note state for successive calls to add_event
1583 MidiRegionView::begin_write()
1585 if (_active_notes) {
1586 delete[] _active_notes;
1588 _active_notes = new Note*[128];
1589 for (unsigned i = 0; i < 128; ++i) {
1590 _active_notes[i] = 0;
1595 /** Destroy note state for add_event
1598 MidiRegionView::end_write()
1600 delete[] _active_notes;
1602 _marked_for_selection.clear();
1603 _marked_for_velocity.clear();
1607 /** Resolve an active MIDI note (while recording).
1610 MidiRegionView::resolve_note(uint8_t note, Evoral::Beats end_time)
1612 if (midi_view()->note_mode() != Sustained) {
1616 if (_active_notes && _active_notes[note]) {
1617 /* Set note length so update_note() works. Note this is a local note
1618 for recording, not from a model, so we can safely mess with it. */
1619 _active_notes[note]->note()->set_length(
1620 end_time - _active_notes[note]->note()->time());
1622 /* End time is relative to the region being recorded. */
1623 const framepos_t end_time_frames = region_beats_to_region_frames(end_time);
1625 _active_notes[note]->set_x1 (trackview.editor().sample_to_pixel(end_time_frames));
1626 _active_notes[note]->set_outline_all ();
1627 _active_notes[note] = 0;
1632 /** Extend active notes to rightmost edge of region (if length is changed)
1635 MidiRegionView::extend_active_notes()
1637 if (!_active_notes) {
1641 for (unsigned i = 0; i < 128; ++i) {
1642 if (_active_notes[i]) {
1643 _active_notes[i]->set_x1(
1644 trackview.editor().sample_to_pixel(_region->length()));
1650 MidiRegionView::play_midi_note(boost::shared_ptr<NoteType> note)
1652 if (_no_sound_notes || !UIConfiguration::instance().get_sound_midi_notes()) {
1656 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1658 if (!route_ui || !route_ui->midi_track()) {
1662 NotePlayer* np = new NotePlayer (route_ui->midi_track ());
1666 /* NotePlayer deletes itself */
1670 MidiRegionView::start_playing_midi_note(boost::shared_ptr<NoteType> note)
1672 const std::vector< boost::shared_ptr<NoteType> > notes(1, note);
1673 start_playing_midi_chord(notes);
1677 MidiRegionView::start_playing_midi_chord (vector<boost::shared_ptr<NoteType> > notes)
1679 if (_no_sound_notes || !UIConfiguration::instance().get_sound_midi_notes()) {
1683 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1685 if (!route_ui || !route_ui->midi_track()) {
1689 NotePlayer* player = new NotePlayer (route_ui->midi_track());
1691 for (vector<boost::shared_ptr<NoteType> >::iterator n = notes.begin(); n != notes.end(); ++n) {
1700 MidiRegionView::note_in_region_range (const boost::shared_ptr<NoteType> note, bool& visible) const
1702 const boost::shared_ptr<ARDOUR::MidiRegion> midi_reg = midi_region();
1704 /* must compare double explicitly as Beats::operator< rounds to ppqn */
1705 const bool outside = (note->time().to_double() < midi_reg->start_beats() ||
1706 note->time().to_double() >= midi_reg->start_beats() + midi_reg->length_beats());
1708 visible = (note->note() >= _current_range_min) &&
1709 (note->note() <= _current_range_max);
1715 MidiRegionView::update_note (NoteBase* note, bool update_ghost_regions)
1719 if ((sus = dynamic_cast<Note*>(note))) {
1720 update_sustained(sus, update_ghost_regions);
1721 } else if ((hit = dynamic_cast<Hit*>(note))) {
1722 update_hit(hit, update_ghost_regions);
1726 /** Update a canvas note's size from its model note.
1727 * @param ev Canvas note to update.
1728 * @param update_ghost_regions true to update the note in any ghost regions that we have, otherwise false.
1731 MidiRegionView::update_sustained (Note* ev, bool update_ghost_regions)
1733 TempoMap& map (trackview.session()->tempo_map());
1734 const boost::shared_ptr<ARDOUR::MidiRegion> mr = midi_region();
1735 boost::shared_ptr<NoteType> note = ev->note();
1737 const double session_source_start = _region->quarter_note() - mr->start_beats();
1738 const framepos_t note_start_frames = map.frame_at_quarter_note (note->time().to_double() + session_source_start) - _region->position();
1740 const double x0 = trackview.editor().sample_to_pixel (note_start_frames);
1742 const double y0 = 1 + floor(note_to_y(note->note()));
1745 /* trim note display to not overlap the end of its region */
1746 if (note->length().to_double() > 0.0) {
1747 double note_end_time = note->end_time().to_double();
1749 if (note_end_time > mr->start_beats() + mr->length_beats()) {
1750 note_end_time = mr->start_beats() + mr->length_beats();
1753 const framepos_t note_end_frames = map.frame_at_quarter_note (session_source_start + note_end_time) - _region->position();
1755 x1 = std::max(1., trackview.editor().sample_to_pixel (note_end_frames)) - 1;
1757 x1 = std::max(1., trackview.editor().sample_to_pixel (_region->length())) - 1;
1760 y1 = y0 + std::max(1., floor(note_height()) - 1);
1762 ev->set (ArdourCanvas::Rect (x0, y0, x1, y1));
1764 if (!note->length()) {
1765 if (_active_notes && note->note() < 128) {
1766 Note* const old_rect = _active_notes[note->note()];
1768 /* There is an active note on this key, so we have a stuck
1769 note. Finish the old rectangle here. */
1770 old_rect->set_x1 (x1);
1771 old_rect->set_outline_all ();
1773 _active_notes[note->note()] = ev;
1775 /* outline all but right edge */
1776 ev->set_outline_what (ArdourCanvas::Rectangle::What (
1777 ArdourCanvas::Rectangle::TOP|
1778 ArdourCanvas::Rectangle::LEFT|
1779 ArdourCanvas::Rectangle::BOTTOM));
1781 /* outline all edges */
1782 ev->set_outline_all ();
1785 // Update color in case velocity has changed
1786 const uint32_t base_col = ev->base_color();
1787 ev->set_fill_color(base_col);
1788 ev->set_outline_color(ev->calculate_outline(base_col, ev->selected()));
1793 MidiRegionView::update_hit (Hit* ev, bool update_ghost_regions)
1795 boost::shared_ptr<NoteType> note = ev->note();
1797 const double note_time_qn = note->time().to_double() + (_region->quarter_note() - midi_region()->start_beats());
1798 const framepos_t note_start_frames = trackview.session()->tempo_map().frame_at_quarter_note (note_time_qn) - _region->position();
1800 const double x = trackview.editor().sample_to_pixel(note_start_frames);
1801 const double diamond_size = std::max(1., floor(note_height()) - 2.);
1802 const double y = 1.5 + floor(note_to_y(note->note())) + diamond_size * .5;
1804 // see DnD note in MidiRegionView::apply_note_range() above
1805 if (y <= 0 || y >= _height) {
1811 ev->set_position (ArdourCanvas::Duple (x, y));
1812 ev->set_height (diamond_size);
1814 // Update color in case velocity has changed
1815 const uint32_t base_col = ev->base_color();
1816 ev->set_fill_color(base_col);
1817 ev->set_outline_color(ev->calculate_outline(base_col, ev->selected()));
1821 /** Add a MIDI note to the view (with length).
1823 * If in sustained mode, notes with length 0 will be considered active
1824 * notes, and resolve_note should be called when the corresponding note off
1825 * event arrives, to properly display the note.
1828 MidiRegionView::add_note(const boost::shared_ptr<NoteType> note, bool visible)
1830 NoteBase* event = 0;
1832 if (midi_view()->note_mode() == Sustained) {
1834 Note* ev_rect = new Note (*this, _note_group, note); // XXX may leak
1836 update_sustained (ev_rect);
1840 } else if (midi_view()->note_mode() == Percussive) {
1842 const double diamond_size = std::max(1., floor(note_height()) - 2.);
1844 Hit* ev_diamond = new Hit (*this, _note_group, diamond_size, note); // XXX may leak
1846 update_hit (ev_diamond);
1855 MidiGhostRegion* gr;
1857 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
1858 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
1859 gr->add_note(event);
1863 if (_marked_for_selection.find(note) != _marked_for_selection.end()) {
1864 note_selected(event, true);
1867 if (_marked_for_velocity.find(note) != _marked_for_velocity.end()) {
1868 event->show_velocity();
1871 event->on_channel_selection_change (get_selected_channels());
1872 _events.insert (make_pair (event->note(), event));
1881 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1882 MidiStreamView* const view = mtv->midi_view();
1884 view->update_note_range (note->note());
1889 MidiRegionView::step_add_note (uint8_t channel, uint8_t number, uint8_t velocity,
1890 Evoral::Beats pos, Evoral::Beats len)
1892 boost::shared_ptr<NoteType> new_note (new NoteType (channel, pos, len, number, velocity));
1894 /* potentially extend region to hold new note */
1896 framepos_t end_frame = source_beats_to_absolute_frames (new_note->end_time());
1897 framepos_t region_end = _region->last_frame();
1899 if (end_frame > region_end) {
1900 /* XX sets length in beats from audio space. make musical */
1901 _region->set_length (end_frame - _region->position(), 0);
1904 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1905 MidiStreamView* const view = mtv->midi_view();
1907 view->update_note_range(new_note->note());
1909 _marked_for_selection.clear ();
1911 start_note_diff_command (_("step add"));
1913 clear_editor_note_selection ();
1914 note_diff_add_note (new_note, true, false);
1918 // last_step_edit_note = new_note;
1922 MidiRegionView::step_sustain (Evoral::Beats beats)
1924 change_note_lengths (false, false, beats, false, true);
1927 /** Add a new patch change flag to the canvas.
1928 * @param patch the patch change to add
1929 * @param the text to display in the flag
1930 * @param active_channel true to display the flag as on an active channel, false to grey it out for an inactive channel.
1933 MidiRegionView::add_canvas_patch_change (MidiModel::PatchChangePtr patch, const string& displaytext, bool /*active_channel*/)
1935 framecnt_t region_frames = source_beats_to_region_frames (patch->time());
1936 const double x = trackview.editor().sample_to_pixel (region_frames);
1938 double const height = midi_stream_view()->contents_height();
1940 // CAIROCANVAS: active_channel info removed from PatcChange constructor
1941 // so we need to do something more sophisticated to keep its color
1942 // appearance (MidiPatchChangeFill/MidiPatchChangeInactiveChannelFill)
1944 boost::shared_ptr<PatchChange> patch_change = boost::shared_ptr<PatchChange>(
1945 new PatchChange(*this, group,
1951 _patch_change_outline,
1955 if (patch_change->item().width() < _pixel_width) {
1956 // Show unless patch change is beyond the region bounds
1957 if (region_frames < 0 || region_frames >= _region->length()) {
1958 patch_change->hide();
1960 patch_change->show();
1963 patch_change->hide ();
1966 _patch_changes.insert (make_pair (patch, patch_change));
1970 MidiRegionView::remove_canvas_patch_change (PatchChange* pc)
1972 /* remove the canvas item */
1973 for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
1974 if (x->second->patch() == pc->patch()) {
1975 _patch_changes.erase (x);
1981 MIDI::Name::PatchPrimaryKey
1982 MidiRegionView::patch_change_to_patch_key (MidiModel::PatchChangePtr p)
1984 return MIDI::Name::PatchPrimaryKey (p->program(), p->bank());
1987 /// Return true iff @p pc applies to the given time on the given channel.
1989 patch_applies (const ARDOUR::MidiModel::constPatchChangePtr pc, Evoral::Beats time, uint8_t channel)
1991 return pc->time() <= time && pc->channel() == channel;
1995 MidiRegionView::get_patch_key_at (Evoral::Beats time, uint8_t channel, MIDI::Name::PatchPrimaryKey& key) const
1997 // The earliest event not before time
1998 MidiModel::PatchChanges::iterator i = _model->patch_change_lower_bound (time);
2000 // Go backwards until we find the latest PC for this channel, or the start
2001 while (i != _model->patch_changes().begin() &&
2002 (i == _model->patch_changes().end() ||
2003 !patch_applies(*i, time, channel))) {
2007 if (i != _model->patch_changes().end() && patch_applies(*i, time, channel)) {
2008 key.set_bank((*i)->bank());
2009 key.set_program((*i)->program ());
2017 MidiRegionView::change_patch_change (PatchChange& pc, const MIDI::Name::PatchPrimaryKey& new_patch)
2019 string name = _("alter patch change");
2020 trackview.editor().begin_reversible_command (name);
2022 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (name);
2024 if (pc.patch()->program() != new_patch.program()) {
2025 c->change_program (pc.patch (), new_patch.program());
2028 int const new_bank = new_patch.bank();
2029 if (pc.patch()->bank() != new_bank) {
2030 c->change_bank (pc.patch (), new_bank);
2033 _model->apply_command (*trackview.session(), c);
2034 trackview.editor().commit_reversible_command ();
2036 remove_canvas_patch_change (&pc);
2037 display_patch_changes ();
2041 MidiRegionView::change_patch_change (MidiModel::PatchChangePtr old_change, const Evoral::PatchChange<Evoral::Beats> & new_change)
2043 string name = _("alter patch change");
2044 trackview.editor().begin_reversible_command (name);
2045 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (name);
2047 if (old_change->time() != new_change.time()) {
2048 c->change_time (old_change, new_change.time());
2051 if (old_change->channel() != new_change.channel()) {
2052 c->change_channel (old_change, new_change.channel());
2055 if (old_change->program() != new_change.program()) {
2056 c->change_program (old_change, new_change.program());
2059 if (old_change->bank() != new_change.bank()) {
2060 c->change_bank (old_change, new_change.bank());
2063 _model->apply_command (*trackview.session(), c);
2064 trackview.editor().commit_reversible_command ();
2066 for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
2067 if (x->second->patch() == old_change) {
2068 _patch_changes.erase (x);
2073 display_patch_changes ();
2076 /** Add a patch change to the region.
2077 * @param t Time in frames relative to region position
2078 * @param patch Patch to add; time and channel are ignored (time is converted from t, and channel comes from
2079 * MidiTimeAxisView::get_channel_for_add())
2082 MidiRegionView::add_patch_change (framecnt_t t, Evoral::PatchChange<Evoral::Beats> const & patch)
2084 string name = _("add patch change");
2086 trackview.editor().begin_reversible_command (name);
2087 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (name);
2088 c->add (MidiModel::PatchChangePtr (
2089 new Evoral::PatchChange<Evoral::Beats> (
2090 absolute_frames_to_source_beats (_region->position() + t),
2091 patch.channel(), patch.program(), patch.bank()
2096 _model->apply_command (*trackview.session(), c);
2097 trackview.editor().commit_reversible_command ();
2099 display_patch_changes ();
2103 MidiRegionView::move_patch_change (PatchChange& pc, Evoral::Beats t)
2105 trackview.editor().begin_reversible_command (_("move patch change"));
2106 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("move patch change"));
2107 c->change_time (pc.patch (), t);
2108 _model->apply_command (*trackview.session(), c);
2109 trackview.editor().commit_reversible_command ();
2111 display_patch_changes ();
2115 MidiRegionView::delete_patch_change (PatchChange* pc)
2117 trackview.editor().begin_reversible_command (_("delete patch change"));
2119 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("delete patch change"));
2120 c->remove (pc->patch ());
2121 _model->apply_command (*trackview.session(), c);
2122 trackview.editor().commit_reversible_command ();
2124 remove_canvas_patch_change (pc);
2125 display_patch_changes ();
2129 MidiRegionView::step_patch (PatchChange& patch, bool bank, int delta)
2131 MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key(patch.patch());
2133 key.set_bank(key.bank() + delta);
2135 key.set_program(key.program() + delta);
2137 change_patch_change(patch, key);
2141 MidiRegionView::note_deleted (NoteBase* cne)
2143 if (_entered_note && cne == _entered_note) {
2147 if (_selection.empty()) {
2151 _selection.erase (cne);
2155 MidiRegionView::delete_selection()
2157 if (_selection.empty()) {
2161 if (trackview.editor().drags()->active()) {
2165 start_note_diff_command (_("delete selection"));
2167 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2168 if ((*i)->selected()) {
2169 _note_diff_command->remove((*i)->note());
2177 hide_verbose_cursor ();
2181 MidiRegionView::delete_note (boost::shared_ptr<NoteType> n)
2183 start_note_diff_command (_("delete note"));
2184 _note_diff_command->remove (n);
2187 hide_verbose_cursor ();
2191 MidiRegionView::clear_editor_note_selection ()
2193 DEBUG_TRACE(DEBUG::Selection, "MRV::clear_editor_note_selection\n");
2194 PublicEditor& editor(trackview.editor());
2195 editor.get_selection().clear_midi_notes();
2199 MidiRegionView::clear_selection ()
2201 clear_selection_internal();
2202 PublicEditor& editor(trackview.editor());
2203 editor.get_selection().remove(this);
2207 MidiRegionView::clear_selection_internal ()
2209 DEBUG_TRACE(DEBUG::Selection, "MRV::clear_selection_internal\n");
2211 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2212 (*i)->set_selected(false);
2213 (*i)->hide_velocity();
2218 // Clearing selection entirely, ungrab keyboard
2219 Keyboard::magic_widget_drop_focus();
2220 _grabbed_keyboard = false;
2225 MidiRegionView::unique_select(NoteBase* ev)
2227 clear_editor_note_selection();
2228 add_to_selection(ev);
2232 MidiRegionView::select_all_notes ()
2234 clear_editor_note_selection ();
2236 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2237 add_to_selection (i->second);
2242 MidiRegionView::select_range (framepos_t start, framepos_t end)
2244 clear_editor_note_selection ();
2246 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2247 framepos_t t = source_beats_to_absolute_frames(i->first->time());
2248 if (t >= start && t <= end) {
2249 add_to_selection (i->second);
2255 MidiRegionView::invert_selection ()
2257 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2258 if (i->second->selected()) {
2259 remove_from_selection(i->second);
2261 add_to_selection (i->second);
2266 /** Used for selection undo/redo.
2267 The requested notes most likely won't exist in the view until the next model redisplay.
2270 MidiRegionView::select_notes (list<Evoral::event_id_t> notes)
2273 list<Evoral::event_id_t>::iterator n;
2275 for (n = notes.begin(); n != notes.end(); ++n) {
2276 if ((cne = find_canvas_note(*n)) != 0) {
2277 add_to_selection (cne);
2279 _pending_note_selection.insert(*n);
2285 MidiRegionView::select_matching_notes (uint8_t notenum, uint16_t channel_mask, bool add, bool extend)
2287 bool have_selection = !_selection.empty();
2288 uint8_t low_note = 127;
2289 uint8_t high_note = 0;
2290 MidiModel::Notes& notes (_model->notes());
2291 _optimization_iterator = _events.begin();
2293 if (extend && !have_selection) {
2297 /* scan existing selection to get note range */
2299 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2300 if ((*i)->note()->note() < low_note) {
2301 low_note = (*i)->note()->note();
2303 if ((*i)->note()->note() > high_note) {
2304 high_note = (*i)->note()->note();
2309 clear_editor_note_selection ();
2311 if (!extend && (low_note == high_note) && (high_note == notenum)) {
2312 /* only note previously selected is the one we are
2313 * reselecting. treat this as cancelling the selection.
2320 low_note = min (low_note, notenum);
2321 high_note = max (high_note, notenum);
2324 _no_sound_notes = true;
2326 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
2328 boost::shared_ptr<NoteType> note (*n);
2330 bool select = false;
2332 if (((1 << note->channel()) & channel_mask) != 0) {
2334 if ((note->note() >= low_note && note->note() <= high_note)) {
2337 } else if (note->note() == notenum) {
2343 if ((cne = find_canvas_note (note)) != 0) {
2344 // extend is false because we've taken care of it,
2345 // since it extends by time range, not pitch.
2346 note_selected (cne, add, false);
2350 add = true; // we need to add all remaining matching notes, even if the passed in value was false (for "set")
2354 _no_sound_notes = false;
2358 MidiRegionView::toggle_matching_notes (uint8_t notenum, uint16_t channel_mask)
2360 MidiModel::Notes& notes (_model->notes());
2361 _optimization_iterator = _events.begin();
2363 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
2365 boost::shared_ptr<NoteType> note (*n);
2368 if (note->note() == notenum && (((0x0001 << note->channel()) & channel_mask) != 0)) {
2369 if ((cne = find_canvas_note (note)) != 0) {
2370 if (cne->selected()) {
2371 note_deselected (cne);
2373 note_selected (cne, true, false);
2381 MidiRegionView::note_selected (NoteBase* ev, bool add, bool extend)
2384 clear_editor_note_selection();
2385 add_to_selection (ev);
2390 if (!ev->selected()) {
2391 add_to_selection (ev);
2395 /* find end of latest note selected, select all between that and the start of "ev" */
2397 Evoral::Beats earliest = Evoral::MaxBeats;
2398 Evoral::Beats latest = Evoral::Beats();
2400 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2401 if ((*i)->note()->end_time() > latest) {
2402 latest = (*i)->note()->end_time();
2404 if ((*i)->note()->time() < earliest) {
2405 earliest = (*i)->note()->time();
2409 if (ev->note()->end_time() > latest) {
2410 latest = ev->note()->end_time();
2413 if (ev->note()->time() < earliest) {
2414 earliest = ev->note()->time();
2417 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2419 /* find notes entirely within OR spanning the earliest..latest range */
2421 if ((i->first->time() >= earliest && i->first->end_time() <= latest) ||
2422 (i->first->time() <= earliest && i->first->end_time() >= latest)) {
2423 add_to_selection (i->second);
2430 MidiRegionView::note_deselected(NoteBase* ev)
2432 remove_from_selection (ev);
2436 MidiRegionView::update_drag_selection(framepos_t start, framepos_t end, double gy0, double gy1, bool extend)
2438 PublicEditor& editor = trackview.editor();
2440 // Convert to local coordinates
2441 const framepos_t p = _region->position();
2442 const double y = midi_view()->y_position();
2443 const double x0 = editor.sample_to_pixel(max((framepos_t)0, start - p));
2444 const double x1 = editor.sample_to_pixel(max((framepos_t)0, end - p));
2445 const double y0 = max(0.0, gy0 - y);
2446 const double y1 = max(0.0, gy1 - y);
2448 // TODO: Make this faster by storing the last updated selection rect, and only
2449 // adjusting things that are in the area that appears/disappeared.
2450 // We probably need a tree to be able to find events in O(log(n)) time.
2452 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2453 if (i->second->x0() < x1 && i->second->x1() > x0 && i->second->y0() < y1 && i->second->y1() > y0) {
2454 // Rectangles intersect
2455 if (!i->second->selected()) {
2456 add_to_selection (i->second);
2458 } else if (i->second->selected() && !extend) {
2459 // Rectangles do not intersect
2460 remove_from_selection (i->second);
2464 typedef RouteTimeAxisView::AutomationTracks ATracks;
2465 typedef std::list<Selectable*> Selectables;
2467 /* Add control points to selection. */
2468 const ATracks& atracks = midi_view()->automation_tracks();
2469 Selectables selectables;
2470 editor.get_selection().clear_points();
2471 for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) {
2472 a->second->get_selectables(start, end, gy0, gy1, selectables);
2473 for (Selectables::const_iterator s = selectables.begin(); s != selectables.end(); ++s) {
2474 ControlPoint* cp = dynamic_cast<ControlPoint*>(*s);
2476 editor.get_selection().add(cp);
2479 a->second->set_selected_points(editor.get_selection().points);
2484 MidiRegionView::update_vertical_drag_selection (double y1, double y2, bool extend)
2490 // TODO: Make this faster by storing the last updated selection rect, and only
2491 // adjusting things that are in the area that appears/disappeared.
2492 // We probably need a tree to be able to find events in O(log(n)) time.
2494 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2495 if ((i->second->y1() >= y1 && i->second->y1() <= y2)) {
2496 // within y- (note-) range
2497 if (!i->second->selected()) {
2498 add_to_selection (i->second);
2500 } else if (i->second->selected() && !extend) {
2501 remove_from_selection (i->second);
2507 MidiRegionView::remove_from_selection (NoteBase* ev)
2509 Selection::iterator i = _selection.find (ev);
2511 if (i != _selection.end()) {
2512 _selection.erase (i);
2513 if (_selection.empty() && _grabbed_keyboard) {
2515 Keyboard::magic_widget_drop_focus();
2516 _grabbed_keyboard = false;
2520 ev->set_selected (false);
2521 ev->hide_velocity ();
2523 if (_selection.empty()) {
2524 PublicEditor& editor (trackview.editor());
2525 editor.get_selection().remove (this);
2530 MidiRegionView::add_to_selection (NoteBase* ev)
2532 const bool selection_was_empty = _selection.empty();
2534 if (_selection.insert (ev).second) {
2535 ev->set_selected (true);
2536 start_playing_midi_note ((ev)->note());
2537 if (selection_was_empty && _entered) {
2538 // Grab keyboard for moving notes with arrow keys
2539 Keyboard::magic_widget_grab_focus();
2540 _grabbed_keyboard = true;
2544 if (selection_was_empty) {
2545 PublicEditor& editor (trackview.editor());
2546 editor.get_selection().add (this);
2551 MidiRegionView::earliest_in_selection ()
2553 Evoral::Beats earliest = Evoral::MaxBeats;
2555 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2556 if ((*i)->note()->time() < earliest) {
2557 earliest = (*i)->note()->time();
2565 MidiRegionView::move_selection(double dx_qn, double dy, double cumulative_dy)
2567 typedef vector<boost::shared_ptr<NoteType> > PossibleChord;
2568 Editor* editor = dynamic_cast<Editor*> (&trackview.editor());
2569 TempoMap& tmap (editor->session()->tempo_map());
2570 PossibleChord to_play;
2571 Evoral::Beats earliest = earliest_in_selection();
2573 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2575 if (n->note()->time() == earliest) {
2576 to_play.push_back (n->note());
2578 double const note_time_qn = session_relative_qn (n->note()->time().to_double());
2580 if (midi_view()->note_mode() == Sustained) {
2581 dx = editor->sample_to_pixel_unrounded (tmap.frame_at_quarter_note (note_time_qn + dx_qn))
2582 - n->item()->item_to_canvas (ArdourCanvas::Duple (n->x0(), 0)).x;
2584 /* Hit::x0() is offset by _position.x, unlike Note::x0() */
2585 Hit* hit = dynamic_cast<Hit*>(n);
2587 dx = editor->sample_to_pixel_unrounded (tmap.frame_at_quarter_note (note_time_qn + dx_qn))
2588 - n->item()->item_to_canvas (ArdourCanvas::Duple (((hit->x0() + hit->x1()) / 2.0) - hit->position().x, 0)).x;
2592 (*i)->move_event(dx, dy);
2595 if (midi_view()->note_mode() == Sustained) {
2596 Note* sus = dynamic_cast<Note*> (*i);
2597 double const len_dx = editor->sample_to_pixel_unrounded (
2598 tmap.frame_at_quarter_note (note_time_qn + dx_qn + n->note()->length().to_double()));
2600 sus->set_x1 (n->item()->canvas_to_item (ArdourCanvas::Duple (len_dx, 0)).x);
2604 if (dy && !_selection.empty() && !_no_sound_notes && UIConfiguration::instance().get_sound_midi_notes()) {
2606 if (to_play.size() > 1) {
2608 PossibleChord shifted;
2610 for (PossibleChord::iterator n = to_play.begin(); n != to_play.end(); ++n) {
2611 boost::shared_ptr<NoteType> moved_note (new NoteType (**n));
2612 moved_note->set_note (moved_note->note() + cumulative_dy);
2613 shifted.push_back (moved_note);
2616 start_playing_midi_chord (shifted);
2618 } else if (!to_play.empty()) {
2620 boost::shared_ptr<NoteType> moved_note (new NoteType (*to_play.front()));
2621 moved_note->set_note (moved_note->note() + cumulative_dy);
2622 start_playing_midi_note (moved_note);
2628 MidiRegionView::copy_selection (NoteBase* primary)
2630 _copy_drag_events.clear ();
2632 if (_selection.empty()) {
2639 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2640 boost::shared_ptr<NoteType> g (new NoteType (*((*i)->note())));
2641 if (midi_view()->note_mode() == Sustained) {
2642 Note* n = new Note (*this, _note_group, g);
2643 update_sustained (n, false);
2646 Hit* h = new Hit (*this, _note_group, 10, g);
2647 update_hit (h, false);
2651 if ((*i) == primary) {
2655 _copy_drag_events.push_back (note);
2662 MidiRegionView::move_copies (double dx_qn, double dy, double cumulative_dy)
2664 typedef vector<boost::shared_ptr<NoteType> > PossibleChord;
2665 Editor* editor = dynamic_cast<Editor*> (&trackview.editor());
2666 TempoMap& tmap (editor->session()->tempo_map());
2667 PossibleChord to_play;
2668 Evoral::Beats earliest = earliest_in_selection();
2670 for (CopyDragEvents::iterator i = _copy_drag_events.begin(); i != _copy_drag_events.end(); ++i) {
2672 if (n->note()->time() == earliest) {
2673 to_play.push_back (n->note());
2675 double const note_time_qn = session_relative_qn (n->note()->time().to_double());
2677 if (midi_view()->note_mode() == Sustained) {
2678 dx = editor->sample_to_pixel_unrounded (tmap.frame_at_quarter_note (note_time_qn + dx_qn))
2679 - n->item()->item_to_canvas (ArdourCanvas::Duple (n->x0(), 0)).x;
2681 Hit* hit = dynamic_cast<Hit*>(n);
2683 dx = editor->sample_to_pixel_unrounded (tmap.frame_at_quarter_note (note_time_qn + dx_qn))
2684 - n->item()->item_to_canvas (ArdourCanvas::Duple (((hit->x0() + hit->x1()) / 2.0) - hit->position().x, 0)).x;
2688 (*i)->move_event(dx, dy);
2690 if (midi_view()->note_mode() == Sustained) {
2691 Note* sus = dynamic_cast<Note*> (*i);
2692 double const len_dx = editor->sample_to_pixel_unrounded (
2693 tmap.frame_at_quarter_note (note_time_qn + dx_qn + n->note()->length().to_double()));
2695 sus->set_x1 (n->item()->canvas_to_item (ArdourCanvas::Duple (len_dx, 0)).x);
2699 if (dy && !_copy_drag_events.empty() && !_no_sound_notes && UIConfiguration::instance().get_sound_midi_notes()) {
2701 if (to_play.size() > 1) {
2703 PossibleChord shifted;
2705 for (PossibleChord::iterator n = to_play.begin(); n != to_play.end(); ++n) {
2706 boost::shared_ptr<NoteType> moved_note (new NoteType (**n));
2707 moved_note->set_note (moved_note->note() + cumulative_dy);
2708 shifted.push_back (moved_note);
2711 start_playing_midi_chord (shifted);
2713 } else if (!to_play.empty()) {
2715 boost::shared_ptr<NoteType> moved_note (new NoteType (*to_play.front()));
2716 moved_note->set_note (moved_note->note() + cumulative_dy);
2717 start_playing_midi_note (moved_note);
2723 MidiRegionView::note_dropped(NoteBase *, double d_qn, int8_t dnote, bool copy)
2725 uint8_t lowest_note_in_selection = 127;
2726 uint8_t highest_note_in_selection = 0;
2727 uint8_t highest_note_difference = 0;
2730 // find highest and lowest notes first
2732 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2733 uint8_t pitch = (*i)->note()->note();
2734 lowest_note_in_selection = std::min(lowest_note_in_selection, pitch);
2735 highest_note_in_selection = std::max(highest_note_in_selection, pitch);
2739 cerr << "dnote: " << (int) dnote << endl;
2740 cerr << "lowest note (streamview): " << int(midi_stream_view()->lowest_note())
2741 << " highest note (streamview): " << int(midi_stream_view()->highest_note()) << endl;
2742 cerr << "lowest note (selection): " << int(lowest_note_in_selection) << " highest note(selection): "
2743 << int(highest_note_in_selection) << endl;
2744 cerr << "selection size: " << _selection.size() << endl;
2745 cerr << "Highest note in selection: " << (int) highest_note_in_selection << endl;
2748 // Make sure the note pitch does not exceed the MIDI standard range
2749 if (highest_note_in_selection + dnote > 127) {
2750 highest_note_difference = highest_note_in_selection - 127;
2753 start_note_diff_command (_("move notes"));
2755 for (Selection::iterator i = _selection.begin(); i != _selection.end() ; ++i) {
2757 Evoral::Beats new_time = Evoral::Beats ((*i)->note()->time().to_double() + d_qn);
2763 note_diff_add_change (*i, MidiModel::NoteDiffCommand::StartTime, new_time);
2765 uint8_t original_pitch = (*i)->note()->note();
2766 uint8_t new_pitch = original_pitch + dnote - highest_note_difference;
2768 // keep notes in standard midi range
2769 clamp_to_0_127(new_pitch);
2771 lowest_note_in_selection = std::min(lowest_note_in_selection, new_pitch);
2772 highest_note_in_selection = std::max(highest_note_in_selection, new_pitch);
2774 note_diff_add_change (*i, MidiModel::NoteDiffCommand::NoteNumber, new_pitch);
2778 clear_editor_note_selection ();
2780 for (CopyDragEvents::iterator i = _copy_drag_events.begin(); i != _copy_drag_events.end(); ++i) {
2781 uint8_t pitch = (*i)->note()->note();
2782 lowest_note_in_selection = std::min(lowest_note_in_selection, pitch);
2783 highest_note_in_selection = std::max(highest_note_in_selection, pitch);
2786 // Make sure the note pitch does not exceed the MIDI standard range
2787 if (highest_note_in_selection + dnote > 127) {
2788 highest_note_difference = highest_note_in_selection - 127;
2791 start_note_diff_command (_("copy notes"));
2793 for (CopyDragEvents::iterator i = _copy_drag_events.begin(); i != _copy_drag_events.end() ; ++i) {
2796 Evoral::Beats new_time = Evoral::Beats ((*i)->note()->time().to_double() + d_qn);
2802 (*i)->note()->set_time (new_time);
2806 uint8_t original_pitch = (*i)->note()->note();
2807 uint8_t new_pitch = original_pitch + dnote - highest_note_difference;
2809 (*i)->note()->set_note (new_pitch);
2811 // keep notes in standard midi range
2812 clamp_to_0_127(new_pitch);
2814 lowest_note_in_selection = std::min(lowest_note_in_selection, new_pitch);
2815 highest_note_in_selection = std::max(highest_note_in_selection, new_pitch);
2817 note_diff_add_note ((*i)->note(), true);
2822 _copy_drag_events.clear ();
2825 apply_diff (false, copy);
2827 // care about notes being moved beyond the upper/lower bounds on the canvas
2828 if (lowest_note_in_selection < midi_stream_view()->lowest_note() ||
2829 highest_note_in_selection > midi_stream_view()->highest_note()) {
2830 midi_stream_view()->set_note_range (MidiStreamView::ContentsRange);
2834 /** @param x Pixel relative to the region position.
2835 * @param ensure_snap defaults to false. true = snap always, ignoring snap mode and magnetic snap.
2836 * Used for inverting the snap logic with key modifiers and snap delta calculation.
2837 * @return Snapped frame relative to the region position.
2840 MidiRegionView::snap_pixel_to_sample(double x, bool ensure_snap)
2842 PublicEditor& editor (trackview.editor());
2843 return snap_frame_to_frame (editor.pixel_to_sample (x), ensure_snap).frame;
2846 /** @param x Pixel relative to the region position.
2847 * @param ensure_snap defaults to false. true = ignore magnetic snap and snap mode (used for snap delta calculation).
2848 * @return Snapped pixel relative to the region position.
2851 MidiRegionView::snap_to_pixel(double x, bool ensure_snap)
2853 return (double) trackview.editor().sample_to_pixel(snap_pixel_to_sample(x, ensure_snap));
2857 MidiRegionView::get_position_pixels()
2859 framepos_t region_frame = get_position();
2860 return trackview.editor().sample_to_pixel(region_frame);
2864 MidiRegionView::get_end_position_pixels()
2866 framepos_t frame = get_position() + get_duration ();
2867 return trackview.editor().sample_to_pixel(frame);
2871 MidiRegionView::source_beats_to_absolute_frames(Evoral::Beats beats) const
2873 /* the time converter will return the frame corresponding to `beats'
2874 relative to the start of the source. The start of the source
2875 is an implied position given by region->position - region->start
2877 const framepos_t source_start = _region->position() - _region->start();
2878 return source_start + _source_relative_time_converter.to (beats);
2882 MidiRegionView::absolute_frames_to_source_beats(framepos_t frames) const
2884 /* the `frames' argument needs to be converted into a frame count
2885 relative to the start of the source before being passed in to the
2888 const framepos_t source_start = _region->position() - _region->start();
2889 return _source_relative_time_converter.from (frames - source_start);
2893 MidiRegionView::region_beats_to_region_frames(Evoral::Beats beats) const
2895 return _region_relative_time_converter.to(beats);
2899 MidiRegionView::region_frames_to_region_beats(framepos_t frames) const
2901 return _region_relative_time_converter.from(frames);
2905 MidiRegionView::region_frames_to_region_beats_double (framepos_t frames) const
2907 return _region_relative_time_converter_double.from(frames);
2911 MidiRegionView::begin_resizing (bool /*at_front*/)
2913 _resize_data.clear();
2915 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2916 Note *note = dynamic_cast<Note*> (*i);
2918 // only insert CanvasNotes into the map
2920 NoteResizeData *resize_data = new NoteResizeData();
2921 resize_data->note = note;
2923 // create a new SimpleRect from the note which will be the resize preview
2924 ArdourCanvas::Rectangle *resize_rect = new ArdourCanvas::Rectangle (_note_group,
2925 ArdourCanvas::Rect (note->x0(), note->y0(), note->x0(), note->y1()));
2927 // calculate the colors: get the color settings
2928 uint32_t fill_color = UINT_RGBA_CHANGE_A(
2929 UIConfiguration::instance().color ("midi note selected"),
2932 // make the resize preview notes more transparent and bright
2933 fill_color = UINT_INTERPOLATE(fill_color, 0xFFFFFF40, 0.5);
2935 // calculate color based on note velocity
2936 resize_rect->set_fill_color (UINT_INTERPOLATE(
2937 NoteBase::meter_style_fill_color(note->note()->velocity(), note->selected()),
2941 resize_rect->set_outline_color (NoteBase::calculate_outline (
2942 UIConfiguration::instance().color ("midi note selected")));
2944 resize_data->resize_rect = resize_rect;
2945 _resize_data.push_back(resize_data);
2950 /** Update resizing notes while user drags.
2951 * @param primary `primary' note for the drag; ie the one that is used as the reference in non-relative mode.
2952 * @param at_front which end of the note (true == note on, false == note off)
2953 * @param delta_x change in mouse position since the start of the drag
2954 * @param relative true if relative resizing is taking place, false if absolute resizing. This only makes
2955 * a difference when multiple notes are being resized; in relative mode, each note's length is changed by the
2956 * amount of the drag. In non-relative mode, all selected notes are set to have the same start or end point
2957 * as the \a primary note.
2958 * @param snap_delta snap offset of the primary note in pixels. used in SnapRelative SnapDelta mode.
2959 * @param with_snap true if snap is to be used to determine the position, false if no snap is to be used.
2962 MidiRegionView::update_resizing (NoteBase* primary, bool at_front, double delta_x, bool relative, double snap_delta, bool with_snap)
2964 TempoMap& tmap (trackview.session()->tempo_map());
2965 bool cursor_set = false;
2966 bool const ensure_snap = trackview.editor().snap_mode () != SnapMagnetic;
2968 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2969 ArdourCanvas::Rectangle* resize_rect = (*i)->resize_rect;
2970 Note* canvas_note = (*i)->note;
2975 current_x = canvas_note->x0() + delta_x + snap_delta;
2977 current_x = primary->x0() + delta_x + snap_delta;
2981 current_x = canvas_note->x1() + delta_x + snap_delta;
2983 current_x = primary->x1() + delta_x + snap_delta;
2987 if (current_x < 0) {
2988 // This works even with snapping because RegionView::snap_frame_to_frame()
2989 // snaps forward if the snapped sample is before the beginning of the region
2992 if (current_x > trackview.editor().sample_to_pixel(_region->length())) {
2993 current_x = trackview.editor().sample_to_pixel(_region->length());
2998 resize_rect->set_x0 (snap_to_pixel (current_x, ensure_snap) - snap_delta);
3000 resize_rect->set_x0 (current_x - snap_delta);
3002 resize_rect->set_x1 (canvas_note->x1());
3005 resize_rect->set_x1 (snap_to_pixel (current_x, ensure_snap) - snap_delta);
3007 resize_rect->set_x1 (current_x - snap_delta);
3009 resize_rect->set_x0 (canvas_note->x0());
3013 /* Convert snap delta from pixels to beats. */
3014 framepos_t snap_delta_samps = trackview.editor().pixel_to_sample (snap_delta);
3015 double snap_delta_beats = 0.0;
3018 /* negative beat offsets aren't allowed */
3019 if (snap_delta_samps > 0) {
3020 snap_delta_beats = region_frames_to_region_beats_double (snap_delta_samps);
3021 } else if (snap_delta_samps < 0) {
3022 snap_delta_beats = region_frames_to_region_beats_double ( - snap_delta_samps);
3027 int32_t divisions = 0;
3030 snapped_x = snap_pixel_to_sample (current_x, ensure_snap);
3031 divisions = trackview.editor().get_grid_music_divisions (0);
3033 snapped_x = trackview.editor ().pixel_to_sample (current_x);
3035 const Evoral::Beats beats = Evoral::Beats (tmap.exact_beat_at_frame (snapped_x + midi_region()->position(), divisions)
3036 - midi_region()->beat()) + midi_region()->start_beats();
3038 Evoral::Beats len = Evoral::Beats();
3041 if (beats < canvas_note->note()->end_time()) {
3042 len = canvas_note->note()->time() - beats + (sign * snap_delta_beats);
3043 len += canvas_note->note()->length();
3046 if (beats >= canvas_note->note()->time()) {
3047 len = beats - canvas_note->note()->time() - (sign * snap_delta_beats);
3051 len = std::max(Evoral::Beats(1 / 512.0), len);
3054 snprintf (buf, sizeof (buf), "%.3g beats", len.to_double());
3055 show_verbose_cursor (buf, 0, 0);
3064 /** Finish resizing notes when the user releases the mouse button.
3065 * Parameters the same as for \a update_resizing().
3068 MidiRegionView::commit_resizing (NoteBase* primary, bool at_front, double delta_x, bool relative, double snap_delta, bool with_snap)
3070 _note_diff_command = _model->new_note_diff_command (_("resize notes"));
3071 TempoMap& tmap (trackview.session()->tempo_map());
3073 /* XX why doesn't snap_pixel_to_sample() handle this properly? */
3074 bool const ensure_snap = trackview.editor().snap_mode () != SnapMagnetic;
3076 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
3077 Note* canvas_note = (*i)->note;
3078 ArdourCanvas::Rectangle* resize_rect = (*i)->resize_rect;
3080 /* Get the new x position for this resize, which is in pixels relative
3081 * to the region position.
3088 current_x = canvas_note->x0() + delta_x + snap_delta;
3090 current_x = primary->x0() + delta_x + snap_delta;
3094 current_x = canvas_note->x1() + delta_x + snap_delta;
3096 current_x = primary->x1() + delta_x + snap_delta;
3100 if (current_x < 0) {
3103 if (current_x > trackview.editor().sample_to_pixel(_region->length())) {
3104 current_x = trackview.editor().sample_to_pixel(_region->length());
3107 /* Convert snap delta from pixels to beats with sign. */
3108 framepos_t snap_delta_samps = trackview.editor().pixel_to_sample (snap_delta);
3109 double snap_delta_beats = 0.0;
3112 if (snap_delta_samps > 0) {
3113 snap_delta_beats = region_frames_to_region_beats_double (snap_delta_samps);
3114 } else if (snap_delta_samps < 0) {
3115 snap_delta_beats = region_frames_to_region_beats_double ( - snap_delta_samps);
3119 uint32_t divisions = 0;
3120 /* Convert the new x position to a frame within the source */
3121 framepos_t current_fr;
3123 current_fr = snap_pixel_to_sample (current_x, ensure_snap);
3124 divisions = trackview.editor().get_grid_music_divisions (0);
3126 current_fr = trackview.editor().pixel_to_sample (current_x);
3129 /* and then to beats */
3130 const double e_qaf = tmap.exact_qn_at_frame (current_fr + midi_region()->position(), divisions);
3131 const double quarter_note_start = _region->quarter_note() - midi_region()->start_beats();
3132 const Evoral::Beats x_beats = Evoral::Beats (e_qaf - quarter_note_start);
3134 if (at_front && x_beats < canvas_note->note()->end_time()) {
3135 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::StartTime, x_beats - (sign * snap_delta_beats));
3136 Evoral::Beats len = canvas_note->note()->time() - x_beats + (sign * snap_delta_beats);
3137 len += canvas_note->note()->length();
3140 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
3145 Evoral::Beats len = std::max(Evoral::Beats(1 / 512.0),
3146 x_beats - canvas_note->note()->time() - (sign * snap_delta_beats));
3147 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
3154 _resize_data.clear();
3159 MidiRegionView::abort_resizing ()
3161 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
3162 delete (*i)->resize_rect;
3166 _resize_data.clear ();
3170 MidiRegionView::change_note_velocity(NoteBase* event, int8_t velocity, bool relative)
3172 uint8_t new_velocity;
3175 new_velocity = event->note()->velocity() + velocity;
3176 clamp_to_0_127(new_velocity);
3178 new_velocity = velocity;
3181 event->set_selected (event->selected()); // change color
3183 note_diff_add_change (event, MidiModel::NoteDiffCommand::Velocity, new_velocity);
3187 MidiRegionView::change_note_note (NoteBase* event, int8_t note, bool relative)
3192 new_note = event->note()->note() + note;
3197 clamp_to_0_127 (new_note);
3198 note_diff_add_change (event, MidiModel::NoteDiffCommand::NoteNumber, new_note);
3202 MidiRegionView::trim_note (NoteBase* event, Evoral::Beats front_delta, Evoral::Beats end_delta)
3204 bool change_start = false;
3205 bool change_length = false;
3206 Evoral::Beats new_start;
3207 Evoral::Beats new_length;
3209 /* NOTE: the semantics of the two delta arguments are slightly subtle:
3211 front_delta: if positive - move the start of the note later in time (shortening it)
3212 if negative - move the start of the note earlier in time (lengthening it)
3214 end_delta: if positive - move the end of the note later in time (lengthening it)
3215 if negative - move the end of the note earlier in time (shortening it)
3218 if (!!front_delta) {
3219 if (front_delta < 0) {
3221 if (event->note()->time() < -front_delta) {
3222 new_start = Evoral::Beats();
3224 new_start = event->note()->time() + front_delta; // moves earlier
3227 /* start moved toward zero, so move the end point out to where it used to be.
3228 Note that front_delta is negative, so this increases the length.
3231 new_length = event->note()->length() - front_delta;
3232 change_start = true;
3233 change_length = true;
3237 Evoral::Beats new_pos = event->note()->time() + front_delta;
3239 if (new_pos < event->note()->end_time()) {
3240 new_start = event->note()->time() + front_delta;
3241 /* start moved toward the end, so move the end point back to where it used to be */
3242 new_length = event->note()->length() - front_delta;
3243 change_start = true;
3244 change_length = true;
3251 bool can_change = true;
3252 if (end_delta < 0) {
3253 if (event->note()->length() < -end_delta) {
3259 new_length = event->note()->length() + end_delta;
3260 change_length = true;
3265 note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_start);
3268 if (change_length) {
3269 note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, new_length);
3274 MidiRegionView::change_note_channel (NoteBase* event, int8_t chn, bool relative)
3276 uint8_t new_channel;
3280 if (event->note()->channel() < -chn) {
3283 new_channel = event->note()->channel() + chn;
3286 new_channel = event->note()->channel() + chn;
3289 new_channel = (uint8_t) chn;
3292 note_diff_add_change (event, MidiModel::NoteDiffCommand::Channel, new_channel);
3296 MidiRegionView::change_note_time (NoteBase* event, Evoral::Beats delta, bool relative)
3298 Evoral::Beats new_time;
3302 if (event->note()->time() < -delta) {
3303 new_time = Evoral::Beats();
3305 new_time = event->note()->time() + delta;
3308 new_time = event->note()->time() + delta;
3314 note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_time);
3318 MidiRegionView::change_note_length (NoteBase* event, Evoral::Beats t)
3320 note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, t);
3324 MidiRegionView::change_velocities (bool up, bool fine, bool allow_smush, bool all_together)
3329 if (_selection.empty()) {
3344 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3345 if ((*i)->note()->velocity() < -delta || (*i)->note()->velocity() + delta > 127) {
3351 start_note_diff_command (_("change velocities"));
3353 for (Selection::iterator i = _selection.begin(); i != _selection.end();) {
3354 Selection::iterator next = i;
3358 if (i == _selection.begin()) {
3359 change_note_velocity (*i, delta, true);
3360 value = (*i)->note()->velocity() + delta;
3362 change_note_velocity (*i, value, false);
3366 change_note_velocity (*i, delta, true);
3375 if (!_selection.empty()) {
3377 snprintf (buf, sizeof (buf), "Vel %d",
3378 (int) (*_selection.begin())->note()->velocity());
3379 show_verbose_cursor (buf, 10, 10);
3385 MidiRegionView::transpose (bool up, bool fine, bool allow_smush)
3387 if (_selection.empty()) {
3404 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3406 if ((int8_t) (*i)->note()->note() + delta <= 0) {
3410 if ((int8_t) (*i)->note()->note() + delta > 127) {
3417 start_note_diff_command (_("transpose"));
3419 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3420 Selection::iterator next = i;
3422 change_note_note (*i, delta, true);
3430 MidiRegionView::change_note_lengths (bool fine, bool shorter, Evoral::Beats delta, bool start, bool end)
3434 delta = Evoral::Beats(1.0/128.0);
3436 /* grab the current grid distance */
3437 delta = get_grid_beats(_region->position());
3445 start_note_diff_command (_("change note lengths"));
3447 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3448 Selection::iterator next = i;
3451 /* note the negation of the delta for start */
3454 (start ? -delta : Evoral::Beats()),
3455 (end ? delta : Evoral::Beats()));
3464 MidiRegionView::nudge_notes (bool forward, bool fine)
3466 if (_selection.empty()) {
3470 /* pick a note as the point along the timeline to get the nudge distance.
3471 its not necessarily the earliest note, so we may want to pull the notes out
3472 into a vector and sort before using the first one.
3475 const framepos_t ref_point = source_beats_to_absolute_frames ((*(_selection.begin()))->note()->time());
3476 Evoral::Beats delta;
3480 /* non-fine, move by 1 bar regardless of snap */
3481 delta = Evoral::Beats(trackview.session()->tempo_map().meter_at_frame (ref_point).divisions_per_bar());
3483 } else if (trackview.editor().snap_mode() == Editing::SnapOff) {
3485 /* grid is off - use nudge distance */
3488 const framecnt_t distance = trackview.editor().get_nudge_distance (ref_point, unused);
3489 delta = region_frames_to_region_beats (fabs ((double)distance));
3495 MusicFrame next_pos (ref_point, 0);
3497 if (max_framepos - 1 < next_pos.frame) {
3498 next_pos.frame += 1;
3501 if (next_pos.frame == 0) {
3504 next_pos.frame -= 1;
3507 trackview.editor().snap_to (next_pos, (forward ? RoundUpAlways : RoundDownAlways), false);
3508 const framecnt_t distance = ref_point - next_pos.frame;
3509 delta = region_frames_to_region_beats (fabs ((double)distance));
3520 start_note_diff_command (_("nudge"));
3522 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3523 Selection::iterator next = i;
3525 change_note_time (*i, delta, true);
3533 MidiRegionView::change_channel(uint8_t channel)
3535 start_note_diff_command(_("change channel"));
3536 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3537 note_diff_add_change (*i, MidiModel::NoteDiffCommand::Channel, channel);
3545 MidiRegionView::note_entered(NoteBase* ev)
3549 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3551 if (_mouse_state == SelectTouchDragging) {
3553 note_selected (ev, true);
3555 } else if (editor->current_mouse_mode() == MouseContent) {
3557 remove_ghost_note ();
3558 show_verbose_cursor (ev->note ());
3560 } else if (editor->current_mouse_mode() == MouseDraw) {
3562 remove_ghost_note ();
3563 show_verbose_cursor (ev->note ());
3568 MidiRegionView::note_left (NoteBase*)
3572 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3573 (*i)->hide_velocity ();
3576 hide_verbose_cursor ();
3580 MidiRegionView::patch_entered (PatchChange* p)
3583 s << _("Bank ") << (p->patch()->bank() + MIDI_BP_ZERO) << '\n'
3584 << instrument_info().get_patch_name_without (p->patch()->bank(), p->patch()->program(), p->patch()->channel()) << '\n'
3585 << _("Channel ") << ((int) p->patch()->channel() + 1);
3586 show_verbose_cursor (s.str(), 10, 20);
3587 p->item().grab_focus();
3591 MidiRegionView::patch_left (PatchChange *)
3593 hide_verbose_cursor ();
3594 /* focus will transfer back via the enter-notify event sent to this
3600 MidiRegionView::sysex_entered (SysEx* p)
3604 // need a way to extract text from p->_flag->_text
3606 // show_verbose_cursor (s.str(), 10, 20);
3607 p->item().grab_focus();
3611 MidiRegionView::sysex_left (SysEx *)
3613 hide_verbose_cursor ();
3614 /* focus will transfer back via the enter-notify event sent to this
3620 MidiRegionView::note_mouse_position (float x_fraction, float /*y_fraction*/, bool can_set_cursor)
3622 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3623 Editing::MouseMode mm = editor->current_mouse_mode();
3624 bool trimmable = (mm == MouseContent || mm == MouseTimeFX || mm == MouseDraw);
3626 Editor::EnterContext* ctx = editor->get_enter_context(NoteItem);
3627 if (can_set_cursor && ctx) {
3628 if (trimmable && x_fraction > 0.0 && x_fraction < 0.2) {
3629 ctx->cursor_ctx->change(editor->cursors()->left_side_trim);
3630 } else if (trimmable && x_fraction >= 0.8 && x_fraction < 1.0) {
3631 ctx->cursor_ctx->change(editor->cursors()->right_side_trim);
3633 ctx->cursor_ctx->change(editor->cursors()->grabber_note);
3639 MidiRegionView::get_fill_color() const
3641 const std::string mod_name = (_dragging ? "dragging region" :
3642 trackview.editor().internal_editing() ? "editable region" :
3645 return UIConfiguration::instance().color_mod ("selected region base", mod_name);
3646 } else if ((!UIConfiguration::instance().get_show_name_highlight() || high_enough_for_name) &&
3647 !UIConfiguration::instance().get_color_regions_using_track_color()) {
3648 return UIConfiguration::instance().color_mod ("midi frame base", mod_name);
3650 return UIConfiguration::instance().color_mod (fill_color, mod_name);
3654 MidiRegionView::midi_channel_mode_changed ()
3656 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3657 uint16_t mask = mtv->midi_track()->get_playback_channel_mask();
3658 ChannelMode mode = mtv->midi_track()->get_playback_channel_mode ();
3660 if (mode == ForceChannel) {
3661 mask = 0xFFFF; // Show all notes as active (below)
3664 // Update notes for selection
3665 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3666 i->second->on_channel_selection_change (mask);
3669 _patch_changes.clear ();
3670 display_patch_changes ();
3674 MidiRegionView::instrument_settings_changed ()
3680 MidiRegionView::cut_copy_clear (Editing::CutCopyOp op)
3682 if (_selection.empty()) {
3686 PublicEditor& editor (trackview.editor());
3690 /* XXX what to do ? */
3694 editor.get_cut_buffer().add (selection_as_cut_buffer());
3702 start_note_diff_command();
3704 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3711 note_diff_remove_note (*i);
3721 MidiRegionView::selection_as_cut_buffer () const
3725 for (Selection::const_iterator i = _selection.begin(); i != _selection.end(); ++i) {
3726 NoteType* n = (*i)->note().get();
3727 notes.insert (boost::shared_ptr<NoteType> (new NoteType (*n)));
3730 MidiCutBuffer* cb = new MidiCutBuffer (trackview.session());
3736 /** This method handles undo */
3738 MidiRegionView::paste (framepos_t pos, const ::Selection& selection, PasteContext& ctx, const int32_t sub_num)
3740 bool commit = false;
3741 // Paste notes, if available
3742 MidiNoteSelection::const_iterator m = selection.midi_notes.get_nth(ctx.counts.n_notes());
3743 if (m != selection.midi_notes.end()) {
3744 ctx.counts.increase_n_notes();
3745 if (!(*m)->empty()) {
3748 paste_internal(pos, ctx.count, ctx.times, **m);
3751 // Paste control points to automation children, if available
3752 typedef RouteTimeAxisView::AutomationTracks ATracks;
3753 const ATracks& atracks = midi_view()->automation_tracks();
3754 for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) {
3755 if (a->second->paste(pos, selection, ctx, sub_num)) {
3757 trackview.editor().begin_reversible_command (Operations::paste);
3764 trackview.editor().commit_reversible_command ();
3769 /** This method handles undo */
3771 MidiRegionView::paste_internal (framepos_t pos, unsigned paste_count, float times, const MidiCutBuffer& mcb)
3777 start_note_diff_command (_("paste"));
3779 const Evoral::Beats snap_beats = get_grid_beats(pos);
3780 const Evoral::Beats first_time = (*mcb.notes().begin())->time();
3781 const Evoral::Beats last_time = (*mcb.notes().rbegin())->end_time();
3782 const Evoral::Beats duration = last_time - first_time;
3783 const Evoral::Beats snap_duration = duration.snap_to(snap_beats);
3784 const Evoral::Beats paste_offset = snap_duration * paste_count;
3785 const Evoral::Beats quarter_note = absolute_frames_to_source_beats(pos) + paste_offset;
3786 Evoral::Beats end_point = Evoral::Beats();
3788 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste data spans from %1 to %2 (%3) ; paste pos beats = %4 (based on %5 - %6)\n",
3791 duration, pos, _region->position(),
3794 clear_editor_note_selection ();
3796 for (int n = 0; n < (int) times; ++n) {
3798 for (Notes::const_iterator i = mcb.notes().begin(); i != mcb.notes().end(); ++i) {
3800 boost::shared_ptr<NoteType> copied_note (new NoteType (*((*i).get())));
3801 copied_note->set_time (quarter_note + copied_note->time() - first_time);
3802 copied_note->set_id (Evoral::next_event_id());
3804 /* make all newly added notes selected */
3806 note_diff_add_note (copied_note, true);
3807 end_point = copied_note->end_time();
3811 /* if we pasted past the current end of the region, extend the region */
3813 framepos_t end_frame = source_beats_to_absolute_frames (end_point);
3814 framepos_t region_end = _region->position() + _region->length() - 1;
3816 if (end_frame > region_end) {
3818 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste extended region from %1 to %2\n", region_end, end_frame));
3820 _region->clear_changes ();
3821 /* we probably need to get the snap modifier somehow to make this correct for non-musical use */
3822 _region->set_length (end_frame - _region->position(), trackview.editor().get_grid_music_divisions (0));
3823 trackview.session()->add_command (new StatefulDiffCommand (_region));
3829 struct EventNoteTimeEarlyFirstComparator {
3830 bool operator() (NoteBase* a, NoteBase* b) {
3831 return a->note()->time() < b->note()->time();
3836 MidiRegionView::goto_next_note (bool add_to_selection)
3838 bool use_next = false;
3840 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3841 uint16_t const channel_mask = mtv->midi_track()->get_playback_channel_mask();
3842 NoteBase* first_note = 0;
3844 MidiModel::ReadLock lock(_model->read_lock());
3845 MidiModel::Notes& notes (_model->notes());
3847 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
3849 if ((cne = find_canvas_note (*n))) {
3851 if (!first_note && (channel_mask & (1 << (*n)->channel()))) {
3855 if (cne->selected()) {
3858 } else if (use_next) {
3859 if (channel_mask & (1 << (*n)->channel())) {
3860 if (!add_to_selection) {
3861 unique_select (cne);
3863 note_selected (cne, true, false);
3872 /* use the first one */
3874 if (!_events.empty() && first_note) {
3875 unique_select (first_note);
3880 MidiRegionView::goto_previous_note (bool add_to_selection)
3882 bool use_next = false;
3884 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3885 uint16_t const channel_mask = mtv->midi_track()->get_playback_channel_mask ();
3886 NoteBase* last_note = 0;
3888 MidiModel::ReadLock lock(_model->read_lock());
3889 MidiModel::Notes& notes (_model->notes());
3891 for (MidiModel::Notes::reverse_iterator n = notes.rbegin(); n != notes.rend(); ++n) {
3893 if ((cne = find_canvas_note (*n))) {
3895 if (!last_note && (channel_mask & (1 << (*n)->channel()))) {
3899 if (cne->selected()) {
3903 } else if (use_next) {
3904 if (channel_mask & (1 << (*n)->channel())) {
3905 if (!add_to_selection) {
3906 unique_select (cne);
3908 note_selected (cne, true, false);
3917 /* use the last one */
3919 if (!_events.empty() && last_note) {
3920 unique_select (last_note);
3925 MidiRegionView::selection_as_notelist (Notes& selected, bool allow_all_if_none_selected)
3927 bool had_selected = false;
3929 /* we previously time sorted events here, but Notes is a multiset sorted by time */
3931 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3932 if (i->second->selected()) {
3933 selected.insert (i->first);
3934 had_selected = true;
3938 if (allow_all_if_none_selected && !had_selected) {
3939 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3940 selected.insert (i->first);
3946 MidiRegionView::update_ghost_note (double x, double y, uint32_t state)
3948 x = std::max(0.0, x);
3950 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3955 _note_group->canvas_to_item (x, y);
3957 PublicEditor& editor = trackview.editor ();
3959 framepos_t const unsnapped_frame = editor.pixel_to_sample (x);
3961 const int32_t divisions = editor.get_grid_music_divisions (state);
3962 const bool shift_snap = midi_view()->note_mode() != Percussive;
3963 const Evoral::Beats snapped_beats = snap_frame_to_grid_underneath (unsnapped_frame, divisions, shift_snap);
3965 /* prevent Percussive mode from displaying a ghost hit at region end */
3966 if (!shift_snap && snapped_beats >= midi_region()->start_beats() + midi_region()->length_beats()) {
3967 _ghost_note->hide();
3968 hide_verbose_cursor ();
3972 /* ghost note may have been snapped before region */
3973 if (_ghost_note && snapped_beats.to_double() < 0.0) {
3974 _ghost_note->hide();
3977 } else if (_ghost_note) {
3978 _ghost_note->show();
3981 /* calculate time in beats relative to start of source */
3982 const Evoral::Beats length = get_grid_beats(unsnapped_frame + _region->position());
3984 _ghost_note->note()->set_time (snapped_beats);
3985 _ghost_note->note()->set_length (length);
3986 _ghost_note->note()->set_note (y_to_note (y));
3987 _ghost_note->note()->set_channel (mtv->get_channel_for_add ());
3988 _ghost_note->note()->set_velocity (get_velocity_for_add (snapped_beats));
3989 /* the ghost note does not appear in ghost regions, so pass false in here */
3990 update_note (_ghost_note, false);
3992 show_verbose_cursor (_ghost_note->note ());
3996 MidiRegionView::create_ghost_note (double x, double y, uint32_t state)
3998 remove_ghost_note ();
4000 boost::shared_ptr<NoteType> g (new NoteType);
4001 if (midi_view()->note_mode() == Sustained) {
4002 _ghost_note = new Note (*this, _note_group, g);
4004 _ghost_note = new Hit (*this, _note_group, 10, g);
4006 _ghost_note->set_ignore_events (true);
4007 _ghost_note->set_outline_color (0x000000aa);
4008 update_ghost_note (x, y, state);
4009 _ghost_note->show ();
4011 show_verbose_cursor (_ghost_note->note ());
4015 MidiRegionView::remove_ghost_note ()
4022 MidiRegionView::hide_verbose_cursor ()
4024 trackview.editor().verbose_cursor()->hide ();
4025 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
4027 mtv->set_note_highlight (NO_MIDI_NOTE);
4032 MidiRegionView::snap_changed ()
4038 create_ghost_note (_last_ghost_x, _last_ghost_y, 0);
4042 MidiRegionView::drop_down_keys ()
4044 _mouse_state = None;
4048 MidiRegionView::maybe_select_by_position (GdkEventButton* ev, double /*x*/, double y)
4050 /* XXX: This is dead code. What was it for? */
4052 double note = y_to_note(y);
4054 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
4056 uint16_t chn_mask = mtv->midi_track()->get_playback_channel_mask();
4058 if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
4059 get_events (e, Evoral::Sequence<Evoral::Beats>::PitchGreaterThanOrEqual, (uint8_t) floor (note), chn_mask);
4060 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
4061 get_events (e, Evoral::Sequence<Evoral::Beats>::PitchLessThanOrEqual, (uint8_t) floor (note), chn_mask);
4066 bool add_mrv_selection = false;
4068 if (_selection.empty()) {
4069 add_mrv_selection = true;
4072 for (Events::iterator i = e.begin(); i != e.end(); ++i) {
4073 if (_selection.insert (i->second).second) {
4074 i->second->set_selected (true);
4078 if (add_mrv_selection) {
4079 PublicEditor& editor (trackview.editor());
4080 editor.get_selection().add (this);
4085 MidiRegionView::color_handler ()
4087 RegionView::color_handler ();
4089 _patch_change_outline = UIConfiguration::instance().color ("midi patch change outline");
4090 _patch_change_fill = UIConfiguration::instance().color_mod ("midi patch change fill", "midi patch change fill");
4092 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
4093 i->second->set_selected (i->second->selected()); // will change color
4096 /* XXX probably more to do here */
4100 MidiRegionView::enable_display (bool yn)
4102 RegionView::enable_display (yn);
4106 MidiRegionView::show_step_edit_cursor (Evoral::Beats pos)
4108 if (_step_edit_cursor == 0) {
4109 ArdourCanvas::Item* const group = get_canvas_group();
4111 _step_edit_cursor = new ArdourCanvas::Rectangle (group);
4112 _step_edit_cursor->set_y0 (0);
4113 _step_edit_cursor->set_y1 (midi_stream_view()->contents_height());
4114 _step_edit_cursor->set_fill_color (RGBA_TO_UINT (45,0,0,90));
4115 _step_edit_cursor->set_outline_color (RGBA_TO_UINT (85,0,0,90));
4118 move_step_edit_cursor (pos);
4119 _step_edit_cursor->show ();
4123 MidiRegionView::move_step_edit_cursor (Evoral::Beats pos)
4125 _step_edit_cursor_position = pos;
4127 if (_step_edit_cursor) {
4128 double pixel = trackview.editor().sample_to_pixel (region_beats_to_region_frames (pos));
4129 _step_edit_cursor->set_x0 (pixel);
4130 set_step_edit_cursor_width (_step_edit_cursor_width);
4135 MidiRegionView::hide_step_edit_cursor ()
4137 if (_step_edit_cursor) {
4138 _step_edit_cursor->hide ();
4143 MidiRegionView::set_step_edit_cursor_width (Evoral::Beats beats)
4145 _step_edit_cursor_width = beats;
4147 if (_step_edit_cursor) {
4148 _step_edit_cursor->set_x1 (_step_edit_cursor->x0() + trackview.editor().sample_to_pixel (
4149 region_beats_to_region_frames (_step_edit_cursor_position + beats)
4150 - region_beats_to_region_frames (_step_edit_cursor_position)));
4154 /** Called when a diskstream on our track has received some data. Update the view, if applicable.
4155 * @param w Source that the data will end up in.
4158 MidiRegionView::data_recorded (boost::weak_ptr<MidiSource> w)
4160 if (!_active_notes) {
4161 /* we aren't actively being recorded to */
4165 boost::shared_ptr<MidiSource> src = w.lock ();
4166 if (!src || src != midi_region()->midi_source()) {
4167 /* recorded data was not destined for our source */
4171 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (&trackview);
4173 boost::shared_ptr<MidiBuffer> buf = mtv->midi_track()->get_gui_feed_buffer ();
4175 framepos_t back = max_framepos;
4177 for (MidiBuffer::iterator i = buf->begin(); i != buf->end(); ++i) {
4178 const Evoral::Event<MidiBuffer::TimeType>& ev = *i;
4180 if (ev.is_channel_event()) {
4181 if (get_channel_mode() == FilterChannels) {
4182 if (((uint16_t(1) << ev.channel()) & get_selected_channels()) == 0) {
4188 /* convert from session frames to source beats */
4189 Evoral::Beats const time_beats = _source_relative_time_converter.from(
4190 ev.time() - src->timeline_position() + _region->start());
4192 if (ev.type() == MIDI_CMD_NOTE_ON) {
4193 boost::shared_ptr<NoteType> note (
4194 new NoteType (ev.channel(), time_beats, Evoral::Beats(), ev.note(), ev.velocity()));
4196 add_note (note, true);
4198 /* fix up our note range */
4199 if (ev.note() < _current_range_min) {
4200 midi_stream_view()->apply_note_range (ev.note(), _current_range_max, true);
4201 } else if (ev.note() > _current_range_max) {
4202 midi_stream_view()->apply_note_range (_current_range_min, ev.note(), true);
4205 } else if (ev.type() == MIDI_CMD_NOTE_OFF) {
4206 resolve_note (ev.note (), time_beats);
4212 midi_stream_view()->check_record_layers (region(), back);
4216 MidiRegionView::trim_front_starting ()
4218 /* We used to eparent the note group to the region view's parent, so that it didn't change.
4224 MidiRegionView::trim_front_ending ()
4226 if (_region->start() < 0) {
4227 /* Trim drag made start time -ve; fix this */
4228 midi_region()->fix_negative_start ();
4233 MidiRegionView::edit_patch_change (PatchChange* pc)
4235 PatchChangeDialog d (&_source_relative_time_converter, trackview.session(), *pc->patch (), instrument_info(), Gtk::Stock::APPLY, true);
4237 int response = d.run();
4240 case Gtk::RESPONSE_ACCEPT:
4242 case Gtk::RESPONSE_REJECT:
4243 delete_patch_change (pc);
4249 change_patch_change (pc->patch(), d.patch ());
4253 MidiRegionView::delete_sysex (SysEx* /*sysex*/)
4256 // sysyex object doesn't have a pointer to a sysex event
4257 // MidiModel::SysExDiffCommand* c = _model->new_sysex_diff_command (_("delete sysex"));
4258 // c->remove (sysex->sysex());
4259 // _model->apply_command (*trackview.session(), c);
4261 //_sys_exes.clear ();
4262 // display_sysexes();
4266 MidiRegionView::get_note_name (boost::shared_ptr<NoteType> n, uint8_t note_value) const
4268 using namespace MIDI::Name;
4271 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
4273 boost::shared_ptr<MasterDeviceNames> device_names(mtv->get_device_names());
4275 MIDI::Name::PatchPrimaryKey patch_key;
4276 get_patch_key_at(n->time(), n->channel(), patch_key);
4277 name = device_names->note_name(mtv->gui_property(X_("midnam-custom-device-mode")),
4280 patch_key.program(),
4286 snprintf (buf, sizeof (buf), "%d %s\nCh %d Vel %d",
4288 name.empty() ? ParameterDescriptor::midi_note_name (note_value).c_str() : name.c_str(),
4289 (int) n->channel() + 1,
4290 (int) n->velocity());
4296 MidiRegionView::show_verbose_cursor_for_new_note_value(boost::shared_ptr<NoteType> current_note,
4297 uint8_t new_value) const
4299 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
4301 mtv->set_note_highlight (new_value);
4304 show_verbose_cursor(get_note_name(current_note, new_value), 10, 20);
4308 MidiRegionView::show_verbose_cursor (boost::shared_ptr<NoteType> n) const
4310 show_verbose_cursor_for_new_note_value(n, n->note());
4314 MidiRegionView::show_verbose_cursor (string const & text, double xoffset, double yoffset) const
4316 trackview.editor().verbose_cursor()->set (text);
4317 trackview.editor().verbose_cursor()->show ();
4318 trackview.editor().verbose_cursor()->set_offset (ArdourCanvas::Duple (xoffset, yoffset));
4322 MidiRegionView::get_velocity_for_add (MidiModel::TimeType time) const
4324 if (_model->notes().empty()) {
4325 return 0x40; // No notes, use default
4328 MidiModel::Notes::const_iterator m = _model->note_lower_bound(time);
4329 if (m == _model->notes().begin()) {
4330 // Before the start, use the velocity of the first note
4331 return (*m)->velocity();
4332 } else if (m == _model->notes().end()) {
4333 // Past the end, use the velocity of the last note
4335 return (*m)->velocity();
4338 // Interpolate velocity of surrounding notes
4339 MidiModel::Notes::const_iterator n = m;
4342 const double frac = ((time - (*n)->time()).to_double() /
4343 ((*m)->time() - (*n)->time()).to_double());
4345 return (*n)->velocity() + (frac * ((*m)->velocity() - (*n)->velocity()));
4348 /** @param p A session framepos.
4349 * @param divisions beat division to snap given by Editor::get_grid_music_divisions() where
4350 * bar is -1, 0 is audio samples and a positive integer is beat subdivisions.
4351 * @return beat duration of p snapped to the grid subdivision underneath it.
4354 MidiRegionView::snap_frame_to_grid_underneath (framepos_t p, int32_t divisions, bool shift_snap) const
4356 TempoMap& map (trackview.session()->tempo_map());
4357 double eqaf = map.exact_qn_at_frame (p + _region->position(), divisions);
4359 if (divisions != 0 && shift_snap) {
4360 const double qaf = map.quarter_note_at_frame (p + _region->position());
4361 /* Hack so that we always snap to the note that we are over, instead of snapping
4362 to the next one if we're more than halfway through the one we're over.
4364 const Evoral::Beats grid_beats = get_grid_beats (p + _region->position());
4365 const double rem = eqaf - qaf;
4367 eqaf -= grid_beats.to_double();
4370 const double session_start_off = _region->quarter_note() - midi_region()->start_beats();
4372 return Evoral::Beats (eqaf - session_start_off);
4376 MidiRegionView::get_channel_mode () const
4378 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (&trackview);
4379 return rtav->midi_track()->get_playback_channel_mode();
4383 MidiRegionView::get_selected_channels () const
4385 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (&trackview);
4386 return rtav->midi_track()->get_playback_channel_mask();
4391 MidiRegionView::get_grid_beats(framepos_t pos) const
4393 PublicEditor& editor = trackview.editor();
4394 bool success = false;
4395 Evoral::Beats beats = editor.get_grid_type_as_beats (success, pos);
4397 beats = Evoral::Beats(1);
4402 MidiRegionView::y_to_note (double y) const
4404 int const n = ((contents_height() - y) / contents_height() * (double)(_current_range_max - _current_range_min + 1))
4405 + _current_range_min;
4409 } else if (n > 127) {
4413 /* min due to rounding and/or off-by-one errors */
4414 return min ((uint8_t) n, _current_range_max);
4418 MidiRegionView::note_to_y(uint8_t note) const
4420 return contents_height() - (note + 1 - _current_range_min) * note_height() + 1;
4424 MidiRegionView::session_relative_qn (double qn) const
4426 return qn + (region()->quarter_note() - midi_region()->start_beats());