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 "pbd/memento_command.h"
31 #include "pbd/stateful_diff_command.h"
33 #include "ardour/midi_model.h"
34 #include "ardour/midi_patch_manager.h"
35 #include "ardour/midi_region.h"
36 #include "ardour/midi_source.h"
37 #include "ardour/midi_track.h"
38 #include "ardour/operations.h"
39 #include "ardour/session.h"
41 #include "evoral/Parameter.hpp"
42 #include "evoral/MIDIEvent.hpp"
43 #include "evoral/Control.hpp"
44 #include "evoral/midi_util.h"
46 #include "canvas/debug.h"
47 #include "canvas/text.h"
49 #include "automation_region_view.h"
50 #include "automation_time_axis.h"
51 #include "control_point.h"
54 #include "editor_drag.h"
55 #include "ghostregion.h"
56 #include "gui_thread.h"
57 #include "item_counts.h"
59 #include "midi_channel_dialog.h"
60 #include "midi_cut_buffer.h"
61 #include "midi_list_editor.h"
62 #include "midi_region_view.h"
63 #include "midi_streamview.h"
64 #include "midi_time_axis.h"
65 #include "midi_util.h"
66 #include "midi_velocity_dialog.h"
67 #include "mouse_cursors.h"
68 #include "note_player.h"
69 #include "paste_context.h"
70 #include "public_editor.h"
71 #include "route_time_axis.h"
72 #include "rgb_macros.h"
73 #include "selection.h"
74 #include "streamview.h"
75 #include "patch_change_dialog.h"
76 #include "verbose_cursor.h"
77 #include "ardour_ui.h"
80 #include "patch_change.h"
85 using namespace ARDOUR;
87 using namespace Editing;
89 using Gtkmm2ext::Keyboard;
91 PBD::Signal1<void, MidiRegionView *> MidiRegionView::SelectionCleared;
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())
106 , _note_group (new ArdourCanvas::Container (group))
107 , _note_diff_command (0)
109 , _step_edit_cursor (0)
110 , _step_edit_cursor_width (1.0)
111 , _step_edit_cursor_position (0.0)
112 , _channel_selection_scoped_note (0)
113 , _temporary_note_group (0)
116 , _sort_needed (true)
117 , _optimization_iterator (_events.end())
119 , _no_sound_notes (false)
122 , _grabbed_keyboard (false)
124 , pre_enter_cursor (0)
125 , pre_press_cursor (0)
126 , pre_note_enter_cursor (0)
129 CANVAS_DEBUG_NAME (_note_group, string_compose ("note group for %1", get_item_name()));
130 _note_group->raise_to_top();
131 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
133 Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&MidiRegionView::parameter_changed, this, _1), gui_context());
134 connect_to_diskstream ();
136 SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), boost::bind (&MidiRegionView::selection_cleared, this, _1), gui_context ());
139 MidiRegionView::MidiRegionView (ArdourCanvas::Container* parent,
140 RouteTimeAxisView& tv,
141 boost::shared_ptr<MidiRegion> r,
143 uint32_t basic_color,
144 TimeAxisViewItem::Visibility visibility)
145 : RegionView (parent, tv, r, spu, basic_color, false, visibility)
146 , _current_range_min(0)
147 , _current_range_max(0)
148 , _region_relative_time_converter(r->session().tempo_map(), r->position())
149 , _source_relative_time_converter(r->session().tempo_map(), r->position() - r->start())
151 , _note_group (new ArdourCanvas::Container (parent))
152 , _note_diff_command (0)
154 , _step_edit_cursor (0)
155 , _step_edit_cursor_width (1.0)
156 , _step_edit_cursor_position (0.0)
157 , _channel_selection_scoped_note (0)
158 , _temporary_note_group (0)
161 , _sort_needed (true)
162 , _optimization_iterator (_events.end())
164 , _no_sound_notes (false)
167 , _grabbed_keyboard (false)
169 , pre_enter_cursor (0)
170 , pre_press_cursor (0)
171 , pre_note_enter_cursor (0)
174 CANVAS_DEBUG_NAME (_note_group, string_compose ("note group for %1", get_item_name()));
175 _note_group->raise_to_top();
177 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
179 connect_to_diskstream ();
181 SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), boost::bind (&MidiRegionView::selection_cleared, this, _1), gui_context ());
185 MidiRegionView::parameter_changed (std::string const & p)
187 if (p == "display-first-midi-bank-as-zero") {
188 if (_enable_display) {
194 MidiRegionView::MidiRegionView (const MidiRegionView& other)
195 : sigc::trackable(other)
197 , _current_range_min(0)
198 , _current_range_max(0)
199 , _region_relative_time_converter(other.region_relative_time_converter())
200 , _source_relative_time_converter(other.source_relative_time_converter())
202 , _note_group (new ArdourCanvas::Container (get_canvas_group()))
203 , _note_diff_command (0)
205 , _step_edit_cursor (0)
206 , _step_edit_cursor_width (1.0)
207 , _step_edit_cursor_position (0.0)
208 , _channel_selection_scoped_note (0)
209 , _temporary_note_group (0)
212 , _sort_needed (true)
213 , _optimization_iterator (_events.end())
215 , _no_sound_notes (false)
218 , _grabbed_keyboard (false)
220 , pre_enter_cursor (0)
221 , pre_press_cursor (0)
222 , pre_note_enter_cursor (0)
228 MidiRegionView::MidiRegionView (const MidiRegionView& other, boost::shared_ptr<MidiRegion> region)
229 : RegionView (other, boost::shared_ptr<Region> (region))
230 , _current_range_min(0)
231 , _current_range_max(0)
232 , _region_relative_time_converter(other.region_relative_time_converter())
233 , _source_relative_time_converter(other.source_relative_time_converter())
235 , _note_group (new ArdourCanvas::Container (get_canvas_group()))
236 , _note_diff_command (0)
238 , _step_edit_cursor (0)
239 , _step_edit_cursor_width (1.0)
240 , _step_edit_cursor_position (0.0)
241 , _channel_selection_scoped_note (0)
242 , _temporary_note_group (0)
245 , _sort_needed (true)
246 , _optimization_iterator (_events.end())
248 , _no_sound_notes (false)
251 , _grabbed_keyboard (false)
253 , pre_enter_cursor (0)
254 , pre_press_cursor (0)
255 , pre_note_enter_cursor (0)
262 MidiRegionView::init (bool wfd)
264 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
266 NoteBase::NoteBaseDeleted.connect (note_delete_connection, MISSING_INVALIDATOR,
267 boost::bind (&MidiRegionView::maybe_remove_deleted_note_from_selection, this, _1),
271 midi_region()->midi_source(0)->load_model();
274 _model = midi_region()->midi_source(0)->model();
275 _enable_display = false;
277 RegionView::init (false);
279 set_height (trackview.current_height());
282 region_sync_changed ();
283 region_resized (ARDOUR::bounds_change);
288 _enable_display = true;
291 display_model (_model);
295 reset_width_dependent_items (_pixel_width);
297 group->raise_to_top();
299 midi_view()->midi_track()->PlaybackChannelModeChanged.connect (_channel_mode_changed_connection, invalidator (*this),
300 boost::bind (&MidiRegionView::midi_channel_mode_changed, this),
303 instrument_info().Changed.connect (_instrument_changed_connection, invalidator (*this),
304 boost::bind (&MidiRegionView::instrument_settings_changed, this), gui_context());
306 trackview.editor().SnapChanged.connect(snap_changed_connection, invalidator(*this),
307 boost::bind (&MidiRegionView::snap_changed, this),
310 Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&MidiRegionView::parameter_changed, this, _1), gui_context());
311 connect_to_diskstream ();
313 SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), boost::bind (&MidiRegionView::selection_cleared, this, _1), gui_context ());
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)
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);
350 const MouseMode m = trackview.editor().current_mouse_mode();
354 case GDK_ENTER_NOTIFY:
355 _last_event_x = ev->crossing.x;
356 _last_event_y = ev->crossing.y;
357 enter_notify(&ev->crossing);
358 // set entered_regionview (among other things)
359 return RegionView::canvas_group_event (ev);
361 case GDK_LEAVE_NOTIFY:
362 _last_event_x = ev->crossing.x;
363 _last_event_y = ev->crossing.y;
364 leave_notify(&ev->crossing);
365 // reset entered_regionview (among other things)
366 return RegionView::canvas_group_event (ev);
368 case GDK_2BUTTON_PRESS:
369 // cannot use double-click to exit internal mode if single-click is being used
370 if ((m != MouseDraw) &&
372 !Keyboard::modifier_state_contains (ev->button.state, Keyboard::insert_note_modifier()))) {
373 return trackview.editor().toggle_internal_editing_from_double_click (ev);
378 if (scroll (&ev->scroll)) {
384 return key_press (&ev->key);
386 case GDK_KEY_RELEASE:
387 return key_release (&ev->key);
389 case GDK_BUTTON_PRESS:
390 return button_press (&ev->button);
392 case GDK_BUTTON_RELEASE:
393 r = button_release (&ev->button);
398 case GDK_MOTION_NOTIFY:
399 _last_event_x = ev->motion.x;
400 _last_event_y = ev->motion.y;
401 return motion (&ev->motion);
407 return RegionView::canvas_group_event (ev);
411 MidiRegionView::enter_notify (GdkEventCrossing* ev)
413 trackview.editor().MouseModeChanged.connect (
414 _mouse_mode_connection, invalidator (*this), boost::bind (&MidiRegionView::mouse_mode_changed, this), gui_context ()
424 MidiRegionView::leave_notify (GdkEventCrossing*)
426 _mouse_mode_connection.disconnect ();
435 MidiRegionView::mouse_mode_changed ()
437 if (trackview.editor().internal_editing()) {
438 // Switched in to internal editing mode while entered
441 // Switched out of internal editing mode while entered
447 MidiRegionView::enter_internal()
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);
454 if (!_selection.empty()) {
455 // Grab keyboard for moving selected notes with arrow keys
456 Keyboard::magic_widget_grab_focus();
457 _grabbed_keyboard = true;
462 MidiRegionView::leave_internal()
464 trackview.editor().verbose_cursor()->hide ();
465 remove_ghost_note ();
467 if (_grabbed_keyboard) {
468 Keyboard::magic_widget_drop_focus();
469 _grabbed_keyboard = false;
474 MidiRegionView::button_press (GdkEventButton* ev)
476 if (ev->button != 1) {
480 Editor* editor = dynamic_cast<Editor *> (&trackview.editor());
481 MouseMode m = editor->current_mouse_mode();
483 if (m == MouseObject && Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())) {
484 pre_press_cursor = editor->get_canvas_cursor ();
485 editor->set_canvas_cursor (editor->cursors()->midi_pencil);
488 if (_mouse_state != SelectTouchDragging) {
490 _pressed_button = ev->button;
491 _mouse_state = Pressed;
496 _pressed_button = ev->button;
502 MidiRegionView::button_release (GdkEventButton* ev)
504 double event_x, event_y;
506 if (ev->button != 1) {
513 group->canvas_to_item (event_x, event_y);
516 PublicEditor& editor = trackview.editor ();
518 if (pre_press_cursor) {
519 dynamic_cast<Editor*>(&editor)->set_canvas_cursor (pre_press_cursor, false);
520 pre_press_cursor = 0;
523 switch (_mouse_state) {
524 case Pressed: // Clicked
526 switch (editor.current_mouse_mode()) {
528 /* no motion occured - simple click */
537 if (Keyboard::is_insert_note_event(ev)) {
539 double event_x, event_y;
543 group->canvas_to_item (event_x, event_y);
545 Evoral::MusicalTime beats = get_grid_beats(editor.pixel_to_sample(event_x));
547 /* Shorten the length by 1 tick so that we can add a new note at the next
548 grid snap without it overlapping this one.
550 beats -= Evoral::MusicalTime::tick();
552 create_note_at (editor.pixel_to_sample (event_x), event_y, beats, true);
559 Evoral::MusicalTime beats = get_grid_beats(editor.pixel_to_sample(event_x));
561 /* Shorten the length by 1 tick so that we can add a new note at the next
562 grid snap without it overlapping this one.
564 beats -= Evoral::MusicalTime::tick();
566 create_note_at (editor.pixel_to_sample (event_x), event_y, beats, true);
577 case SelectRectDragging:
579 editor.drags()->end_grab ((GdkEvent *) ev);
581 create_ghost_note (ev->x, ev->y);
593 MidiRegionView::motion (GdkEventMotion* ev)
595 PublicEditor& editor = trackview.editor ();
597 if (!_ghost_note && editor.current_mouse_mode() == MouseObject &&
598 Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier()) &&
599 _mouse_state != AddDragging) {
601 create_ghost_note (ev->x, ev->y);
603 } else if (_ghost_note && editor.current_mouse_mode() == MouseObject &&
604 Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())) {
606 update_ghost_note (ev->x, ev->y);
608 } else if (_ghost_note && editor.current_mouse_mode() == MouseObject) {
610 remove_ghost_note ();
611 editor.verbose_cursor()->hide ();
613 } else if (_ghost_note && editor.current_mouse_mode() == MouseDraw) {
615 update_ghost_note (ev->x, ev->y);
618 /* any motion immediately hides velocity text that may have been visible */
620 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
621 (*i)->hide_velocity ();
624 switch (_mouse_state) {
627 if (_pressed_button == 1) {
629 MouseMode m = editor.current_mouse_mode();
631 if (m == MouseDraw || (m == MouseObject && Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier()))) {
632 editor.drags()->set (new NoteCreateDrag (dynamic_cast<Editor *> (&editor), group, this), (GdkEvent *) ev);
633 _mouse_state = AddDragging;
634 remove_ghost_note ();
635 editor.verbose_cursor()->hide ();
637 } else if (m == MouseObject) {
638 editor.drags()->set (new MidiRubberbandSelectDrag (dynamic_cast<Editor *> (&editor), this), (GdkEvent *) ev);
639 if (!Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
642 _mouse_state = SelectRectDragging;
644 } else if (m == MouseRange) {
645 editor.drags()->set (new MidiVerticalSelectDrag (dynamic_cast<Editor *> (&editor), this), (GdkEvent *) ev);
646 _mouse_state = SelectVerticalDragging;
653 case SelectRectDragging:
654 case SelectVerticalDragging:
656 editor.drags()->motion_handler ((GdkEvent *) ev, false);
659 case SelectTouchDragging:
667 /* we may be dragging some non-note object (eg. patch-change, sysex)
670 return editor.drags()->motion_handler ((GdkEvent *) ev, false);
675 MidiRegionView::scroll (GdkEventScroll* ev)
677 if (_selection.empty()) {
681 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
682 /* XXX: bit of a hack; allow PrimaryModifier scroll through so that
683 it still works for zoom.
688 trackview.editor().verbose_cursor()->hide ();
690 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
691 bool together = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
693 if (ev->direction == GDK_SCROLL_UP) {
694 change_velocities (true, fine, false, together);
695 } else if (ev->direction == GDK_SCROLL_DOWN) {
696 change_velocities (false, fine, false, together);
698 /* left, right: we don't use them */
706 MidiRegionView::key_press (GdkEventKey* ev)
708 /* since GTK bindings are generally activated on press, and since
709 detectable auto-repeat is the name of the game and only sends
710 repeated presses, carry out key actions at key press, not release.
713 bool unmodified = Keyboard::no_modifier_keys_pressed (ev);
715 if (unmodified && (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R)) {
716 _mouse_state = SelectTouchDragging;
719 } else if (ev->keyval == GDK_Escape && unmodified) {
723 } else if (unmodified && (ev->keyval == GDK_comma || ev->keyval == GDK_period)) {
725 bool start = (ev->keyval == GDK_comma);
726 bool end = (ev->keyval == GDK_period);
727 bool shorter = Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier);
728 bool fine = Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
730 change_note_lengths (fine, shorter, Evoral::MusicalTime(), start, end);
734 } else if ((ev->keyval == GDK_BackSpace || ev->keyval == GDK_Delete) && unmodified) {
736 if (_selection.empty()) {
743 } else if (ev->keyval == GDK_Tab) {
745 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
746 goto_previous_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
748 goto_next_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
752 } else if (ev->keyval == GDK_ISO_Left_Tab) {
754 /* Shift-TAB generates ISO Left Tab, for some reason */
756 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
757 goto_previous_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
759 goto_next_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
765 } else if (ev->keyval == GDK_Up) {
767 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
768 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
769 bool together = Keyboard::modifier_state_contains (ev->state, Keyboard::Level4Modifier);
771 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
772 change_velocities (true, fine, allow_smush, together);
774 transpose (true, fine, allow_smush);
778 } else if (ev->keyval == GDK_Down) {
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 (false, fine, allow_smush, together);
787 transpose (false, fine, allow_smush);
791 } else if (ev->keyval == GDK_Left) {
793 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
794 nudge_notes (false, fine);
797 } else if (ev->keyval == GDK_Right) {
799 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
800 nudge_notes (true, fine);
803 } else if (ev->keyval == GDK_c && unmodified) {
807 } else if (ev->keyval == GDK_v && unmodified) {
816 MidiRegionView::key_release (GdkEventKey* ev)
818 if ((_mouse_state == SelectTouchDragging) && (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R)) {
826 MidiRegionView::channel_edit ()
828 if (_selection.empty()) {
832 /* pick a note somewhat at random (since Selection is a set<>) to
833 * provide the "current" channel for the dialog.
836 uint8_t current_channel = (*_selection.begin())->note()->channel ();
837 MidiChannelDialog channel_dialog (current_channel);
838 int ret = channel_dialog.run ();
841 case Gtk::RESPONSE_OK:
847 uint8_t new_channel = channel_dialog.active_channel ();
849 start_note_diff_command (_("channel edit"));
851 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
852 Selection::iterator next = i;
854 change_note_channel (*i, new_channel);
862 MidiRegionView::velocity_edit ()
864 if (_selection.empty()) {
868 /* pick a note somewhat at random (since Selection is a set<>) to
869 * provide the "current" velocity for the dialog.
872 uint8_t current_velocity = (*_selection.begin())->note()->velocity ();
873 MidiVelocityDialog velocity_dialog (current_velocity);
874 int ret = velocity_dialog.run ();
877 case Gtk::RESPONSE_OK:
883 uint8_t new_velocity = velocity_dialog.velocity ();
885 start_note_diff_command (_("velocity edit"));
887 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
888 Selection::iterator next = i;
890 change_note_velocity (*i, new_velocity, false);
898 MidiRegionView::show_list_editor ()
901 _list_editor = new MidiListEditor (trackview.session(), midi_region(), midi_view()->midi_track());
903 _list_editor->present ();
906 /** Add a note to the model, and the view, at a canvas (click) coordinate.
907 * \param t time in frames relative to the position of the region
908 * \param y vertical position in pixels
909 * \param length duration of the note in beats
910 * \param snap_t true to snap t to the grid, otherwise false.
913 MidiRegionView::create_note_at (framepos_t t, double y, Evoral::MusicalTime length, bool snap_t)
915 if (length < 2 * DBL_EPSILON) {
919 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
920 MidiStreamView* const view = mtv->midi_view();
922 const double note = view->y_to_note(y);
924 // Start of note in frames relative to region start
926 framecnt_t grid_frames;
927 t = snap_frame_to_grid_underneath (t, grid_frames);
930 const boost::shared_ptr<NoteType> new_note (
931 new NoteType (mtv->get_channel_for_add (),
932 region_frames_to_region_beats(t + _region->start()),
934 (uint8_t)note, 0x40));
936 if (_model->contains (new_note)) {
940 view->update_note_range(new_note->note());
942 MidiModel::NoteDiffCommand* cmd = _model->new_note_diff_command(_("add note"));
944 _model->apply_command(*trackview.session(), cmd);
946 play_midi_note (new_note);
950 MidiRegionView::clear_events (bool with_selection_signal)
952 clear_selection (with_selection_signal);
955 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
956 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
961 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
966 _patch_changes.clear();
968 _optimization_iterator = _events.end();
972 MidiRegionView::display_model(boost::shared_ptr<MidiModel> model)
976 content_connection.disconnect ();
977 _model->ContentsChanged.connect (content_connection, invalidator (*this), boost::bind (&MidiRegionView::redisplay_model, this), gui_context());
981 if (_enable_display) {
987 MidiRegionView::start_note_diff_command (string name)
989 if (!_note_diff_command) {
990 _note_diff_command = _model->new_note_diff_command (name);
995 MidiRegionView::note_diff_add_note (const boost::shared_ptr<NoteType> note, bool selected, bool show_velocity)
997 if (_note_diff_command) {
998 _note_diff_command->add (note);
1001 _marked_for_selection.insert(note);
1003 if (show_velocity) {
1004 _marked_for_velocity.insert(note);
1009 MidiRegionView::note_diff_remove_note (NoteBase* ev)
1011 if (_note_diff_command && ev->note()) {
1012 _note_diff_command->remove(ev->note());
1017 MidiRegionView::note_diff_add_change (NoteBase* ev,
1018 MidiModel::NoteDiffCommand::Property property,
1021 if (_note_diff_command) {
1022 _note_diff_command->change (ev->note(), property, val);
1027 MidiRegionView::note_diff_add_change (NoteBase* ev,
1028 MidiModel::NoteDiffCommand::Property property,
1029 Evoral::MusicalTime val)
1031 if (_note_diff_command) {
1032 _note_diff_command->change (ev->note(), property, val);
1037 MidiRegionView::apply_diff (bool as_subcommand)
1041 if (!_note_diff_command) {
1045 if ((add_or_remove = _note_diff_command->adds_or_removes())) {
1046 // Mark all selected notes for selection when model reloads
1047 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1048 _marked_for_selection.insert((*i)->note());
1052 if (as_subcommand) {
1053 _model->apply_command_as_subcommand (*trackview.session(), _note_diff_command);
1055 _model->apply_command (*trackview.session(), _note_diff_command);
1058 _note_diff_command = 0;
1059 midi_view()->midi_track()->playlist_modified();
1061 if (add_or_remove) {
1062 _marked_for_selection.clear();
1065 _marked_for_velocity.clear();
1069 MidiRegionView::abort_command()
1071 delete _note_diff_command;
1072 _note_diff_command = 0;
1077 MidiRegionView::find_canvas_note (boost::shared_ptr<NoteType> note)
1079 if (_optimization_iterator != _events.end()) {
1080 ++_optimization_iterator;
1083 if (_optimization_iterator != _events.end() && (*_optimization_iterator)->note() == note) {
1084 return *_optimization_iterator;
1087 for (_optimization_iterator = _events.begin(); _optimization_iterator != _events.end(); ++_optimization_iterator) {
1088 if ((*_optimization_iterator)->note() == note) {
1089 return *_optimization_iterator;
1097 MidiRegionView::get_events (Events& e, Evoral::Sequence<Evoral::MusicalTime>::NoteOperator op, uint8_t val, int chan_mask)
1099 MidiModel::Notes notes;
1100 _model->get_notes (notes, op, val, chan_mask);
1102 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1103 NoteBase* cne = find_canvas_note (*n);
1111 MidiRegionView::redisplay_model()
1113 // Don't redisplay the model if we're currently recording and displaying that
1114 if (_active_notes) {
1122 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1123 (*i)->invalidate ();
1126 MidiModel::ReadLock lock(_model->read_lock());
1128 MidiModel::Notes& notes (_model->notes());
1129 _optimization_iterator = _events.begin();
1131 bool empty_when_starting = _events.empty();
1133 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1135 boost::shared_ptr<NoteType> note (*n);
1139 if (note_in_region_range (note, visible)) {
1141 if (!empty_when_starting && (cne = find_canvas_note (note)) != 0) {
1148 if ((cn = dynamic_cast<Note*>(cne)) != 0) {
1150 } else if ((ch = dynamic_cast<Hit*>(cne)) != 0) {
1162 add_note (note, visible);
1167 if (!empty_when_starting && (cne = find_canvas_note (note)) != 0) {
1175 /* remove note items that are no longer valid */
1177 if (!empty_when_starting) {
1178 for (Events::iterator i = _events.begin(); i != _events.end(); ) {
1179 if (!(*i)->valid ()) {
1181 for (vector<GhostRegion*>::iterator j = ghosts.begin(); j != ghosts.end(); ++j) {
1182 MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*j);
1184 gr->remove_note (*i);
1189 i = _events.erase (i);
1197 _patch_changes.clear();
1201 display_patch_changes ();
1203 _marked_for_selection.clear ();
1204 _marked_for_velocity.clear ();
1206 /* we may have caused _events to contain things out of order (e.g. if a note
1207 moved earlier or later). we don't generally need them in time order, but
1208 make a note that a sort is required for those cases that require it.
1211 _sort_needed = true;
1215 MidiRegionView::display_patch_changes ()
1217 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1218 uint16_t chn_mask = mtv->midi_track()->get_playback_channel_mask();
1220 for (uint8_t i = 0; i < 16; ++i) {
1221 display_patch_changes_on_channel (i, chn_mask & (1 << i));
1225 /** @param active_channel true to display patch changes fully, false to display
1226 * them `greyed-out' (as on an inactive channel)
1229 MidiRegionView::display_patch_changes_on_channel (uint8_t channel, bool active_channel)
1231 for (MidiModel::PatchChanges::const_iterator i = _model->patch_changes().begin(); i != _model->patch_changes().end(); ++i) {
1233 if ((*i)->channel() != channel) {
1237 const string patch_name = instrument_info().get_patch_name ((*i)->bank(), (*i)->program(), channel);
1238 add_canvas_patch_change (*i, patch_name, active_channel);
1243 MidiRegionView::display_sysexes()
1245 bool have_periodic_system_messages = false;
1246 bool display_periodic_messages = true;
1248 if (!Config->get_never_display_periodic_midi()) {
1250 for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
1251 const boost::shared_ptr<const Evoral::MIDIEvent<Evoral::MusicalTime> > mev =
1252 boost::static_pointer_cast<const Evoral::MIDIEvent<Evoral::MusicalTime> > (*i);
1255 if (mev->is_spp() || mev->is_mtc_quarter() || mev->is_mtc_full()) {
1256 have_periodic_system_messages = true;
1262 if (have_periodic_system_messages) {
1263 double zoom = trackview.editor().get_current_zoom (); // frames per pixel
1265 /* get an approximate value for the number of samples per video frame */
1267 double video_frame = trackview.session()->frame_rate() * (1.0/30);
1269 /* if we are zoomed out beyond than the cutoff (i.e. more
1270 * frames per pixel than frames per 4 video frames), don't
1271 * show periodic sysex messages.
1274 if (zoom > (video_frame*4)) {
1275 display_periodic_messages = false;
1279 display_periodic_messages = false;
1282 for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
1284 const boost::shared_ptr<const Evoral::MIDIEvent<Evoral::MusicalTime> > mev =
1285 boost::static_pointer_cast<const Evoral::MIDIEvent<Evoral::MusicalTime> > (*i);
1287 Evoral::MusicalTime time = (*i)->time();
1290 if (mev->is_spp() || mev->is_mtc_quarter() || mev->is_mtc_full()) {
1291 if (!display_periodic_messages) {
1299 for (uint32_t b = 0; b < (*i)->size(); ++b) {
1300 str << int((*i)->buffer()[b]);
1301 if (b != (*i)->size() -1) {
1305 string text = str.str();
1307 const double x = trackview.editor().sample_to_pixel(source_beats_to_region_frames(time));
1309 double height = midi_stream_view()->contents_height();
1311 // CAIROCANVAS: no longer passing *i (the sysex event) to the
1312 // SysEx canvas object!!!
1314 boost::shared_ptr<SysEx> sysex = boost::shared_ptr<SysEx>(
1315 new SysEx (*this, _note_group, text, height, x, 1.0));
1317 // Show unless message is beyond the region bounds
1318 if (time - _region->start() >= _region->length() || time < _region->start()) {
1324 _sys_exes.push_back(sysex);
1328 MidiRegionView::~MidiRegionView ()
1330 in_destructor = true;
1332 trackview.editor().verbose_cursor()->hide ();
1334 note_delete_connection.disconnect ();
1336 delete _list_editor;
1338 RegionViewGoingAway (this); /* EMIT_SIGNAL */
1340 if (_active_notes) {
1344 _selection_cleared_connection.disconnect ();
1347 clear_events (false);
1350 delete _note_diff_command;
1351 delete _step_edit_cursor;
1352 delete _temporary_note_group;
1356 MidiRegionView::region_resized (const PropertyChange& what_changed)
1358 RegionView::region_resized(what_changed);
1360 if (what_changed.contains (ARDOUR::Properties::position)) {
1361 _region_relative_time_converter.set_origin_b(_region->position());
1362 set_duration(_region->length(), 0);
1363 if (_enable_display) {
1368 if (what_changed.contains (ARDOUR::Properties::start) ||
1369 what_changed.contains (ARDOUR::Properties::position)) {
1370 _source_relative_time_converter.set_origin_b (_region->position() - _region->start());
1375 MidiRegionView::reset_width_dependent_items (double pixel_width)
1377 RegionView::reset_width_dependent_items(pixel_width);
1379 if (_enable_display) {
1383 for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
1384 if ((*x)->canvas_item()->width() >= _pixel_width) {
1391 move_step_edit_cursor (_step_edit_cursor_position);
1392 set_step_edit_cursor_width (_step_edit_cursor_width);
1396 MidiRegionView::set_height (double height)
1398 double old_height = _height;
1399 RegionView::set_height(height);
1401 apply_note_range (midi_stream_view()->lowest_note(),
1402 midi_stream_view()->highest_note(),
1403 height != old_height);
1406 name_text->raise_to_top();
1409 for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
1410 (*x)->set_height (midi_stream_view()->contents_height());
1413 if (_step_edit_cursor) {
1414 _step_edit_cursor->set_y1 (midi_stream_view()->contents_height());
1419 /** Apply the current note range from the stream view
1420 * by repositioning/hiding notes as necessary
1423 MidiRegionView::apply_note_range (uint8_t min, uint8_t max, bool force)
1425 if (!_enable_display) {
1429 if (!force && _current_range_min == min && _current_range_max == max) {
1433 _current_range_min = min;
1434 _current_range_max = max;
1436 for (Events::const_iterator i = _events.begin(); i != _events.end(); ++i) {
1437 NoteBase* event = *i;
1438 boost::shared_ptr<NoteType> note (event->note());
1440 if (note->note() < _current_range_min ||
1441 note->note() > _current_range_max) {
1447 if (Note* cnote = dynamic_cast<Note*>(event)) {
1449 const double y0 = 1. + floor (midi_stream_view()->note_to_y(note->note()));
1450 const double y1 = y0 + std::max(1., floor(midi_stream_view()->note_height()) - 1.);
1455 } else if (Hit* chit = dynamic_cast<Hit*>(event)) {
1462 MidiRegionView::add_ghost (TimeAxisView& tv)
1466 double unit_position = _region->position () / samples_per_pixel;
1467 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(&tv);
1468 MidiGhostRegion* ghost;
1470 if (mtv && mtv->midi_view()) {
1471 /* if ghost is inserted into midi track, use a dedicated midi ghost canvas group
1472 to allow having midi notes on top of note lines and waveforms.
1474 ghost = new MidiGhostRegion (*mtv->midi_view(), trackview, unit_position);
1476 ghost = new MidiGhostRegion (tv, trackview, unit_position);
1479 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1480 if ((note = dynamic_cast<Note*>(*i)) != 0) {
1481 ghost->add_note(note);
1485 ghost->set_height ();
1486 ghost->set_duration (_region->length() / samples_per_pixel);
1487 ghosts.push_back (ghost);
1489 GhostRegion::CatchDeletion.connect (*this, invalidator (*this), boost::bind (&RegionView::remove_ghost, this, _1), gui_context());
1495 /** Begin tracking note state for successive calls to add_event
1498 MidiRegionView::begin_write()
1500 if (_active_notes) {
1501 delete[] _active_notes;
1503 _active_notes = new Note*[128];
1504 for (unsigned i = 0; i < 128; ++i) {
1505 _active_notes[i] = 0;
1510 /** Destroy note state for add_event
1513 MidiRegionView::end_write()
1515 delete[] _active_notes;
1517 _marked_for_selection.clear();
1518 _marked_for_velocity.clear();
1522 /** Resolve an active MIDI note (while recording).
1525 MidiRegionView::resolve_note(uint8_t note, Evoral::MusicalTime end_time)
1527 if (midi_view()->note_mode() != Sustained) {
1531 if (_active_notes && _active_notes[note]) {
1533 /* XXX is end_time really region-centric? I think so, because
1534 this is a new region that we're recording, so source zero is
1535 the same as region zero
1537 const framepos_t end_time_frames = region_beats_to_region_frames(end_time);
1539 _active_notes[note]->set_x1 (trackview.editor().sample_to_pixel(end_time_frames));
1540 _active_notes[note]->set_outline_all ();
1541 _active_notes[note] = 0;
1547 /** Extend active notes to rightmost edge of region (if length is changed)
1550 MidiRegionView::extend_active_notes()
1552 if (!_active_notes) {
1556 for (unsigned i=0; i < 128; ++i) {
1557 if (_active_notes[i]) {
1558 _active_notes[i]->set_x1 (trackview.editor().sample_to_pixel(_region->length()));
1565 MidiRegionView::play_midi_note(boost::shared_ptr<NoteType> note)
1567 if (_no_sound_notes || !Config->get_sound_midi_notes()) {
1571 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1573 if (!route_ui || !route_ui->midi_track()) {
1577 NotePlayer* np = new NotePlayer (route_ui->midi_track ());
1581 /* NotePlayer deletes itself */
1585 MidiRegionView::start_playing_midi_note(boost::shared_ptr<NoteType> note)
1587 if (_no_sound_notes || !Config->get_sound_midi_notes()) {
1591 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1593 if (!route_ui || !route_ui->midi_track()) {
1597 delete _note_player;
1598 _note_player = new NotePlayer (route_ui->midi_track ());
1599 _note_player->add (note);
1600 _note_player->on ();
1604 MidiRegionView::start_playing_midi_chord (vector<boost::shared_ptr<NoteType> > notes)
1606 if (_no_sound_notes || !Config->get_sound_midi_notes()) {
1610 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1612 if (!route_ui || !route_ui->midi_track()) {
1616 delete _note_player;
1617 _note_player = new NotePlayer (route_ui->midi_track());
1619 for (vector<boost::shared_ptr<NoteType> >::iterator n = notes.begin(); n != notes.end(); ++n) {
1620 _note_player->add (*n);
1623 _note_player->on ();
1628 MidiRegionView::note_in_region_range (const boost::shared_ptr<NoteType> note, bool& visible) const
1630 const framepos_t note_start_frames = source_beats_to_region_frames (note->time());
1631 bool outside = (note_start_frames < 0) || (note_start_frames > _region->last_frame());
1633 visible = (note->note() >= midi_stream_view()->lowest_note()) &&
1634 (note->note() <= midi_stream_view()->highest_note());
1639 /** Update a canvas note's size from its model note.
1640 * @param ev Canvas note to update.
1641 * @param update_ghost_regions true to update the note in any ghost regions that we have, otherwise false.
1644 MidiRegionView::update_note (Note* ev, bool update_ghost_regions)
1646 boost::shared_ptr<NoteType> note = ev->note();
1647 const double x = trackview.editor().sample_to_pixel (source_beats_to_region_frames (note->time()));
1648 const double y0 = 1 + floor(midi_stream_view()->note_to_y(note->note()));
1653 /* trim note display to not overlap the end of its region */
1655 if (note->length() > 0) {
1656 const framepos_t note_end_frames = min (source_beats_to_region_frames (note->end_time()), _region->length());
1657 ev->set_x1 (trackview.editor().sample_to_pixel (note_end_frames));
1659 ev->set_x1 (trackview.editor().sample_to_pixel (_region->length()));
1662 ev->set_y1 (y0 + std::max(1., floor(midi_stream_view()->note_height()) - 1));
1664 if (!note->length()) {
1665 if (_active_notes && note->note() < 128) {
1666 // If this note is already active there's a stuck note,
1667 // finish the old note rectangle
1668 if (_active_notes[note->note()]) {
1669 Note* const old_rect = _active_notes[note->note()];
1670 boost::shared_ptr<NoteType> old_note = old_rect->note();
1671 old_rect->set_x1 (x);
1672 old_rect->set_outline_all ();
1674 _active_notes[note->note()] = ev;
1676 /* outline all but right edge */
1677 ev->set_outline_what (ArdourCanvas::Rectangle::What (
1678 ArdourCanvas::Rectangle::TOP|
1679 ArdourCanvas::Rectangle::LEFT|
1680 ArdourCanvas::Rectangle::BOTTOM));
1682 /* outline all edges */
1683 ev->set_outline_all ();
1686 if (update_ghost_regions) {
1687 for (std::vector<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
1688 MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*i);
1690 gr->update_note (ev);
1697 MidiRegionView::update_hit (Hit* ev)
1699 boost::shared_ptr<NoteType> note = ev->note();
1701 const framepos_t note_start_frames = source_beats_to_region_frames(note->time());
1702 const double x = trackview.editor().sample_to_pixel(note_start_frames);
1703 const double diamond_size = std::max(1., floor(midi_stream_view()->note_height()) - 2.);
1704 const double y = 1.5 + floor(midi_stream_view()->note_to_y(note->note())) + diamond_size * .5;
1706 ev->set_position (ArdourCanvas::Duple (x, y));
1707 ev->set_height (diamond_size);
1710 /** Add a MIDI note to the view (with length).
1712 * If in sustained mode, notes with length 0 will be considered active
1713 * notes, and resolve_note should be called when the corresponding note off
1714 * event arrives, to properly display the note.
1717 MidiRegionView::add_note(const boost::shared_ptr<NoteType> note, bool visible)
1719 NoteBase* event = 0;
1721 if (midi_view()->note_mode() == Sustained) {
1723 Note* ev_rect = new Note (*this, _note_group, note);
1725 update_note (ev_rect);
1729 MidiGhostRegion* gr;
1731 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
1732 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
1733 gr->add_note(ev_rect);
1737 } else if (midi_view()->note_mode() == Percussive) {
1739 const double diamond_size = std::max(1., floor(midi_stream_view()->note_height()) - 2.);
1741 Hit* ev_diamond = new Hit (*this, _note_group, diamond_size, note);
1743 update_hit (ev_diamond);
1752 if (_marked_for_selection.find(note) != _marked_for_selection.end()) {
1753 note_selected(event, true);
1756 if (_marked_for_velocity.find(note) != _marked_for_velocity.end()) {
1757 event->show_velocity();
1760 event->on_channel_selection_change (get_selected_channels());
1761 _events.push_back(event);
1770 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1771 MidiStreamView* const view = mtv->midi_view();
1773 view->update_note_range (note->note());
1777 MidiRegionView::step_add_note (uint8_t channel, uint8_t number, uint8_t velocity,
1778 Evoral::MusicalTime pos, Evoral::MusicalTime len)
1780 boost::shared_ptr<NoteType> new_note (new NoteType (channel, pos, len, number, velocity));
1782 /* potentially extend region to hold new note */
1784 framepos_t end_frame = source_beats_to_absolute_frames (new_note->end_time());
1785 framepos_t region_end = _region->last_frame();
1787 if (end_frame > region_end) {
1788 _region->set_length (end_frame - _region->position());
1791 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1792 MidiStreamView* const view = mtv->midi_view();
1794 view->update_note_range(new_note->note());
1796 _marked_for_selection.clear ();
1799 start_note_diff_command (_("step add"));
1800 note_diff_add_note (new_note, true, false);
1803 // last_step_edit_note = new_note;
1807 MidiRegionView::step_sustain (Evoral::MusicalTime beats)
1809 change_note_lengths (false, false, beats, false, true);
1812 /** Add a new patch change flag to the canvas.
1813 * @param patch the patch change to add
1814 * @param the text to display in the flag
1815 * @param active_channel true to display the flag as on an active channel, false to grey it out for an inactive channel.
1818 MidiRegionView::add_canvas_patch_change (MidiModel::PatchChangePtr patch, const string& displaytext, bool /*active_channel*/)
1820 framecnt_t region_frames = source_beats_to_region_frames (patch->time());
1821 const double x = trackview.editor().sample_to_pixel (region_frames);
1823 double const height = midi_stream_view()->contents_height();
1825 // CAIROCANVAS: active_channel info removed from PatcChange constructor
1826 // so we need to do something more sophisticated to keep its color
1827 // appearance (MidiPatchChangeFill/MidiPatchChangeInactiveChannelFill)
1830 boost::shared_ptr<PatchChange> patch_change = boost::shared_ptr<PatchChange>(
1831 new PatchChange(*this, group,
1838 if (patch_change->item().width() < _pixel_width) {
1839 // Show unless patch change is beyond the region bounds
1840 if (region_frames < 0 || region_frames >= _region->length()) {
1841 patch_change->hide();
1843 patch_change->show();
1846 patch_change->hide ();
1849 _patch_changes.push_back (patch_change);
1852 MIDI::Name::PatchPrimaryKey
1853 MidiRegionView::patch_change_to_patch_key (MidiModel::PatchChangePtr p)
1855 return MIDI::Name::PatchPrimaryKey (p->program(), p->bank());
1858 /// Return true iff @p pc applies to the given time on the given channel.
1860 patch_applies (const ARDOUR::MidiModel::constPatchChangePtr pc, Evoral::MusicalTime time, uint8_t channel)
1862 return pc->time() <= time && pc->channel() == channel;
1866 MidiRegionView::get_patch_key_at (Evoral::MusicalTime time, uint8_t channel, MIDI::Name::PatchPrimaryKey& key) const
1868 // The earliest event not before time
1869 MidiModel::PatchChanges::iterator i = _model->patch_change_lower_bound (time);
1871 // Go backwards until we find the latest PC for this channel, or the start
1872 while (i != _model->patch_changes().begin() &&
1873 (i == _model->patch_changes().end() ||
1874 !patch_applies(*i, time, channel))) {
1878 if (i != _model->patch_changes().end() && patch_applies(*i, time, channel)) {
1879 key.bank_number = (*i)->bank();
1880 key.program_number = (*i)->program ();
1882 key.bank_number = key.program_number = 0;
1885 if (!key.is_sane()) {
1886 error << string_compose(_("insane MIDI patch key %1:%2"),
1887 key.bank_number, key.program_number) << endmsg;
1892 MidiRegionView::change_patch_change (PatchChange& pc, const MIDI::Name::PatchPrimaryKey& new_patch)
1894 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("alter patch change"));
1896 if (pc.patch()->program() != new_patch.program_number) {
1897 c->change_program (pc.patch (), new_patch.program_number);
1900 int const new_bank = new_patch.bank_number;
1901 if (pc.patch()->bank() != new_bank) {
1902 c->change_bank (pc.patch (), new_bank);
1905 _model->apply_command (*trackview.session(), c);
1907 _patch_changes.clear ();
1908 display_patch_changes ();
1912 MidiRegionView::change_patch_change (MidiModel::PatchChangePtr old_change, const Evoral::PatchChange<Evoral::MusicalTime> & new_change)
1914 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("alter patch change"));
1916 if (old_change->time() != new_change.time()) {
1917 c->change_time (old_change, new_change.time());
1920 if (old_change->channel() != new_change.channel()) {
1921 c->change_channel (old_change, new_change.channel());
1924 if (old_change->program() != new_change.program()) {
1925 c->change_program (old_change, new_change.program());
1928 if (old_change->bank() != new_change.bank()) {
1929 c->change_bank (old_change, new_change.bank());
1932 _model->apply_command (*trackview.session(), c);
1934 _patch_changes.clear ();
1935 display_patch_changes ();
1938 /** Add a patch change to the region.
1939 * @param t Time in frames relative to region position
1940 * @param patch Patch to add; time and channel are ignored (time is converted from t, and channel comes from
1941 * MidiTimeAxisView::get_channel_for_add())
1944 MidiRegionView::add_patch_change (framecnt_t t, Evoral::PatchChange<Evoral::MusicalTime> const & patch)
1946 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1948 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("add patch change"));
1949 c->add (MidiModel::PatchChangePtr (
1950 new Evoral::PatchChange<Evoral::MusicalTime> (
1951 absolute_frames_to_source_beats (_region->position() + t),
1952 mtv->get_channel_for_add(), patch.program(), patch.bank()
1957 _model->apply_command (*trackview.session(), c);
1959 _patch_changes.clear ();
1960 display_patch_changes ();
1964 MidiRegionView::move_patch_change (PatchChange& pc, Evoral::MusicalTime t)
1966 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("move patch change"));
1967 c->change_time (pc.patch (), t);
1968 _model->apply_command (*trackview.session(), c);
1970 _patch_changes.clear ();
1971 display_patch_changes ();
1975 MidiRegionView::delete_patch_change (PatchChange* pc)
1977 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("delete patch change"));
1978 c->remove (pc->patch ());
1979 _model->apply_command (*trackview.session(), c);
1981 _patch_changes.clear ();
1982 display_patch_changes ();
1986 MidiRegionView::previous_patch (PatchChange& patch)
1988 if (patch.patch()->program() < 127) {
1989 MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch());
1990 key.program_number++;
1991 change_patch_change (patch, key);
1996 MidiRegionView::next_patch (PatchChange& patch)
1998 if (patch.patch()->program() > 0) {
1999 MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch());
2000 key.program_number--;
2001 change_patch_change (patch, key);
2006 MidiRegionView::next_bank (PatchChange& patch)
2008 if (patch.patch()->program() < 127) {
2009 MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch());
2010 if (key.bank_number > 0) {
2012 change_patch_change (patch, key);
2018 MidiRegionView::previous_bank (PatchChange& patch)
2020 if (patch.patch()->program() > 0) {
2021 MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch());
2022 if (key.bank_number < 127) {
2024 change_patch_change (patch, key);
2030 MidiRegionView::maybe_remove_deleted_note_from_selection (NoteBase* cne)
2032 if (_selection.empty()) {
2036 _selection.erase (cne);
2040 MidiRegionView::delete_selection()
2042 if (_selection.empty()) {
2046 start_note_diff_command (_("delete selection"));
2048 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2049 if ((*i)->selected()) {
2050 _note_diff_command->remove((*i)->note());
2060 MidiRegionView::delete_note (boost::shared_ptr<NoteType> n)
2062 start_note_diff_command (_("delete note"));
2063 _note_diff_command->remove (n);
2066 trackview.editor().verbose_cursor()->hide ();
2070 MidiRegionView::clear_selection_except (NoteBase* ev, bool signal)
2072 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2074 Selection::iterator tmp = i;
2077 (*i)->set_selected (false);
2078 (*i)->hide_velocity ();
2079 _selection.erase (i);
2087 if (!ev && _entered) {
2088 // Clearing selection entirely, ungrab keyboard
2089 Keyboard::magic_widget_drop_focus();
2090 _grabbed_keyboard = false;
2093 /* this does not change the status of this regionview w.r.t the editor
2098 SelectionCleared (this); /* EMIT SIGNAL */
2103 MidiRegionView::unique_select(NoteBase* ev)
2105 const bool selection_was_empty = _selection.empty();
2107 clear_selection_except (ev);
2109 /* don't bother with checking to see if we should remove this
2110 regionview from the editor selection, since we're about to add
2111 another note, and thus put/keep this regionview in the editor
2115 if (!ev->selected()) {
2116 add_to_selection (ev);
2117 if (selection_was_empty && _entered) {
2118 // Grab keyboard for moving notes with arrow keys
2119 Keyboard::magic_widget_grab_focus();
2120 _grabbed_keyboard = true;
2126 MidiRegionView::select_all_notes ()
2130 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2131 add_to_selection (*i);
2136 MidiRegionView::select_range (framepos_t start, framepos_t end)
2140 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2141 framepos_t t = source_beats_to_absolute_frames((*i)->note()->time());
2142 if (t >= start && t <= end) {
2143 add_to_selection (*i);
2149 MidiRegionView::invert_selection ()
2151 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2152 if ((*i)->selected()) {
2153 remove_from_selection(*i);
2155 add_to_selection (*i);
2161 MidiRegionView::select_matching_notes (uint8_t notenum, uint16_t channel_mask, bool add, bool extend)
2163 bool have_selection = !_selection.empty();
2164 uint8_t low_note = 127;
2165 uint8_t high_note = 0;
2166 MidiModel::Notes& notes (_model->notes());
2167 _optimization_iterator = _events.begin();
2169 if (extend && !have_selection) {
2173 /* scan existing selection to get note range */
2175 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2176 if ((*i)->note()->note() < low_note) {
2177 low_note = (*i)->note()->note();
2179 if ((*i)->note()->note() > high_note) {
2180 high_note = (*i)->note()->note();
2187 if (!extend && (low_note == high_note) && (high_note == notenum)) {
2188 /* only note previously selected is the one we are
2189 * reselecting. treat this as cancelling the selection.
2196 low_note = min (low_note, notenum);
2197 high_note = max (high_note, notenum);
2200 _no_sound_notes = true;
2202 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
2204 boost::shared_ptr<NoteType> note (*n);
2206 bool select = false;
2208 if (((1 << note->channel()) & channel_mask) != 0) {
2210 if ((note->note() >= low_note && note->note() <= high_note)) {
2213 } else if (note->note() == notenum) {
2219 if ((cne = find_canvas_note (note)) != 0) {
2220 // extend is false because we've taken care of it,
2221 // since it extends by time range, not pitch.
2222 note_selected (cne, add, false);
2226 add = true; // we need to add all remaining matching notes, even if the passed in value was false (for "set")
2230 _no_sound_notes = false;
2234 MidiRegionView::toggle_matching_notes (uint8_t notenum, uint16_t channel_mask)
2236 MidiModel::Notes& notes (_model->notes());
2237 _optimization_iterator = _events.begin();
2239 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
2241 boost::shared_ptr<NoteType> note (*n);
2244 if (note->note() == notenum && (((0x0001 << note->channel()) & channel_mask) != 0)) {
2245 if ((cne = find_canvas_note (note)) != 0) {
2246 if (cne->selected()) {
2247 note_deselected (cne);
2249 note_selected (cne, true, false);
2257 MidiRegionView::note_selected (NoteBase* ev, bool add, bool extend)
2260 clear_selection_except (ev);
2261 if (!_selection.empty()) {
2262 PublicEditor& editor (trackview.editor());
2263 editor.get_selection().add (this);
2269 if (!ev->selected()) {
2270 add_to_selection (ev);
2274 /* find end of latest note selected, select all between that and the start of "ev" */
2276 Evoral::MusicalTime earliest = Evoral::MaxMusicalTime;
2277 Evoral::MusicalTime latest = Evoral::MusicalTime();
2279 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2280 if ((*i)->note()->end_time() > latest) {
2281 latest = (*i)->note()->end_time();
2283 if ((*i)->note()->time() < earliest) {
2284 earliest = (*i)->note()->time();
2288 if (ev->note()->end_time() > latest) {
2289 latest = ev->note()->end_time();
2292 if (ev->note()->time() < earliest) {
2293 earliest = ev->note()->time();
2296 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2298 /* find notes entirely within OR spanning the earliest..latest range */
2300 if (((*i)->note()->time() >= earliest && (*i)->note()->end_time() <= latest) ||
2301 ((*i)->note()->time() <= earliest && (*i)->note()->end_time() >= latest)) {
2302 add_to_selection (*i);
2310 MidiRegionView::note_deselected(NoteBase* ev)
2312 remove_from_selection (ev);
2316 MidiRegionView::update_drag_selection(framepos_t start, framepos_t end, double gy0, double gy1, bool extend)
2318 PublicEditor& editor = trackview.editor();
2320 // Convert to local coordinates
2321 const framepos_t p = _region->position();
2322 const double y = midi_view()->y_position();
2323 const double x0 = editor.sample_to_pixel(max((framepos_t)0, start - p));
2324 const double x1 = editor.sample_to_pixel(max((framepos_t)0, end - p));
2325 const double y0 = max(0.0, gy0 - y);
2326 const double y1 = max(0.0, gy1 - y);
2328 // TODO: Make this faster by storing the last updated selection rect, and only
2329 // adjusting things that are in the area that appears/disappeared.
2330 // We probably need a tree to be able to find events in O(log(n)) time.
2332 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2333 if ((*i)->x0() < x1 && (*i)->x1() > x0 && (*i)->y0() < y1 && (*i)->y1() > y0) {
2334 // Rectangles intersect
2335 if (!(*i)->selected()) {
2336 add_to_selection (*i);
2338 } else if ((*i)->selected() && !extend) {
2339 // Rectangles do not intersect
2340 remove_from_selection (*i);
2344 typedef RouteTimeAxisView::AutomationTracks ATracks;
2345 typedef std::list<Selectable*> Selectables;
2347 /* Add control points to selection. */
2348 const ATracks& atracks = midi_view()->automation_tracks();
2349 Selectables selectables;
2350 editor.get_selection().clear_points();
2351 for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) {
2352 a->second->get_selectables(start, end, gy0, gy1, selectables);
2353 for (Selectables::const_iterator s = selectables.begin(); s != selectables.end(); ++s) {
2354 ControlPoint* cp = dynamic_cast<ControlPoint*>(*s);
2356 editor.get_selection().add(cp);
2359 a->second->set_selected_points(editor.get_selection().points);
2364 MidiRegionView::update_vertical_drag_selection (double y1, double y2, bool extend)
2370 // TODO: Make this faster by storing the last updated selection rect, and only
2371 // adjusting things that are in the area that appears/disappeared.
2372 // We probably need a tree to be able to find events in O(log(n)) time.
2374 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2375 if (((*i)->y1() >= y1 && (*i)->y1() <= y2)) {
2376 // within y- (note-) range
2377 if (!(*i)->selected()) {
2378 add_to_selection (*i);
2380 } else if ((*i)->selected() && !extend) {
2381 remove_from_selection (*i);
2387 MidiRegionView::remove_from_selection (NoteBase* ev)
2389 Selection::iterator i = _selection.find (ev);
2391 if (i != _selection.end()) {
2392 _selection.erase (i);
2393 if (_selection.empty() && _grabbed_keyboard) {
2395 Keyboard::magic_widget_drop_focus();
2396 _grabbed_keyboard = false;
2400 ev->set_selected (false);
2401 ev->hide_velocity ();
2403 if (_selection.empty()) {
2404 PublicEditor& editor (trackview.editor());
2405 editor.get_selection().remove (this);
2410 MidiRegionView::add_to_selection (NoteBase* ev)
2412 const bool selection_was_empty = _selection.empty();
2414 if (_selection.insert (ev).second) {
2415 ev->set_selected (true);
2416 start_playing_midi_note ((ev)->note());
2417 if (selection_was_empty && _entered) {
2418 // Grab keyboard for moving notes with arrow keys
2419 Keyboard::magic_widget_grab_focus();
2420 _grabbed_keyboard = true;
2424 if (selection_was_empty) {
2425 PublicEditor& editor (trackview.editor());
2426 editor.get_selection().add (this);
2431 MidiRegionView::move_selection(double dx, double dy, double cumulative_dy)
2433 typedef vector<boost::shared_ptr<NoteType> > PossibleChord;
2434 PossibleChord to_play;
2435 Evoral::MusicalTime earliest = Evoral::MaxMusicalTime;
2437 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2438 if ((*i)->note()->time() < earliest) {
2439 earliest = (*i)->note()->time();
2443 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2444 if ((*i)->note()->time() == earliest) {
2445 to_play.push_back ((*i)->note());
2447 (*i)->move_event(dx, dy);
2450 if (dy && !_selection.empty() && !_no_sound_notes && Config->get_sound_midi_notes()) {
2452 if (to_play.size() > 1) {
2454 PossibleChord shifted;
2456 for (PossibleChord::iterator n = to_play.begin(); n != to_play.end(); ++n) {
2457 boost::shared_ptr<NoteType> moved_note (new NoteType (**n));
2458 moved_note->set_note (moved_note->note() + cumulative_dy);
2459 shifted.push_back (moved_note);
2462 start_playing_midi_chord (shifted);
2464 } else if (!to_play.empty()) {
2466 boost::shared_ptr<NoteType> moved_note (new NoteType (*to_play.front()));
2467 moved_note->set_note (moved_note->note() + cumulative_dy);
2468 start_playing_midi_note (moved_note);
2474 MidiRegionView::note_dropped(NoteBase *, frameoffset_t dt, int8_t dnote)
2476 uint8_t lowest_note_in_selection = 127;
2477 uint8_t highest_note_in_selection = 0;
2478 uint8_t highest_note_difference = 0;
2480 // find highest and lowest notes first
2482 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2483 uint8_t pitch = (*i)->note()->note();
2484 lowest_note_in_selection = std::min(lowest_note_in_selection, pitch);
2485 highest_note_in_selection = std::max(highest_note_in_selection, pitch);
2489 cerr << "dnote: " << (int) dnote << endl;
2490 cerr << "lowest note (streamview): " << int(midi_stream_view()->lowest_note())
2491 << " highest note (streamview): " << int(midi_stream_view()->highest_note()) << endl;
2492 cerr << "lowest note (selection): " << int(lowest_note_in_selection) << " highest note(selection): "
2493 << int(highest_note_in_selection) << endl;
2494 cerr << "selection size: " << _selection.size() << endl;
2495 cerr << "Highest note in selection: " << (int) highest_note_in_selection << endl;
2498 // Make sure the note pitch does not exceed the MIDI standard range
2499 if (highest_note_in_selection + dnote > 127) {
2500 highest_note_difference = highest_note_in_selection - 127;
2503 start_note_diff_command (_("move notes"));
2505 for (Selection::iterator i = _selection.begin(); i != _selection.end() ; ++i) {
2507 framepos_t new_frames = source_beats_to_absolute_frames ((*i)->note()->time()) + dt;
2508 Evoral::MusicalTime new_time = absolute_frames_to_source_beats (new_frames);
2514 note_diff_add_change (*i, MidiModel::NoteDiffCommand::StartTime, new_time);
2516 uint8_t original_pitch = (*i)->note()->note();
2517 uint8_t new_pitch = original_pitch + dnote - highest_note_difference;
2519 // keep notes in standard midi range
2520 clamp_to_0_127(new_pitch);
2522 lowest_note_in_selection = std::min(lowest_note_in_selection, new_pitch);
2523 highest_note_in_selection = std::max(highest_note_in_selection, new_pitch);
2525 note_diff_add_change (*i, MidiModel::NoteDiffCommand::NoteNumber, new_pitch);
2530 // care about notes being moved beyond the upper/lower bounds on the canvas
2531 if (lowest_note_in_selection < midi_stream_view()->lowest_note() ||
2532 highest_note_in_selection > midi_stream_view()->highest_note()) {
2533 midi_stream_view()->set_note_range(MidiStreamView::ContentsRange);
2537 /** @param x Pixel relative to the region position.
2538 * @return Snapped frame relative to the region position.
2541 MidiRegionView::snap_pixel_to_sample(double x)
2543 PublicEditor& editor (trackview.editor());
2544 return snap_frame_to_frame (editor.pixel_to_sample (x));
2547 /** @param x Pixel relative to the region position.
2548 * @return Snapped pixel relative to the region position.
2551 MidiRegionView::snap_to_pixel(double x)
2553 return (double) trackview.editor().sample_to_pixel(snap_pixel_to_sample(x));
2557 MidiRegionView::get_position_pixels()
2559 framepos_t region_frame = get_position();
2560 return trackview.editor().sample_to_pixel(region_frame);
2564 MidiRegionView::get_end_position_pixels()
2566 framepos_t frame = get_position() + get_duration ();
2567 return trackview.editor().sample_to_pixel(frame);
2571 MidiRegionView::source_beats_to_absolute_frames(Evoral::MusicalTime beats) const
2573 /* the time converter will return the frame corresponding to `beats'
2574 relative to the start of the source. The start of the source
2575 is an implied position given by region->position - region->start
2577 const framepos_t source_start = _region->position() - _region->start();
2578 return source_start + _source_relative_time_converter.to (beats);
2582 MidiRegionView::absolute_frames_to_source_beats(framepos_t frames) const
2584 /* the `frames' argument needs to be converted into a frame count
2585 relative to the start of the source before being passed in to the
2588 const framepos_t source_start = _region->position() - _region->start();
2589 return _source_relative_time_converter.from (frames - source_start);
2593 MidiRegionView::region_beats_to_region_frames(Evoral::MusicalTime beats) const
2595 return _region_relative_time_converter.to(beats);
2599 MidiRegionView::region_frames_to_region_beats(framepos_t frames) const
2601 return _region_relative_time_converter.from(frames);
2605 MidiRegionView::begin_resizing (bool /*at_front*/)
2607 _resize_data.clear();
2609 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2610 Note *note = dynamic_cast<Note*> (*i);
2612 // only insert CanvasNotes into the map
2614 NoteResizeData *resize_data = new NoteResizeData();
2615 resize_data->note = note;
2617 // create a new SimpleRect from the note which will be the resize preview
2618 ArdourCanvas::Rectangle *resize_rect = new ArdourCanvas::Rectangle (_note_group,
2619 ArdourCanvas::Rect (note->x0(), note->y0(), note->x0(), note->y1()));
2621 // calculate the colors: get the color settings
2622 uint32_t fill_color = UINT_RGBA_CHANGE_A(
2623 ARDOUR_UI::config()->get_MidiNoteSelected(),
2626 // make the resize preview notes more transparent and bright
2627 fill_color = UINT_INTERPOLATE(fill_color, 0xFFFFFF40, 0.5);
2629 // calculate color based on note velocity
2630 resize_rect->set_fill_color (UINT_INTERPOLATE(
2631 NoteBase::meter_style_fill_color(note->note()->velocity(), note->selected()),
2635 resize_rect->set_outline_color (NoteBase::calculate_outline (
2636 ARDOUR_UI::config()->get_MidiNoteSelected()));
2638 resize_data->resize_rect = resize_rect;
2639 _resize_data.push_back(resize_data);
2644 /** Update resizing notes while user drags.
2645 * @param primary `primary' note for the drag; ie the one that is used as the reference in non-relative mode.
2646 * @param at_front which end of the note (true == note on, false == note off)
2647 * @param delta_x change in mouse position since the start of the drag
2648 * @param relative true if relative resizing is taking place, false if absolute resizing. This only makes
2649 * a difference when multiple notes are being resized; in relative mode, each note's length is changed by the
2650 * amount of the drag. In non-relative mode, all selected notes are set to have the same start or end point
2651 * as the \a primary note.
2654 MidiRegionView::update_resizing (NoteBase* primary, bool at_front, double delta_x, bool relative)
2656 bool cursor_set = false;
2658 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2659 ArdourCanvas::Rectangle* resize_rect = (*i)->resize_rect;
2660 Note* canvas_note = (*i)->note;
2665 current_x = canvas_note->x0() + delta_x;
2667 current_x = primary->x0() + delta_x;
2671 current_x = canvas_note->x1() + delta_x;
2673 current_x = primary->x1() + delta_x;
2677 if (current_x < 0) {
2678 // This works even with snapping because RegionView::snap_frame_to_frame()
2679 // snaps forward if the snapped sample is before the beginning of the region
2682 if (current_x > trackview.editor().sample_to_pixel(_region->length())) {
2683 current_x = trackview.editor().sample_to_pixel(_region->length());
2687 resize_rect->set_x0 (snap_to_pixel(current_x));
2688 resize_rect->set_x1 (canvas_note->x1());
2690 resize_rect->set_x1 (snap_to_pixel(current_x));
2691 resize_rect->set_x0 (canvas_note->x0());
2695 const double snapped_x = snap_pixel_to_sample (current_x);
2696 Evoral::MusicalTime beats = region_frames_to_region_beats (snapped_x);
2697 Evoral::MusicalTime len = Evoral::MusicalTime();
2700 if (beats < canvas_note->note()->end_time()) {
2701 len = canvas_note->note()->time() - beats;
2702 len += canvas_note->note()->length();
2705 if (beats >= canvas_note->note()->time()) {
2706 len = beats - canvas_note->note()->time();
2711 snprintf (buf, sizeof (buf), "%.3g beats", len.to_double());
2712 show_verbose_cursor (buf, 0, 0);
2721 /** Finish resizing notes when the user releases the mouse button.
2722 * Parameters the same as for \a update_resizing().
2725 MidiRegionView::commit_resizing (NoteBase* primary, bool at_front, double delta_x, bool relative)
2727 start_note_diff_command (_("resize notes"));
2729 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2730 Note* canvas_note = (*i)->note;
2731 ArdourCanvas::Rectangle* resize_rect = (*i)->resize_rect;
2733 /* Get the new x position for this resize, which is in pixels relative
2734 * to the region position.
2741 current_x = canvas_note->x0() + delta_x;
2743 current_x = primary->x0() + delta_x;
2747 current_x = canvas_note->x1() + delta_x;
2749 current_x = primary->x1() + delta_x;
2753 if (current_x < 0) {
2756 if (current_x > trackview.editor().sample_to_pixel(_region->length())) {
2757 current_x = trackview.editor().sample_to_pixel(_region->length());
2760 /* Convert that to a frame within the source */
2761 current_x = snap_pixel_to_sample (current_x) + _region->start ();
2763 /* and then to beats */
2764 const Evoral::MusicalTime x_beats = region_frames_to_region_beats (current_x);
2766 if (at_front && x_beats < canvas_note->note()->end_time()) {
2767 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::StartTime, x_beats);
2769 Evoral::MusicalTime len = canvas_note->note()->time() - x_beats;
2770 len += canvas_note->note()->length();
2773 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
2778 const Evoral::MusicalTime len = x_beats - canvas_note->note()->time();
2781 /* XXX convert to beats */
2782 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
2790 _resize_data.clear();
2795 MidiRegionView::abort_resizing ()
2797 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2798 delete (*i)->resize_rect;
2802 _resize_data.clear ();
2806 MidiRegionView::change_note_velocity(NoteBase* event, int8_t velocity, bool relative)
2808 uint8_t new_velocity;
2811 new_velocity = event->note()->velocity() + velocity;
2812 clamp_to_0_127(new_velocity);
2814 new_velocity = velocity;
2817 event->set_selected (event->selected()); // change color
2819 note_diff_add_change (event, MidiModel::NoteDiffCommand::Velocity, new_velocity);
2823 MidiRegionView::change_note_note (NoteBase* event, int8_t note, bool relative)
2828 new_note = event->note()->note() + note;
2833 clamp_to_0_127 (new_note);
2834 note_diff_add_change (event, MidiModel::NoteDiffCommand::NoteNumber, new_note);
2838 MidiRegionView::trim_note (NoteBase* event, Evoral::MusicalTime front_delta, Evoral::MusicalTime end_delta)
2840 bool change_start = false;
2841 bool change_length = false;
2842 Evoral::MusicalTime new_start;
2843 Evoral::MusicalTime new_length;
2845 /* NOTE: the semantics of the two delta arguments are slightly subtle:
2847 front_delta: if positive - move the start of the note later in time (shortening it)
2848 if negative - move the start of the note earlier in time (lengthening it)
2850 end_delta: if positive - move the end of the note later in time (lengthening it)
2851 if negative - move the end of the note earlier in time (shortening it)
2854 if (!!front_delta) {
2855 if (front_delta < 0) {
2857 if (event->note()->time() < -front_delta) {
2858 new_start = Evoral::MusicalTime();
2860 new_start = event->note()->time() + front_delta; // moves earlier
2863 /* start moved toward zero, so move the end point out to where it used to be.
2864 Note that front_delta is negative, so this increases the length.
2867 new_length = event->note()->length() - front_delta;
2868 change_start = true;
2869 change_length = true;
2873 Evoral::MusicalTime new_pos = event->note()->time() + front_delta;
2875 if (new_pos < event->note()->end_time()) {
2876 new_start = event->note()->time() + front_delta;
2877 /* start moved toward the end, so move the end point back to where it used to be */
2878 new_length = event->note()->length() - front_delta;
2879 change_start = true;
2880 change_length = true;
2887 bool can_change = true;
2888 if (end_delta < 0) {
2889 if (event->note()->length() < -end_delta) {
2895 new_length = event->note()->length() + end_delta;
2896 change_length = true;
2901 note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_start);
2904 if (change_length) {
2905 note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, new_length);
2910 MidiRegionView::change_note_channel (NoteBase* event, int8_t chn, bool relative)
2912 uint8_t new_channel;
2916 if (event->note()->channel() < -chn) {
2919 new_channel = event->note()->channel() + chn;
2922 new_channel = event->note()->channel() + chn;
2925 new_channel = (uint8_t) chn;
2928 note_diff_add_change (event, MidiModel::NoteDiffCommand::Channel, new_channel);
2932 MidiRegionView::change_note_time (NoteBase* event, Evoral::MusicalTime delta, bool relative)
2934 Evoral::MusicalTime new_time;
2938 if (event->note()->time() < -delta) {
2939 new_time = Evoral::MusicalTime();
2941 new_time = event->note()->time() + delta;
2944 new_time = event->note()->time() + delta;
2950 note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_time);
2954 MidiRegionView::change_note_length (NoteBase* event, Evoral::MusicalTime t)
2956 note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, t);
2960 MidiRegionView::change_velocities (bool up, bool fine, bool allow_smush, bool all_together)
2965 if (_selection.empty()) {
2980 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2981 if ((*i)->note()->velocity() < -delta || (*i)->note()->velocity() + delta > 127) {
2987 start_note_diff_command (_("change velocities"));
2989 for (Selection::iterator i = _selection.begin(); i != _selection.end();) {
2990 Selection::iterator next = i;
2994 if (i == _selection.begin()) {
2995 change_note_velocity (*i, delta, true);
2996 value = (*i)->note()->velocity() + delta;
2998 change_note_velocity (*i, value, false);
3002 change_note_velocity (*i, delta, true);
3011 if (!_selection.empty()) {
3013 snprintf (buf, sizeof (buf), "Vel %d",
3014 (int) (*_selection.begin())->note()->velocity());
3015 show_verbose_cursor (buf, 10, 10);
3021 MidiRegionView::transpose (bool up, bool fine, bool allow_smush)
3023 if (_selection.empty()) {
3040 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3042 if ((int8_t) (*i)->note()->note() + delta <= 0) {
3046 if ((int8_t) (*i)->note()->note() + delta > 127) {
3053 start_note_diff_command (_("transpose"));
3055 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3056 Selection::iterator next = i;
3058 change_note_note (*i, delta, true);
3066 MidiRegionView::change_note_lengths (bool fine, bool shorter, Evoral::MusicalTime delta, bool start, bool end)
3070 delta = Evoral::MusicalTime(1.0/128.0);
3072 /* grab the current grid distance */
3073 delta = get_grid_beats(_region->position());
3081 start_note_diff_command (_("change note lengths"));
3083 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3084 Selection::iterator next = i;
3087 /* note the negation of the delta for start */
3090 (start ? -delta : Evoral::MusicalTime()),
3091 (end ? delta : Evoral::MusicalTime()));
3100 MidiRegionView::nudge_notes (bool forward, bool fine)
3102 if (_selection.empty()) {
3106 /* pick a note as the point along the timeline to get the nudge distance.
3107 its not necessarily the earliest note, so we may want to pull the notes out
3108 into a vector and sort before using the first one.
3111 const framepos_t ref_point = source_beats_to_absolute_frames ((*(_selection.begin()))->note()->time());
3112 Evoral::MusicalTime delta;
3116 /* non-fine, move by 1 bar regardless of snap */
3117 delta = Evoral::MusicalTime(trackview.session()->tempo_map().meter_at(ref_point).divisions_per_bar());
3119 } else if (trackview.editor().snap_mode() == Editing::SnapOff) {
3121 /* grid is off - use nudge distance */
3124 const framecnt_t distance = trackview.editor().get_nudge_distance (ref_point, unused);
3125 delta = region_frames_to_region_beats (fabs ((double)distance));
3131 framepos_t next_pos = ref_point;
3134 if (max_framepos - 1 < next_pos) {
3138 if (next_pos == 0) {
3144 trackview.editor().snap_to (next_pos, (forward ? RoundUpAlways : RoundDownAlways), false);
3145 const framecnt_t distance = ref_point - next_pos;
3146 delta = region_frames_to_region_beats (fabs ((double)distance));
3157 start_note_diff_command (_("nudge"));
3159 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3160 Selection::iterator next = i;
3162 change_note_time (*i, delta, true);
3170 MidiRegionView::change_channel(uint8_t channel)
3172 start_note_diff_command(_("change channel"));
3173 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3174 note_diff_add_change (*i, MidiModel::NoteDiffCommand::Channel, channel);
3182 MidiRegionView::note_entered(NoteBase* ev)
3184 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3186 pre_note_enter_cursor = editor->get_canvas_cursor ();
3188 if (_mouse_state == SelectTouchDragging) {
3189 note_selected (ev, true);
3192 show_verbose_cursor (ev->note ());
3196 MidiRegionView::note_left (NoteBase*)
3198 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3200 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3201 (*i)->hide_velocity ();
3204 editor->verbose_cursor()->hide ();
3206 if (pre_note_enter_cursor) {
3207 editor->set_canvas_cursor (pre_note_enter_cursor);
3208 pre_note_enter_cursor = 0;
3213 MidiRegionView::patch_entered (PatchChange* p)
3216 /* XXX should get patch name if we can */
3217 s << _("Bank ") << (p->patch()->bank() + MIDI_BP_ZERO) << '\n'
3218 << _("Program ") << ((int) p->patch()->program()) + MIDI_BP_ZERO << '\n'
3219 << _("Channel ") << ((int) p->patch()->channel() + 1);
3220 show_verbose_cursor (s.str(), 10, 20);
3221 p->item().grab_focus();
3225 MidiRegionView::patch_left (PatchChange *)
3227 trackview.editor().verbose_cursor()->hide ();
3228 /* focus will transfer back via the enter-notify event sent to this
3234 MidiRegionView::sysex_entered (SysEx* p)
3238 // need a way to extract text from p->_flag->_text
3240 // show_verbose_cursor (s.str(), 10, 20);
3241 p->item().grab_focus();
3245 MidiRegionView::sysex_left (SysEx *)
3247 trackview.editor().verbose_cursor()->hide ();
3248 /* focus will transfer back via the enter-notify event sent to this
3254 MidiRegionView::note_mouse_position (float x_fraction, float /*y_fraction*/, bool can_set_cursor)
3256 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3257 Editing::MouseMode mm = editor->current_mouse_mode();
3258 bool trimmable = (mm == MouseObject || mm == MouseTimeFX || mm == MouseDraw);
3260 if (can_set_cursor) {
3261 if (trimmable && x_fraction > 0.0 && x_fraction < 0.2) {
3262 editor->set_canvas_cursor (editor->cursors()->left_side_trim);
3263 } else if (trimmable && x_fraction >= 0.8 && x_fraction < 1.0) {
3264 editor->set_canvas_cursor (editor->cursors()->right_side_trim);
3265 } else if (pre_note_enter_cursor) {
3266 editor->set_canvas_cursor (pre_note_enter_cursor);
3272 MidiRegionView::set_frame_color()
3276 TimeAxisViewItem::set_frame_color ();
3283 f = ARDOUR_UI::config()->get_SelectedFrameBase();
3284 } else if (high_enough_for_name) {
3285 f= ARDOUR_UI::config()->get_MidiFrameBase();
3290 if (!rect_visible) {
3291 f = UINT_RGBA_CHANGE_A (f, 80);
3294 frame->set_fill_color (f);
3298 MidiRegionView::midi_channel_mode_changed ()
3300 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3301 uint16_t mask = mtv->midi_track()->get_playback_channel_mask();
3302 ChannelMode mode = mtv->midi_track()->get_playback_channel_mode ();
3304 if (mode == ForceChannel) {
3305 mask = 0xFFFF; // Show all notes as active (below)
3308 // Update notes for selection
3309 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3310 (*i)->on_channel_selection_change (mask);
3313 _patch_changes.clear ();
3314 display_patch_changes ();
3318 MidiRegionView::instrument_settings_changed ()
3324 MidiRegionView::cut_copy_clear (Editing::CutCopyOp op)
3326 if (_selection.empty()) {
3330 PublicEditor& editor (trackview.editor());
3334 /* XXX what to do ? */
3338 editor.get_cut_buffer().add (selection_as_cut_buffer());
3346 start_note_diff_command();
3348 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3355 note_diff_remove_note (*i);
3365 MidiRegionView::selection_as_cut_buffer () const
3369 for (Selection::const_iterator i = _selection.begin(); i != _selection.end(); ++i) {
3370 NoteType* n = (*i)->note().get();
3371 notes.insert (boost::shared_ptr<NoteType> (new NoteType (*n)));
3374 MidiCutBuffer* cb = new MidiCutBuffer (trackview.session());
3380 /** This method handles undo */
3382 MidiRegionView::paste (framepos_t pos, const ::Selection& selection, PasteContext& ctx)
3384 trackview.session()->begin_reversible_command (Operations::paste);
3386 // Paste notes, if available
3387 MidiNoteSelection::const_iterator m = selection.midi_notes.get_nth(ctx.counts.n_notes());
3388 if (m != selection.midi_notes.end()) {
3389 ctx.counts.increase_n_notes();
3390 paste_internal(pos, ctx.count, ctx.times, **m);
3393 // Paste control points to automation children, if available
3394 typedef RouteTimeAxisView::AutomationTracks ATracks;
3395 const ATracks& atracks = midi_view()->automation_tracks();
3396 for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) {
3397 a->second->paste(pos, selection, ctx);
3400 trackview.session()->commit_reversible_command ();
3405 /** This method handles undo */
3407 MidiRegionView::paste_internal (framepos_t pos, unsigned paste_count, float times, const MidiCutBuffer& mcb)
3413 start_note_diff_command (_("paste"));
3415 const Evoral::MusicalTime snap_beats = get_grid_beats(pos);
3416 const Evoral::MusicalTime first_time = (*mcb.notes().begin())->time();
3417 const Evoral::MusicalTime last_time = (*mcb.notes().rbegin())->end_time();
3418 const Evoral::MusicalTime duration = last_time - first_time;
3419 const Evoral::MusicalTime snap_duration = duration.snap_to(snap_beats);
3420 const Evoral::MusicalTime paste_offset = snap_duration * paste_count;
3421 const Evoral::MusicalTime pos_beats = absolute_frames_to_source_beats(pos) + paste_offset;
3422 Evoral::MusicalTime end_point = Evoral::MusicalTime();
3424 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste data spans from %1 to %2 (%3) ; paste pos beats = %4 (based on %5 - %6)\n",
3427 duration, pos, _region->position(),
3432 for (int n = 0; n < (int) times; ++n) {
3434 for (Notes::const_iterator i = mcb.notes().begin(); i != mcb.notes().end(); ++i) {
3436 boost::shared_ptr<NoteType> copied_note (new NoteType (*((*i).get())));
3437 copied_note->set_time (pos_beats + copied_note->time() - first_time);
3439 /* make all newly added notes selected */
3441 note_diff_add_note (copied_note, true);
3442 end_point = copied_note->end_time();
3446 /* if we pasted past the current end of the region, extend the region */
3448 framepos_t end_frame = source_beats_to_absolute_frames (end_point);
3449 framepos_t region_end = _region->position() + _region->length() - 1;
3451 if (end_frame > region_end) {
3453 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste extended region from %1 to %2\n", region_end, end_frame));
3455 _region->clear_changes ();
3456 _region->set_length (end_frame - _region->position());
3457 trackview.session()->add_command (new StatefulDiffCommand (_region));
3463 struct EventNoteTimeEarlyFirstComparator {
3464 bool operator() (NoteBase* a, NoteBase* b) {
3465 return a->note()->time() < b->note()->time();
3470 MidiRegionView::time_sort_events ()
3472 if (!_sort_needed) {
3476 EventNoteTimeEarlyFirstComparator cmp;
3479 _sort_needed = false;
3483 MidiRegionView::goto_next_note (bool add_to_selection)
3485 bool use_next = false;
3487 if (_events.back()->selected()) {
3491 time_sort_events ();
3493 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3494 uint16_t const channel_mask = mtv->midi_track()->get_playback_channel_mask();
3496 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3497 if ((*i)->selected()) {
3500 } else if (use_next) {
3501 if (channel_mask & (1 << (*i)->note()->channel())) {
3502 if (!add_to_selection) {
3505 note_selected (*i, true, false);
3512 /* use the first one */
3514 if (!_events.empty() && (channel_mask & (1 << _events.front()->note()->channel ()))) {
3515 unique_select (_events.front());
3520 MidiRegionView::goto_previous_note (bool add_to_selection)
3522 bool use_next = false;
3524 if (_events.front()->selected()) {
3528 time_sort_events ();
3530 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3531 uint16_t const channel_mask = mtv->midi_track()->get_playback_channel_mask ();
3533 for (Events::reverse_iterator i = _events.rbegin(); i != _events.rend(); ++i) {
3534 if ((*i)->selected()) {
3537 } else if (use_next) {
3538 if (channel_mask & (1 << (*i)->note()->channel())) {
3539 if (!add_to_selection) {
3542 note_selected (*i, true, false);
3549 /* use the last one */
3551 if (!_events.empty() && (channel_mask & (1 << (*_events.rbegin())->note()->channel ()))) {
3552 unique_select (*(_events.rbegin()));
3557 MidiRegionView::selection_as_notelist (Notes& selected, bool allow_all_if_none_selected)
3559 bool had_selected = false;
3561 time_sort_events ();
3563 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3564 if ((*i)->selected()) {
3565 selected.insert ((*i)->note());
3566 had_selected = true;
3570 if (allow_all_if_none_selected && !had_selected) {
3571 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3572 selected.insert ((*i)->note());
3578 MidiRegionView::update_ghost_note (double x, double y)
3580 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3585 _note_group->canvas_to_item (x, y);
3587 PublicEditor& editor = trackview.editor ();
3589 framepos_t const unsnapped_frame = editor.pixel_to_sample (x);
3590 framecnt_t grid_frames;
3591 framepos_t const f = snap_frame_to_grid_underneath (unsnapped_frame, grid_frames);
3593 /* use region_frames... because we are converting a delta within the region
3596 const Evoral::MusicalTime length = get_grid_beats(unsnapped_frame);
3598 /* note that this sets the time of the ghost note in beats relative to
3599 the start of the source; that is how all note times are stored.
3601 _ghost_note->note()->set_time (
3602 std::max(Evoral::MusicalTime(),
3603 absolute_frames_to_source_beats (f + _region->position ())));
3604 _ghost_note->note()->set_length (length);
3605 _ghost_note->note()->set_note (midi_stream_view()->y_to_note (y));
3606 _ghost_note->note()->set_channel (mtv->get_channel_for_add ());
3608 /* the ghost note does not appear in ghost regions, so pass false in here */
3609 update_note (_ghost_note, false);
3611 show_verbose_cursor (_ghost_note->note ());
3615 MidiRegionView::create_ghost_note (double x, double y)
3617 remove_ghost_note ();
3619 boost::shared_ptr<NoteType> g (new NoteType);
3620 _ghost_note = new Note (*this, _note_group, g);
3621 _ghost_note->set_ignore_events (true);
3622 _ghost_note->set_outline_color (0x000000aa);
3623 if (x < 0) { x = 0; }
3624 update_ghost_note (x, y);
3625 _ghost_note->show ();
3630 show_verbose_cursor (_ghost_note->note ());
3634 MidiRegionView::remove_ghost_note ()
3641 MidiRegionView::snap_changed ()
3647 create_ghost_note (_last_ghost_x, _last_ghost_y);
3651 MidiRegionView::drop_down_keys ()
3653 _mouse_state = None;
3657 MidiRegionView::maybe_select_by_position (GdkEventButton* ev, double /*x*/, double y)
3659 /* XXX: This is dead code. What was it for? */
3661 double note = midi_stream_view()->y_to_note(y);
3663 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3665 uint16_t chn_mask = mtv->midi_track()->get_playback_channel_mask();
3667 if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
3668 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchGreaterThanOrEqual, (uint8_t) floor (note), chn_mask);
3669 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
3670 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchLessThanOrEqual, (uint8_t) floor (note), chn_mask);
3675 bool add_mrv_selection = false;
3677 if (_selection.empty()) {
3678 add_mrv_selection = true;
3681 for (Events::iterator i = e.begin(); i != e.end(); ++i) {
3682 if (_selection.insert (*i).second) {
3683 (*i)->set_selected (true);
3687 if (add_mrv_selection) {
3688 PublicEditor& editor (trackview.editor());
3689 editor.get_selection().add (this);
3694 MidiRegionView::color_handler ()
3696 RegionView::color_handler ();
3698 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3699 (*i)->set_selected ((*i)->selected()); // will change color
3702 /* XXX probably more to do here */
3706 MidiRegionView::enable_display (bool yn)
3708 RegionView::enable_display (yn);
3715 MidiRegionView::show_step_edit_cursor (Evoral::MusicalTime pos)
3717 if (_step_edit_cursor == 0) {
3718 ArdourCanvas::Item* const group = get_canvas_group();
3720 _step_edit_cursor = new ArdourCanvas::Rectangle (group);
3721 _step_edit_cursor->set_y0 (0);
3722 _step_edit_cursor->set_y1 (midi_stream_view()->contents_height());
3723 _step_edit_cursor->set_fill_color (RGBA_TO_UINT (45,0,0,90));
3724 _step_edit_cursor->set_outline_color (RGBA_TO_UINT (85,0,0,90));
3727 move_step_edit_cursor (pos);
3728 _step_edit_cursor->show ();
3732 MidiRegionView::move_step_edit_cursor (Evoral::MusicalTime pos)
3734 _step_edit_cursor_position = pos;
3736 if (_step_edit_cursor) {
3737 double pixel = trackview.editor().sample_to_pixel (region_beats_to_region_frames (pos));
3738 _step_edit_cursor->set_x0 (pixel);
3739 set_step_edit_cursor_width (_step_edit_cursor_width);
3744 MidiRegionView::hide_step_edit_cursor ()
3746 if (_step_edit_cursor) {
3747 _step_edit_cursor->hide ();
3752 MidiRegionView::set_step_edit_cursor_width (Evoral::MusicalTime beats)
3754 _step_edit_cursor_width = beats;
3756 if (_step_edit_cursor) {
3757 _step_edit_cursor->set_x1 (_step_edit_cursor->x0() + trackview.editor().sample_to_pixel (region_beats_to_region_frames (beats)));
3761 /** Called when a diskstream on our track has received some data. Update the view, if applicable.
3762 * @param w Source that the data will end up in.
3765 MidiRegionView::data_recorded (boost::weak_ptr<MidiSource> w)
3767 if (!_active_notes) {
3768 /* we aren't actively being recorded to */
3772 boost::shared_ptr<MidiSource> src = w.lock ();
3773 if (!src || src != midi_region()->midi_source()) {
3774 /* recorded data was not destined for our source */
3778 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (&trackview);
3780 boost::shared_ptr<MidiBuffer> buf = mtv->midi_track()->get_gui_feed_buffer ();
3782 BeatsFramesConverter converter (trackview.session()->tempo_map(), mtv->midi_track()->get_capture_start_frame (0));
3784 framepos_t back = max_framepos;
3786 for (MidiBuffer::iterator i = buf->begin(); i != buf->end(); ++i) {
3787 Evoral::MIDIEvent<MidiBuffer::TimeType> const ev (*i, false);
3789 if (ev.is_channel_event()) {
3790 if (get_channel_mode() == FilterChannels) {
3791 if (((uint16_t(1) << ev.channel()) & get_selected_channels()) == 0) {
3797 /* ev.time() is in session frames, so (ev.time() - converter.origin_b()) is
3798 frames from the start of the source, and so time_beats is in terms of the
3802 Evoral::MusicalTime const time_beats = converter.from (ev.time () - converter.origin_b ());
3804 if (ev.type() == MIDI_CMD_NOTE_ON) {
3805 boost::shared_ptr<NoteType> note (
3806 new NoteType (ev.channel(), time_beats, Evoral::MusicalTime(), ev.note(), ev.velocity()));
3808 add_note (note, true);
3810 /* fix up our note range */
3811 if (ev.note() < _current_range_min) {
3812 midi_stream_view()->apply_note_range (ev.note(), _current_range_max, true);
3813 } else if (ev.note() > _current_range_max) {
3814 midi_stream_view()->apply_note_range (_current_range_min, ev.note(), true);
3817 } else if (ev.type() == MIDI_CMD_NOTE_OFF) {
3818 resolve_note (ev.note (), time_beats);
3824 midi_stream_view()->check_record_layers (region(), back);
3828 MidiRegionView::trim_front_starting ()
3830 /* Reparent the note group to the region view's parent, so that it doesn't change
3831 when the region view is trimmed.
3833 _temporary_note_group = new ArdourCanvas::Container (group->parent ());
3834 _temporary_note_group->move (group->position ());
3835 _note_group->reparent (_temporary_note_group);
3839 MidiRegionView::trim_front_ending ()
3841 _note_group->reparent (group);
3842 delete _temporary_note_group;
3843 _temporary_note_group = 0;
3845 if (_region->start() < 0) {
3846 /* Trim drag made start time -ve; fix this */
3847 midi_region()->fix_negative_start ();
3852 MidiRegionView::edit_patch_change (PatchChange* pc)
3854 PatchChangeDialog d (&_source_relative_time_converter, trackview.session(), *pc->patch (), instrument_info(), Gtk::Stock::APPLY, true);
3856 int response = d.run();
3859 case Gtk::RESPONSE_ACCEPT:
3861 case Gtk::RESPONSE_REJECT:
3862 delete_patch_change (pc);
3868 change_patch_change (pc->patch(), d.patch ());
3872 MidiRegionView::delete_sysex (SysEx* /*sysex*/)
3875 // sysyex object doesn't have a pointer to a sysex event
3876 // MidiModel::SysExDiffCommand* c = _model->new_sysex_diff_command (_("delete sysex"));
3877 // c->remove (sysex->sysex());
3878 // _model->apply_command (*trackview.session(), c);
3880 //_sys_exes.clear ();
3881 // display_sysexes();
3885 MidiRegionView::show_verbose_cursor (boost::shared_ptr<NoteType> n) const
3887 using namespace MIDI::Name;
3891 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3893 boost::shared_ptr<MasterDeviceNames> device_names(mtv->get_device_names());
3895 MIDI::Name::PatchPrimaryKey patch_key;
3896 get_patch_key_at(n->time(), n->channel(), patch_key);
3897 name = device_names->note_name(mtv->gui_property(X_("midnam-custom-device-mode")),
3899 patch_key.bank_number,
3900 patch_key.program_number,
3906 snprintf (buf, sizeof (buf), "%d %s\nCh %d Vel %d",
3908 name.empty() ? Evoral::midi_note_name (n->note()).c_str() : name.c_str(),
3909 (int) n->channel() + 1,
3910 (int) n->velocity());
3912 show_verbose_cursor(buf, 10, 20);
3916 MidiRegionView::show_verbose_cursor (string const & text, double xoffset, double yoffset) const
3918 trackview.editor().verbose_cursor()->set (text);
3919 trackview.editor().verbose_cursor()->show ();
3920 trackview.editor().verbose_cursor()->set_offset (ArdourCanvas::Duple (xoffset, yoffset));
3923 /** @param p A session framepos.
3924 * @param grid_frames Filled in with the number of frames that a grid interval is at p.
3925 * @return p snapped to the grid subdivision underneath it.
3928 MidiRegionView::snap_frame_to_grid_underneath (framepos_t p, framecnt_t& grid_frames) const
3930 PublicEditor& editor = trackview.editor ();
3932 const Evoral::MusicalTime grid_beats = get_grid_beats(p);
3934 grid_frames = region_beats_to_region_frames (grid_beats);
3936 /* Hack so that we always snap to the note that we are over, instead of snapping
3937 to the next one if we're more than halfway through the one we're over.
3939 if (editor.snap_mode() == SnapNormal && p >= grid_frames / 2) {
3940 p -= grid_frames / 2;
3943 return snap_frame_to_frame (p);
3946 /** Called when the selection has been cleared in any MidiRegionView.
3947 * @param rv MidiRegionView that the selection was cleared in.
3950 MidiRegionView::selection_cleared (MidiRegionView* rv)
3956 /* Clear our selection in sympathy; but don't signal the fact */
3957 clear_selection (false);
3961 MidiRegionView::note_button_release ()
3963 delete _note_player;
3968 MidiRegionView::get_channel_mode () const
3970 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (&trackview);
3971 return rtav->midi_track()->get_playback_channel_mode();
3975 MidiRegionView::get_selected_channels () const
3977 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (&trackview);
3978 return rtav->midi_track()->get_playback_channel_mask();
3983 MidiRegionView::get_grid_beats(framepos_t pos) const
3985 PublicEditor& editor = trackview.editor();
3986 bool success = false;
3987 Evoral::MusicalTime beats = editor.get_grid_type_as_beats(success, pos);
3989 beats = Evoral::MusicalTime(1);