2 Copyright (C) 2001-2011 Paul Davis
3 Author: David Robillard
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 #include "gtkmm2ext/gtk_ui.h"
28 #include <sigc++/signal.h>
30 #include "midi++/midnam_patch.h"
32 #include "pbd/memento_command.h"
33 #include "pbd/stateful_diff_command.h"
35 #include "ardour/midi_model.h"
36 #include "ardour/midi_region.h"
37 #include "ardour/midi_source.h"
38 #include "ardour/midi_track.h"
39 #include "ardour/operations.h"
40 #include "ardour/session.h"
42 #include "evoral/Parameter.hpp"
43 #include "evoral/MIDIEvent.hpp"
44 #include "evoral/Control.hpp"
45 #include "evoral/midi_util.h"
47 #include "canvas/debug.h"
48 #include "canvas/text.h"
50 #include "automation_region_view.h"
51 #include "automation_time_axis.h"
52 #include "control_point.h"
55 #include "editor_drag.h"
56 #include "ghostregion.h"
57 #include "gui_thread.h"
58 #include "item_counts.h"
60 #include "midi_channel_dialog.h"
61 #include "midi_cut_buffer.h"
62 #include "midi_list_editor.h"
63 #include "midi_region_view.h"
64 #include "midi_streamview.h"
65 #include "midi_time_axis.h"
66 #include "midi_util.h"
67 #include "midi_velocity_dialog.h"
68 #include "mouse_cursors.h"
69 #include "note_player.h"
70 #include "paste_context.h"
71 #include "public_editor.h"
72 #include "route_time_axis.h"
73 #include "rgb_macros.h"
74 #include "selection.h"
75 #include "streamview.h"
76 #include "patch_change_dialog.h"
77 #include "verbose_cursor.h"
78 #include "ardour_ui.h"
81 #include "patch_change.h"
86 using namespace ARDOUR;
88 using namespace Editing;
90 using Gtkmm2ext::Keyboard;
92 PBD::Signal1<void, MidiRegionView *> MidiRegionView::SelectionCleared;
94 #define MIDI_BP_ZERO ((Config->get_first_midi_bank_is_zero())?0:1)
96 MidiRegionView::MidiRegionView (ArdourCanvas::Container* parent,
97 RouteTimeAxisView& tv,
98 boost::shared_ptr<MidiRegion> r,
100 uint32_t basic_color)
101 : RegionView (parent, tv, r, spu, basic_color)
102 , _current_range_min(0)
103 , _current_range_max(0)
104 , _region_relative_time_converter(r->session().tempo_map(), r->position())
105 , _source_relative_time_converter(r->session().tempo_map(), r->position() - r->start())
107 , _note_group (new ArdourCanvas::Container (group))
108 , _note_diff_command (0)
110 , _step_edit_cursor (0)
111 , _step_edit_cursor_width (1.0)
112 , _step_edit_cursor_position (0.0)
113 , _channel_selection_scoped_note (0)
114 , _temporary_note_group (0)
117 , _sort_needed (true)
118 , _optimization_iterator (_events.end())
120 , _no_sound_notes (false)
123 , _grabbed_keyboard (false)
126 CANVAS_DEBUG_NAME (_note_group, string_compose ("note group for %1", get_item_name()));
127 _note_group->raise_to_top();
128 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
130 Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&MidiRegionView::parameter_changed, this, _1), gui_context());
131 connect_to_diskstream ();
133 SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), boost::bind (&MidiRegionView::selection_cleared, this, _1), gui_context ());
135 PublicEditor& editor (trackview.editor());
136 editor.get_selection().ClearMidiNoteSelection.connect (_clear_midi_selection_connection, invalidator (*this), boost::bind (&MidiRegionView::clear_midi_selection, this), gui_context ());
139 MidiRegionView::MidiRegionView (ArdourCanvas::Container* parent,
140 RouteTimeAxisView& tv,
141 boost::shared_ptr<MidiRegion> r,
143 uint32_t basic_color,
145 TimeAxisViewItem::Visibility visibility)
146 : RegionView (parent, tv, r, spu, basic_color, recording, visibility)
147 , _current_range_min(0)
148 , _current_range_max(0)
149 , _region_relative_time_converter(r->session().tempo_map(), r->position())
150 , _source_relative_time_converter(r->session().tempo_map(), r->position() - r->start())
152 , _note_group (new ArdourCanvas::Container (parent))
153 , _note_diff_command (0)
155 , _step_edit_cursor (0)
156 , _step_edit_cursor_width (1.0)
157 , _step_edit_cursor_position (0.0)
158 , _channel_selection_scoped_note (0)
159 , _temporary_note_group (0)
162 , _sort_needed (true)
163 , _optimization_iterator (_events.end())
165 , _no_sound_notes (false)
168 , _grabbed_keyboard (false)
171 CANVAS_DEBUG_NAME (_note_group, string_compose ("note group for %1", get_item_name()));
172 _note_group->raise_to_top();
174 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
176 connect_to_diskstream ();
178 SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), boost::bind (&MidiRegionView::selection_cleared, this, _1), gui_context ());
180 PublicEditor& editor (trackview.editor());
181 editor.get_selection().ClearMidiNoteSelection.connect (_clear_midi_selection_connection, invalidator (*this), boost::bind (&MidiRegionView::clear_midi_selection, this), 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)
224 MidiRegionView::MidiRegionView (const MidiRegionView& other, boost::shared_ptr<MidiRegion> region)
225 : RegionView (other, boost::shared_ptr<Region> (region))
226 , _current_range_min(0)
227 , _current_range_max(0)
228 , _region_relative_time_converter(other.region_relative_time_converter())
229 , _source_relative_time_converter(other.source_relative_time_converter())
231 , _note_group (new ArdourCanvas::Container (get_canvas_group()))
232 , _note_diff_command (0)
234 , _step_edit_cursor (0)
235 , _step_edit_cursor_width (1.0)
236 , _step_edit_cursor_position (0.0)
237 , _channel_selection_scoped_note (0)
238 , _temporary_note_group (0)
241 , _sort_needed (true)
242 , _optimization_iterator (_events.end())
244 , _no_sound_notes (false)
247 , _grabbed_keyboard (false)
254 MidiRegionView::init (bool wfd)
256 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
258 NoteBase::NoteBaseDeleted.connect (note_delete_connection, MISSING_INVALIDATOR,
259 boost::bind (&MidiRegionView::maybe_remove_deleted_note_from_selection, this, _1),
263 Glib::Threads::Mutex::Lock lm(midi_region()->midi_source(0)->mutex());
264 midi_region()->midi_source(0)->load_model(lm);
267 _model = midi_region()->midi_source(0)->model();
268 _enable_display = false;
269 fill_color_name = "midi frame base";
271 RegionView::init (false);
273 set_height (trackview.current_height());
276 region_sync_changed ();
277 region_resized (ARDOUR::bounds_change);
282 _enable_display = true;
285 display_model (_model);
289 reset_width_dependent_items (_pixel_width);
291 group->raise_to_top();
293 midi_view()->midi_track()->PlaybackChannelModeChanged.connect (_channel_mode_changed_connection, invalidator (*this),
294 boost::bind (&MidiRegionView::midi_channel_mode_changed, this),
297 instrument_info().Changed.connect (_instrument_changed_connection, invalidator (*this),
298 boost::bind (&MidiRegionView::instrument_settings_changed, this), gui_context());
300 trackview.editor().SnapChanged.connect(snap_changed_connection, invalidator(*this),
301 boost::bind (&MidiRegionView::snap_changed, this),
304 trackview.editor().MouseModeChanged.connect(_mouse_mode_connection, invalidator (*this),
305 boost::bind (&MidiRegionView::mouse_mode_changed, this),
308 Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&MidiRegionView::parameter_changed, this, _1), gui_context());
309 connect_to_diskstream ();
311 SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), boost::bind (&MidiRegionView::selection_cleared, this, _1), gui_context ());
313 PublicEditor& editor (trackview.editor());
314 editor.get_selection().ClearMidiNoteSelection.connect (_clear_midi_selection_connection, invalidator (*this), boost::bind (&MidiRegionView::clear_midi_selection, this), gui_context ());
318 MidiRegionView::instrument_info () const
320 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
321 return route_ui->route()->instrument_info();
324 const boost::shared_ptr<ARDOUR::MidiRegion>
325 MidiRegionView::midi_region() const
327 return boost::dynamic_pointer_cast<ARDOUR::MidiRegion>(_region);
331 MidiRegionView::connect_to_diskstream ()
333 midi_view()->midi_track()->DataRecorded.connect(
334 *this, invalidator(*this),
335 boost::bind (&MidiRegionView::data_recorded, this, _1),
340 MidiRegionView::canvas_group_event(GdkEvent* ev)
342 if (in_destructor || _recregion) {
346 if (!trackview.editor().internal_editing()) {
347 // not in internal edit mode, so just act like a normal region
348 return RegionView::canvas_group_event (ev);
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);
369 if (scroll (&ev->scroll)) {
375 return key_press (&ev->key);
377 case GDK_KEY_RELEASE:
378 return key_release (&ev->key);
380 case GDK_BUTTON_PRESS:
381 return button_press (&ev->button);
383 case GDK_BUTTON_RELEASE:
384 r = button_release (&ev->button);
385 _note_player.reset();
388 case GDK_MOTION_NOTIFY:
389 _last_event_x = ev->motion.x;
390 _last_event_y = ev->motion.y;
391 return motion (&ev->motion);
397 return RegionView::canvas_group_event (ev);
401 MidiRegionView::enter_notify (GdkEventCrossing* ev)
410 MidiRegionView::leave_notify (GdkEventCrossing*)
419 MidiRegionView::mouse_mode_changed ()
421 // Adjust frame colour (become more transparent for internal tools)
425 if (trackview.editor().internal_editing()) {
426 // Switched in to internal editing mode while entered
429 // Switched out of internal editing mode while entered
436 MidiRegionView::enter_internal()
438 if (trackview.editor().current_mouse_mode() == MouseDraw && _mouse_state != AddDragging) {
439 // Show ghost note under pencil
440 create_ghost_note(_last_event_x, _last_event_y);
443 if (!_selection.empty()) {
444 // Grab keyboard for moving selected notes with arrow keys
445 Keyboard::magic_widget_grab_focus();
446 _grabbed_keyboard = true;
451 MidiRegionView::leave_internal()
453 trackview.editor().verbose_cursor()->hide ();
454 remove_ghost_note ();
456 if (_grabbed_keyboard) {
457 Keyboard::magic_widget_drop_focus();
458 _grabbed_keyboard = false;
463 MidiRegionView::button_press (GdkEventButton* ev)
465 if (ev->button != 1) {
469 Editor* editor = dynamic_cast<Editor *> (&trackview.editor());
470 MouseMode m = editor->current_mouse_mode();
472 if (m == MouseContent && Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())) {
473 _press_cursor_ctx = CursorContext::create(*editor, editor->cursors()->midi_pencil);
476 if (_mouse_state != SelectTouchDragging) {
478 _pressed_button = ev->button;
479 _mouse_state = Pressed;
484 _pressed_button = ev->button;
490 MidiRegionView::button_release (GdkEventButton* ev)
492 double event_x, event_y;
494 if (ev->button != 1) {
501 group->canvas_to_item (event_x, event_y);
504 PublicEditor& editor = trackview.editor ();
506 _press_cursor_ctx.reset();
508 switch (_mouse_state) {
509 case Pressed: // Clicked
511 switch (editor.current_mouse_mode()) {
513 /* no motion occured - simple click */
522 if (Keyboard::is_insert_note_event(ev)) {
524 double event_x, event_y;
528 group->canvas_to_item (event_x, event_y);
530 Evoral::MusicalTime beats = get_grid_beats(editor.pixel_to_sample(event_x));
532 /* Shorten the length by 1 tick so that we can add a new note at the next
533 grid snap without it overlapping this one.
535 beats -= Evoral::MusicalTime::tick();
537 create_note_at (editor.pixel_to_sample (event_x), event_y, beats, true);
544 Evoral::MusicalTime beats = get_grid_beats(editor.pixel_to_sample(event_x));
546 /* Shorten the length by 1 tick so that we can add a new note at the next
547 grid snap without it overlapping this one.
549 beats -= Evoral::MusicalTime::tick();
551 create_note_at (editor.pixel_to_sample (event_x), event_y, beats, true);
562 case SelectRectDragging:
564 editor.drags()->end_grab ((GdkEvent *) ev);
566 create_ghost_note (ev->x, ev->y);
578 MidiRegionView::motion (GdkEventMotion* ev)
580 PublicEditor& editor = trackview.editor ();
582 if (!_ghost_note && editor.current_mouse_mode() == MouseContent &&
583 Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier()) &&
584 _mouse_state != AddDragging) {
586 create_ghost_note (ev->x, ev->y);
588 } else if (_ghost_note && editor.current_mouse_mode() == MouseContent &&
589 Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())) {
591 update_ghost_note (ev->x, ev->y);
593 } else if (_ghost_note && editor.current_mouse_mode() == MouseContent) {
595 remove_ghost_note ();
596 editor.verbose_cursor()->hide ();
598 } else if (_ghost_note && editor.current_mouse_mode() == MouseDraw) {
600 update_ghost_note (ev->x, ev->y);
603 /* any motion immediately hides velocity text that may have been visible */
605 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
606 (*i)->hide_velocity ();
609 switch (_mouse_state) {
612 if (_pressed_button == 1) {
614 MouseMode m = editor.current_mouse_mode();
616 if (m == MouseDraw || (m == MouseContent && Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier()))) {
617 editor.drags()->set (new NoteCreateDrag (dynamic_cast<Editor *> (&editor), group, this), (GdkEvent *) ev);
618 _mouse_state = AddDragging;
619 remove_ghost_note ();
620 editor.verbose_cursor()->hide ();
622 } else if (m == MouseContent) {
623 editor.drags()->set (new MidiRubberbandSelectDrag (dynamic_cast<Editor *> (&editor), this), (GdkEvent *) ev);
624 if (!Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
627 _mouse_state = SelectRectDragging;
629 } else if (m == MouseRange) {
630 editor.drags()->set (new MidiVerticalSelectDrag (dynamic_cast<Editor *> (&editor), this), (GdkEvent *) ev);
631 _mouse_state = SelectVerticalDragging;
638 case SelectRectDragging:
639 case SelectVerticalDragging:
641 editor.drags()->motion_handler ((GdkEvent *) ev, false);
644 case SelectTouchDragging:
652 /* we may be dragging some non-note object (eg. patch-change, sysex)
655 return editor.drags()->motion_handler ((GdkEvent *) ev, false);
660 MidiRegionView::scroll (GdkEventScroll* ev)
662 if (_selection.empty()) {
666 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
667 /* XXX: bit of a hack; allow PrimaryModifier scroll through so that
668 it still works for zoom.
673 trackview.editor().verbose_cursor()->hide ();
675 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
676 bool together = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
678 if (ev->direction == GDK_SCROLL_UP) {
679 change_velocities (true, fine, false, together);
680 } else if (ev->direction == GDK_SCROLL_DOWN) {
681 change_velocities (false, fine, false, together);
683 /* left, right: we don't use them */
691 MidiRegionView::key_press (GdkEventKey* ev)
693 /* since GTK bindings are generally activated on press, and since
694 detectable auto-repeat is the name of the game and only sends
695 repeated presses, carry out key actions at key press, not release.
698 bool unmodified = Keyboard::no_modifier_keys_pressed (ev);
700 if (unmodified && (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R)) {
701 _mouse_state = SelectTouchDragging;
704 } else if (ev->keyval == GDK_Escape && unmodified) {
708 } else if (unmodified && (ev->keyval == GDK_comma || ev->keyval == GDK_period)) {
710 bool start = (ev->keyval == GDK_comma);
711 bool end = (ev->keyval == GDK_period);
712 bool shorter = Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier);
713 bool fine = Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
715 change_note_lengths (fine, shorter, Evoral::MusicalTime(), start, end);
719 } else if ((ev->keyval == GDK_BackSpace || ev->keyval == GDK_Delete) && unmodified) {
721 if (_selection.empty()) {
728 } else if (ev->keyval == GDK_Tab) {
730 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
731 goto_previous_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
733 goto_next_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
737 } else if (ev->keyval == GDK_ISO_Left_Tab) {
739 /* Shift-TAB generates ISO Left Tab, for some reason */
741 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
742 goto_previous_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
744 goto_next_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
750 } else if (ev->keyval == GDK_Up) {
752 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
753 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
754 bool together = Keyboard::modifier_state_contains (ev->state, Keyboard::Level4Modifier);
756 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
757 change_velocities (true, fine, allow_smush, together);
759 transpose (true, fine, allow_smush);
763 } else if (ev->keyval == GDK_Down) {
765 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
766 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
767 bool together = Keyboard::modifier_state_contains (ev->state, Keyboard::Level4Modifier);
769 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
770 change_velocities (false, fine, allow_smush, together);
772 transpose (false, fine, allow_smush);
776 } else if (ev->keyval == GDK_Left) {
778 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
779 nudge_notes (false, fine);
782 } else if (ev->keyval == GDK_Right) {
784 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
785 nudge_notes (true, fine);
788 } else if (ev->keyval == GDK_c && unmodified) {
792 } else if (ev->keyval == GDK_v && unmodified) {
801 MidiRegionView::key_release (GdkEventKey* ev)
803 if ((_mouse_state == SelectTouchDragging) && (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R)) {
811 MidiRegionView::channel_edit ()
813 if (_selection.empty()) {
817 /* pick a note somewhat at random (since Selection is a set<>) to
818 * provide the "current" channel for the dialog.
821 uint8_t current_channel = (*_selection.begin())->note()->channel ();
822 MidiChannelDialog channel_dialog (current_channel);
823 int ret = channel_dialog.run ();
826 case Gtk::RESPONSE_OK:
832 uint8_t new_channel = channel_dialog.active_channel ();
834 start_note_diff_command (_("channel edit"));
836 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
837 Selection::iterator next = i;
839 change_note_channel (*i, new_channel);
847 MidiRegionView::velocity_edit ()
849 if (_selection.empty()) {
853 /* pick a note somewhat at random (since Selection is a set<>) to
854 * provide the "current" velocity for the dialog.
857 uint8_t current_velocity = (*_selection.begin())->note()->velocity ();
858 MidiVelocityDialog velocity_dialog (current_velocity);
859 int ret = velocity_dialog.run ();
862 case Gtk::RESPONSE_OK:
868 uint8_t new_velocity = velocity_dialog.velocity ();
870 start_note_diff_command (_("velocity edit"));
872 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
873 Selection::iterator next = i;
875 change_note_velocity (*i, new_velocity, false);
883 MidiRegionView::show_list_editor ()
886 _list_editor = new MidiListEditor (trackview.session(), midi_region(), midi_view()->midi_track());
888 _list_editor->present ();
891 /** Add a note to the model, and the view, at a canvas (click) coordinate.
892 * \param t time in frames relative to the position of the region
893 * \param y vertical position in pixels
894 * \param length duration of the note in beats
895 * \param snap_t true to snap t to the grid, otherwise false.
898 MidiRegionView::create_note_at (framepos_t t, double y, Evoral::MusicalTime length, bool snap_t)
900 if (length < 2 * DBL_EPSILON) {
904 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
905 MidiStreamView* const view = mtv->midi_view();
907 const double note = view->y_to_note(y);
909 // Start of note in frames relative to region start
911 framecnt_t grid_frames;
912 t = snap_frame_to_grid_underneath (t, grid_frames);
915 const boost::shared_ptr<NoteType> new_note (
916 new NoteType (mtv->get_channel_for_add (),
917 region_frames_to_region_beats(t + _region->start()),
919 (uint8_t)note, 0x40));
921 if (_model->contains (new_note)) {
925 view->update_note_range(new_note->note());
927 MidiModel::NoteDiffCommand* cmd = _model->new_note_diff_command(_("add note"));
929 _model->apply_command(*trackview.session(), cmd);
931 play_midi_note (new_note);
935 MidiRegionView::clear_events (bool with_selection_signal)
937 clear_selection (with_selection_signal);
940 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
941 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
946 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
951 _patch_changes.clear();
953 _optimization_iterator = _events.end();
957 MidiRegionView::display_model(boost::shared_ptr<MidiModel> model)
961 content_connection.disconnect ();
962 _model->ContentsChanged.connect (content_connection, invalidator (*this), boost::bind (&MidiRegionView::redisplay_model, this), gui_context());
966 if (_enable_display) {
972 MidiRegionView::start_note_diff_command (string name)
974 if (!_note_diff_command) {
975 _note_diff_command = _model->new_note_diff_command (name);
980 MidiRegionView::note_diff_add_note (const boost::shared_ptr<NoteType> note, bool selected, bool show_velocity)
982 if (_note_diff_command) {
983 _note_diff_command->add (note);
986 _marked_for_selection.insert(note);
989 _marked_for_velocity.insert(note);
994 MidiRegionView::note_diff_remove_note (NoteBase* ev)
996 if (_note_diff_command && ev->note()) {
997 _note_diff_command->remove(ev->note());
1002 MidiRegionView::note_diff_add_change (NoteBase* ev,
1003 MidiModel::NoteDiffCommand::Property property,
1006 if (_note_diff_command) {
1007 _note_diff_command->change (ev->note(), property, val);
1012 MidiRegionView::note_diff_add_change (NoteBase* ev,
1013 MidiModel::NoteDiffCommand::Property property,
1014 Evoral::MusicalTime val)
1016 if (_note_diff_command) {
1017 _note_diff_command->change (ev->note(), property, val);
1022 MidiRegionView::apply_diff (bool as_subcommand)
1026 if (!_note_diff_command) {
1030 if ((add_or_remove = _note_diff_command->adds_or_removes())) {
1031 // Mark all selected notes for selection when model reloads
1032 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1033 _marked_for_selection.insert((*i)->note());
1037 if (as_subcommand) {
1038 _model->apply_command_as_subcommand (*trackview.session(), _note_diff_command);
1040 _model->apply_command (*trackview.session(), _note_diff_command);
1043 _note_diff_command = 0;
1044 midi_view()->midi_track()->playlist_modified();
1046 if (add_or_remove) {
1047 _marked_for_selection.clear();
1050 _marked_for_velocity.clear();
1054 MidiRegionView::abort_command()
1056 delete _note_diff_command;
1057 _note_diff_command = 0;
1062 MidiRegionView::find_canvas_note (boost::shared_ptr<NoteType> note)
1064 if (_optimization_iterator != _events.end()) {
1065 ++_optimization_iterator;
1068 if (_optimization_iterator != _events.end() && (*_optimization_iterator)->note() == note) {
1069 return *_optimization_iterator;
1072 for (_optimization_iterator = _events.begin(); _optimization_iterator != _events.end(); ++_optimization_iterator) {
1073 if ((*_optimization_iterator)->note() == note) {
1074 return *_optimization_iterator;
1082 MidiRegionView::get_events (Events& e, Evoral::Sequence<Evoral::MusicalTime>::NoteOperator op, uint8_t val, int chan_mask)
1084 MidiModel::Notes notes;
1085 _model->get_notes (notes, op, val, chan_mask);
1087 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1088 NoteBase* cne = find_canvas_note (*n);
1096 MidiRegionView::redisplay_model()
1098 // Don't redisplay the model if we're currently recording and displaying that
1099 if (_active_notes) {
1107 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1108 (*i)->invalidate ();
1111 MidiModel::ReadLock lock(_model->read_lock());
1113 MidiModel::Notes& notes (_model->notes());
1114 _optimization_iterator = _events.begin();
1116 bool empty_when_starting = _events.empty();
1118 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1120 boost::shared_ptr<NoteType> note (*n);
1124 if (note_in_region_range (note, visible)) {
1126 if (!empty_when_starting && (cne = find_canvas_note (note)) != 0) {
1133 if ((cn = dynamic_cast<Note*>(cne)) != 0) {
1135 } else if ((ch = dynamic_cast<Hit*>(cne)) != 0) {
1147 add_note (note, visible);
1152 if (!empty_when_starting && (cne = find_canvas_note (note)) != 0) {
1160 /* remove note items that are no longer valid */
1162 if (!empty_when_starting) {
1163 for (Events::iterator i = _events.begin(); i != _events.end(); ) {
1164 if (!(*i)->valid ()) {
1166 for (vector<GhostRegion*>::iterator j = ghosts.begin(); j != ghosts.end(); ++j) {
1167 MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*j);
1169 gr->remove_note (*i);
1174 i = _events.erase (i);
1182 _patch_changes.clear();
1186 display_patch_changes ();
1188 _marked_for_selection.clear ();
1189 _marked_for_velocity.clear ();
1191 /* we may have caused _events to contain things out of order (e.g. if a note
1192 moved earlier or later). we don't generally need them in time order, but
1193 make a note that a sort is required for those cases that require it.
1196 _sort_needed = true;
1200 MidiRegionView::display_patch_changes ()
1202 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1203 uint16_t chn_mask = mtv->midi_track()->get_playback_channel_mask();
1205 for (uint8_t i = 0; i < 16; ++i) {
1206 display_patch_changes_on_channel (i, chn_mask & (1 << i));
1210 /** @param active_channel true to display patch changes fully, false to display
1211 * them `greyed-out' (as on an inactive channel)
1214 MidiRegionView::display_patch_changes_on_channel (uint8_t channel, bool active_channel)
1216 for (MidiModel::PatchChanges::const_iterator i = _model->patch_changes().begin(); i != _model->patch_changes().end(); ++i) {
1218 if ((*i)->channel() != channel) {
1222 const string patch_name = instrument_info().get_patch_name ((*i)->bank(), (*i)->program(), channel);
1223 add_canvas_patch_change (*i, patch_name, active_channel);
1228 MidiRegionView::display_sysexes()
1230 bool have_periodic_system_messages = false;
1231 bool display_periodic_messages = true;
1233 if (!ARDOUR_UI::config()->get_never_display_periodic_midi()) {
1235 for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
1236 const boost::shared_ptr<const Evoral::MIDIEvent<Evoral::MusicalTime> > mev =
1237 boost::static_pointer_cast<const Evoral::MIDIEvent<Evoral::MusicalTime> > (*i);
1240 if (mev->is_spp() || mev->is_mtc_quarter() || mev->is_mtc_full()) {
1241 have_periodic_system_messages = true;
1247 if (have_periodic_system_messages) {
1248 double zoom = trackview.editor().get_current_zoom (); // frames per pixel
1250 /* get an approximate value for the number of samples per video frame */
1252 double video_frame = trackview.session()->frame_rate() * (1.0/30);
1254 /* if we are zoomed out beyond than the cutoff (i.e. more
1255 * frames per pixel than frames per 4 video frames), don't
1256 * show periodic sysex messages.
1259 if (zoom > (video_frame*4)) {
1260 display_periodic_messages = false;
1264 display_periodic_messages = false;
1267 for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
1269 const boost::shared_ptr<const Evoral::MIDIEvent<Evoral::MusicalTime> > mev =
1270 boost::static_pointer_cast<const Evoral::MIDIEvent<Evoral::MusicalTime> > (*i);
1272 Evoral::MusicalTime time = (*i)->time();
1275 if (mev->is_spp() || mev->is_mtc_quarter() || mev->is_mtc_full()) {
1276 if (!display_periodic_messages) {
1284 for (uint32_t b = 0; b < (*i)->size(); ++b) {
1285 str << int((*i)->buffer()[b]);
1286 if (b != (*i)->size() -1) {
1290 string text = str.str();
1292 const double x = trackview.editor().sample_to_pixel(source_beats_to_region_frames(time));
1294 double height = midi_stream_view()->contents_height();
1296 // CAIROCANVAS: no longer passing *i (the sysex event) to the
1297 // SysEx canvas object!!!
1299 boost::shared_ptr<SysEx> sysex = boost::shared_ptr<SysEx>(
1300 new SysEx (*this, _note_group, text, height, x, 1.0));
1302 // Show unless message is beyond the region bounds
1303 if (time - _region->start() >= _region->length() || time < _region->start()) {
1309 _sys_exes.push_back(sysex);
1313 MidiRegionView::~MidiRegionView ()
1315 in_destructor = true;
1317 trackview.editor().verbose_cursor()->hide ();
1319 note_delete_connection.disconnect ();
1321 delete _list_editor;
1323 RegionViewGoingAway (this); /* EMIT_SIGNAL */
1325 if (_active_notes) {
1329 _selection_cleared_connection.disconnect ();
1332 clear_events (false);
1335 delete _note_diff_command;
1336 delete _step_edit_cursor;
1337 delete _temporary_note_group;
1341 MidiRegionView::region_resized (const PropertyChange& what_changed)
1343 RegionView::region_resized(what_changed);
1345 if (what_changed.contains (ARDOUR::Properties::position)) {
1346 _region_relative_time_converter.set_origin_b(_region->position());
1347 set_duration(_region->length(), 0);
1348 if (_enable_display) {
1353 if (what_changed.contains (ARDOUR::Properties::start) ||
1354 what_changed.contains (ARDOUR::Properties::position)) {
1355 _source_relative_time_converter.set_origin_b (_region->position() - _region->start());
1360 MidiRegionView::reset_width_dependent_items (double pixel_width)
1362 RegionView::reset_width_dependent_items(pixel_width);
1364 if (_enable_display) {
1368 for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
1369 if ((*x)->canvas_item()->width() >= _pixel_width) {
1376 move_step_edit_cursor (_step_edit_cursor_position);
1377 set_step_edit_cursor_width (_step_edit_cursor_width);
1381 MidiRegionView::set_height (double height)
1383 double old_height = _height;
1384 RegionView::set_height(height);
1386 apply_note_range (midi_stream_view()->lowest_note(),
1387 midi_stream_view()->highest_note(),
1388 height != old_height);
1391 name_text->raise_to_top();
1394 for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
1395 (*x)->set_height (midi_stream_view()->contents_height());
1398 if (_step_edit_cursor) {
1399 _step_edit_cursor->set_y1 (midi_stream_view()->contents_height());
1404 /** Apply the current note range from the stream view
1405 * by repositioning/hiding notes as necessary
1408 MidiRegionView::apply_note_range (uint8_t min, uint8_t max, bool force)
1410 if (!_enable_display) {
1414 if (!force && _current_range_min == min && _current_range_max == max) {
1418 _current_range_min = min;
1419 _current_range_max = max;
1421 for (Events::const_iterator i = _events.begin(); i != _events.end(); ++i) {
1422 NoteBase* event = *i;
1423 boost::shared_ptr<NoteType> note (event->note());
1425 if (note->note() < _current_range_min ||
1426 note->note() > _current_range_max) {
1432 if (Note* cnote = dynamic_cast<Note*>(event)) {
1434 const double y0 = 1. + floor (midi_stream_view()->note_to_y(note->note()));
1435 const double y1 = y0 + std::max(1., floor(midi_stream_view()->note_height()) - 1.);
1440 } else if (Hit* chit = dynamic_cast<Hit*>(event)) {
1447 MidiRegionView::add_ghost (TimeAxisView& tv)
1451 double unit_position = _region->position () / samples_per_pixel;
1452 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(&tv);
1453 MidiGhostRegion* ghost;
1455 if (mtv && mtv->midi_view()) {
1456 /* if ghost is inserted into midi track, use a dedicated midi ghost canvas group
1457 to allow having midi notes on top of note lines and waveforms.
1459 ghost = new MidiGhostRegion (*mtv->midi_view(), trackview, unit_position);
1461 ghost = new MidiGhostRegion (tv, trackview, unit_position);
1464 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1465 if ((note = dynamic_cast<Note*>(*i)) != 0) {
1466 ghost->add_note(note);
1470 ghost->set_height ();
1471 ghost->set_duration (_region->length() / samples_per_pixel);
1472 ghosts.push_back (ghost);
1474 GhostRegion::CatchDeletion.connect (*this, invalidator (*this), boost::bind (&RegionView::remove_ghost, this, _1), gui_context());
1480 /** Begin tracking note state for successive calls to add_event
1483 MidiRegionView::begin_write()
1485 if (_active_notes) {
1486 delete[] _active_notes;
1488 _active_notes = new Note*[128];
1489 for (unsigned i = 0; i < 128; ++i) {
1490 _active_notes[i] = 0;
1495 /** Destroy note state for add_event
1498 MidiRegionView::end_write()
1500 delete[] _active_notes;
1502 _marked_for_selection.clear();
1503 _marked_for_velocity.clear();
1507 /** Resolve an active MIDI note (while recording).
1510 MidiRegionView::resolve_note(uint8_t note, Evoral::MusicalTime end_time)
1512 if (midi_view()->note_mode() != Sustained) {
1516 if (_active_notes && _active_notes[note]) {
1518 /* XXX is end_time really region-centric? I think so, because
1519 this is a new region that we're recording, so source zero is
1520 the same as region zero
1522 const framepos_t end_time_frames = region_beats_to_region_frames(end_time);
1524 _active_notes[note]->set_x1 (trackview.editor().sample_to_pixel(end_time_frames));
1525 _active_notes[note]->set_outline_all ();
1526 _active_notes[note] = 0;
1532 /** Extend active notes to rightmost edge of region (if length is changed)
1535 MidiRegionView::extend_active_notes()
1537 if (!_active_notes) {
1541 for (unsigned i=0; i < 128; ++i) {
1542 if (_active_notes[i]) {
1543 _active_notes[i]->set_x1(
1544 trackview.editor().sample_to_pixel(_region->position() + _region->length()));
1551 MidiRegionView::play_midi_note(boost::shared_ptr<NoteType> note)
1553 if (_no_sound_notes || !ARDOUR_UI::config()->get_sound_midi_notes()) {
1557 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1559 if (!route_ui || !route_ui->midi_track()) {
1563 NotePlayer* np = new NotePlayer (route_ui->midi_track ());
1567 /* NotePlayer deletes itself */
1571 MidiRegionView::start_playing_midi_note(boost::shared_ptr<NoteType> note)
1573 const std::vector< boost::shared_ptr<NoteType> > notes(1, note);
1574 start_playing_midi_chord(notes);
1578 MidiRegionView::start_playing_midi_chord (vector<boost::shared_ptr<NoteType> > notes)
1580 if (_no_sound_notes || !ARDOUR_UI::config()->get_sound_midi_notes()) {
1584 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1586 if (!route_ui || !route_ui->midi_track()) {
1590 _note_player = boost::shared_ptr<NotePlayer>(new NotePlayer(route_ui->midi_track()));
1592 for (vector<boost::shared_ptr<NoteType> >::iterator n = notes.begin(); n != notes.end(); ++n) {
1593 _note_player->add (*n);
1596 _note_player->on ();
1601 MidiRegionView::note_in_region_range (const boost::shared_ptr<NoteType> note, bool& visible) const
1603 const framepos_t note_start_frames = source_beats_to_region_frames (note->time());
1604 bool outside = (note_start_frames < 0) || (note_start_frames > _region->last_frame());
1606 visible = (note->note() >= midi_stream_view()->lowest_note()) &&
1607 (note->note() <= midi_stream_view()->highest_note());
1612 /** Update a canvas note's size from its model note.
1613 * @param ev Canvas note to update.
1614 * @param update_ghost_regions true to update the note in any ghost regions that we have, otherwise false.
1617 MidiRegionView::update_note (Note* ev, bool update_ghost_regions)
1619 boost::shared_ptr<NoteType> note = ev->note();
1620 const double x = trackview.editor().sample_to_pixel (source_beats_to_region_frames (note->time()));
1621 const double y0 = 1 + floor(midi_stream_view()->note_to_y(note->note()));
1626 /* trim note display to not overlap the end of its region */
1628 if (note->length() > 0) {
1629 const framepos_t note_end_frames = min (source_beats_to_region_frames (note->end_time()), _region->length());
1630 ev->set_x1 (trackview.editor().sample_to_pixel (note_end_frames));
1632 ev->set_x1 (trackview.editor().sample_to_pixel (_region->length()));
1635 ev->set_y1 (y0 + std::max(1., floor(midi_stream_view()->note_height()) - 1));
1637 if (!note->length()) {
1638 if (_active_notes && note->note() < 128) {
1639 // If this note is already active there's a stuck note,
1640 // finish the old note rectangle
1641 if (_active_notes[note->note()]) {
1642 Note* const old_rect = _active_notes[note->note()];
1643 boost::shared_ptr<NoteType> old_note = old_rect->note();
1644 old_rect->set_x1 (x);
1645 old_rect->set_outline_all ();
1647 _active_notes[note->note()] = ev;
1649 /* outline all but right edge */
1650 ev->set_outline_what (ArdourCanvas::Rectangle::What (
1651 ArdourCanvas::Rectangle::TOP|
1652 ArdourCanvas::Rectangle::LEFT|
1653 ArdourCanvas::Rectangle::BOTTOM));
1655 /* outline all edges */
1656 ev->set_outline_all ();
1659 // Update color in case velocity has changed
1660 ev->set_fill_color(ev->base_color());
1661 ev->set_outline_color(ev->calculate_outline(ev->base_color(), ev->selected()));
1663 if (update_ghost_regions) {
1664 for (std::vector<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
1665 MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*i);
1667 gr->update_note (ev);
1674 MidiRegionView::update_hit (Hit* ev)
1676 boost::shared_ptr<NoteType> note = ev->note();
1678 const framepos_t note_start_frames = source_beats_to_region_frames(note->time());
1679 const double x = trackview.editor().sample_to_pixel(note_start_frames);
1680 const double diamond_size = std::max(1., floor(midi_stream_view()->note_height()) - 2.);
1681 const double y = 1.5 + floor(midi_stream_view()->note_to_y(note->note())) + diamond_size * .5;
1683 ev->set_position (ArdourCanvas::Duple (x, y));
1684 ev->set_height (diamond_size);
1687 /** Add a MIDI note to the view (with length).
1689 * If in sustained mode, notes with length 0 will be considered active
1690 * notes, and resolve_note should be called when the corresponding note off
1691 * event arrives, to properly display the note.
1694 MidiRegionView::add_note(const boost::shared_ptr<NoteType> note, bool visible)
1696 NoteBase* event = 0;
1698 if (midi_view()->note_mode() == Sustained) {
1700 Note* ev_rect = new Note (*this, _note_group, note);
1702 update_note (ev_rect);
1706 MidiGhostRegion* gr;
1708 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
1709 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
1710 gr->add_note(ev_rect);
1714 } else if (midi_view()->note_mode() == Percussive) {
1716 const double diamond_size = std::max(1., floor(midi_stream_view()->note_height()) - 2.);
1718 Hit* ev_diamond = new Hit (*this, _note_group, diamond_size, note);
1720 update_hit (ev_diamond);
1729 if (_marked_for_selection.find(note) != _marked_for_selection.end()) {
1730 note_selected(event, true);
1733 if (_marked_for_velocity.find(note) != _marked_for_velocity.end()) {
1734 event->show_velocity();
1737 event->on_channel_selection_change (get_selected_channels());
1738 _events.push_back(event);
1747 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1748 MidiStreamView* const view = mtv->midi_view();
1750 view->update_note_range (note->note());
1754 MidiRegionView::step_add_note (uint8_t channel, uint8_t number, uint8_t velocity,
1755 Evoral::MusicalTime pos, Evoral::MusicalTime len)
1757 boost::shared_ptr<NoteType> new_note (new NoteType (channel, pos, len, number, velocity));
1759 /* potentially extend region to hold new note */
1761 framepos_t end_frame = source_beats_to_absolute_frames (new_note->end_time());
1762 framepos_t region_end = _region->last_frame();
1764 if (end_frame > region_end) {
1765 _region->set_length (end_frame - _region->position());
1768 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1769 MidiStreamView* const view = mtv->midi_view();
1771 view->update_note_range(new_note->note());
1773 _marked_for_selection.clear ();
1776 start_note_diff_command (_("step add"));
1777 note_diff_add_note (new_note, true, false);
1780 // last_step_edit_note = new_note;
1784 MidiRegionView::step_sustain (Evoral::MusicalTime beats)
1786 change_note_lengths (false, false, beats, false, true);
1789 /** Add a new patch change flag to the canvas.
1790 * @param patch the patch change to add
1791 * @param the text to display in the flag
1792 * @param active_channel true to display the flag as on an active channel, false to grey it out for an inactive channel.
1795 MidiRegionView::add_canvas_patch_change (MidiModel::PatchChangePtr patch, const string& displaytext, bool /*active_channel*/)
1797 framecnt_t region_frames = source_beats_to_region_frames (patch->time());
1798 const double x = trackview.editor().sample_to_pixel (region_frames);
1800 double const height = midi_stream_view()->contents_height();
1802 // CAIROCANVAS: active_channel info removed from PatcChange constructor
1803 // so we need to do something more sophisticated to keep its color
1804 // appearance (MidiPatchChangeFill/MidiPatchChangeInactiveChannelFill)
1807 boost::shared_ptr<PatchChange> patch_change = boost::shared_ptr<PatchChange>(
1808 new PatchChange(*this, group,
1815 if (patch_change->item().width() < _pixel_width) {
1816 // Show unless patch change is beyond the region bounds
1817 if (region_frames < 0 || region_frames >= _region->length()) {
1818 patch_change->hide();
1820 patch_change->show();
1823 patch_change->hide ();
1826 _patch_changes.push_back (patch_change);
1829 MIDI::Name::PatchPrimaryKey
1830 MidiRegionView::patch_change_to_patch_key (MidiModel::PatchChangePtr p)
1832 return MIDI::Name::PatchPrimaryKey (p->program(), p->bank());
1835 /// Return true iff @p pc applies to the given time on the given channel.
1837 patch_applies (const ARDOUR::MidiModel::constPatchChangePtr pc, Evoral::MusicalTime time, uint8_t channel)
1839 return pc->time() <= time && pc->channel() == channel;
1843 MidiRegionView::get_patch_key_at (Evoral::MusicalTime time, uint8_t channel, MIDI::Name::PatchPrimaryKey& key) const
1845 // The earliest event not before time
1846 MidiModel::PatchChanges::iterator i = _model->patch_change_lower_bound (time);
1848 // Go backwards until we find the latest PC for this channel, or the start
1849 while (i != _model->patch_changes().begin() &&
1850 (i == _model->patch_changes().end() ||
1851 !patch_applies(*i, time, channel))) {
1855 if (i != _model->patch_changes().end() && patch_applies(*i, time, channel)) {
1856 key.set_bank((*i)->bank());
1857 key.set_program((*i)->program ());
1865 MidiRegionView::change_patch_change (PatchChange& pc, const MIDI::Name::PatchPrimaryKey& new_patch)
1867 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("alter patch change"));
1869 if (pc.patch()->program() != new_patch.program()) {
1870 c->change_program (pc.patch (), new_patch.program());
1873 int const new_bank = new_patch.bank();
1874 if (pc.patch()->bank() != new_bank) {
1875 c->change_bank (pc.patch (), new_bank);
1878 _model->apply_command (*trackview.session(), c);
1880 _patch_changes.clear ();
1881 display_patch_changes ();
1885 MidiRegionView::change_patch_change (MidiModel::PatchChangePtr old_change, const Evoral::PatchChange<Evoral::MusicalTime> & new_change)
1887 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("alter patch change"));
1889 if (old_change->time() != new_change.time()) {
1890 c->change_time (old_change, new_change.time());
1893 if (old_change->channel() != new_change.channel()) {
1894 c->change_channel (old_change, new_change.channel());
1897 if (old_change->program() != new_change.program()) {
1898 c->change_program (old_change, new_change.program());
1901 if (old_change->bank() != new_change.bank()) {
1902 c->change_bank (old_change, new_change.bank());
1905 _model->apply_command (*trackview.session(), c);
1907 _patch_changes.clear ();
1908 display_patch_changes ();
1911 /** Add a patch change to the region.
1912 * @param t Time in frames relative to region position
1913 * @param patch Patch to add; time and channel are ignored (time is converted from t, and channel comes from
1914 * MidiTimeAxisView::get_channel_for_add())
1917 MidiRegionView::add_patch_change (framecnt_t t, Evoral::PatchChange<Evoral::MusicalTime> const & patch)
1919 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1921 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("add patch change"));
1922 c->add (MidiModel::PatchChangePtr (
1923 new Evoral::PatchChange<Evoral::MusicalTime> (
1924 absolute_frames_to_source_beats (_region->position() + t),
1925 mtv->get_channel_for_add(), patch.program(), patch.bank()
1930 _model->apply_command (*trackview.session(), c);
1932 _patch_changes.clear ();
1933 display_patch_changes ();
1937 MidiRegionView::move_patch_change (PatchChange& pc, Evoral::MusicalTime t)
1939 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("move patch change"));
1940 c->change_time (pc.patch (), t);
1941 _model->apply_command (*trackview.session(), c);
1943 _patch_changes.clear ();
1944 display_patch_changes ();
1948 MidiRegionView::delete_patch_change (PatchChange* pc)
1950 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("delete patch change"));
1951 c->remove (pc->patch ());
1952 _model->apply_command (*trackview.session(), c);
1954 _patch_changes.clear ();
1955 display_patch_changes ();
1959 MidiRegionView::step_patch (PatchChange& patch, bool bank, int delta)
1961 MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key(patch.patch());
1963 key.set_bank(key.bank() + delta);
1965 key.set_program(key.program() + delta);
1967 change_patch_change(patch, key);
1971 MidiRegionView::maybe_remove_deleted_note_from_selection (NoteBase* cne)
1973 if (_selection.empty()) {
1977 _selection.erase (cne);
1981 MidiRegionView::delete_selection()
1983 if (_selection.empty()) {
1987 start_note_diff_command (_("delete selection"));
1989 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1990 if ((*i)->selected()) {
1991 _note_diff_command->remove((*i)->note());
2001 MidiRegionView::delete_note (boost::shared_ptr<NoteType> n)
2003 start_note_diff_command (_("delete note"));
2004 _note_diff_command->remove (n);
2007 trackview.editor().verbose_cursor()->hide ();
2011 MidiRegionView::clear_selection_except (NoteBase* ev, bool signal)
2013 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2015 Selection::iterator tmp = i;
2018 (*i)->set_selected (false);
2019 (*i)->hide_velocity ();
2020 _selection.erase (i);
2028 if (!ev && _entered) {
2029 // Clearing selection entirely, ungrab keyboard
2030 Keyboard::magic_widget_drop_focus();
2031 _grabbed_keyboard = false;
2034 /* this does not change the status of this regionview w.r.t the editor
2039 SelectionCleared (this); /* EMIT SIGNAL */
2044 MidiRegionView::unique_select(NoteBase* ev)
2046 const bool selection_was_empty = _selection.empty();
2048 clear_selection_except (ev);
2050 /* don't bother with checking to see if we should remove this
2051 regionview from the editor selection, since we're about to add
2052 another note, and thus put/keep this regionview in the editor
2056 if (!ev->selected()) {
2057 add_to_selection (ev);
2058 if (selection_was_empty && _entered) {
2059 // Grab keyboard for moving notes with arrow keys
2060 Keyboard::magic_widget_grab_focus();
2061 _grabbed_keyboard = true;
2067 MidiRegionView::select_all_notes ()
2071 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2072 add_to_selection (*i);
2077 MidiRegionView::select_range (framepos_t start, framepos_t end)
2081 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2082 framepos_t t = source_beats_to_absolute_frames((*i)->note()->time());
2083 if (t >= start && t <= end) {
2084 add_to_selection (*i);
2090 MidiRegionView::invert_selection ()
2092 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2093 if ((*i)->selected()) {
2094 remove_from_selection(*i);
2096 add_to_selection (*i);
2102 MidiRegionView::select_matching_notes (uint8_t notenum, uint16_t channel_mask, bool add, bool extend)
2104 bool have_selection = !_selection.empty();
2105 uint8_t low_note = 127;
2106 uint8_t high_note = 0;
2107 MidiModel::Notes& notes (_model->notes());
2108 _optimization_iterator = _events.begin();
2110 if (extend && !have_selection) {
2114 /* scan existing selection to get note range */
2116 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2117 if ((*i)->note()->note() < low_note) {
2118 low_note = (*i)->note()->note();
2120 if ((*i)->note()->note() > high_note) {
2121 high_note = (*i)->note()->note();
2128 if (!extend && (low_note == high_note) && (high_note == notenum)) {
2129 /* only note previously selected is the one we are
2130 * reselecting. treat this as cancelling the selection.
2137 low_note = min (low_note, notenum);
2138 high_note = max (high_note, notenum);
2141 _no_sound_notes = true;
2143 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
2145 boost::shared_ptr<NoteType> note (*n);
2147 bool select = false;
2149 if (((1 << note->channel()) & channel_mask) != 0) {
2151 if ((note->note() >= low_note && note->note() <= high_note)) {
2154 } else if (note->note() == notenum) {
2160 if ((cne = find_canvas_note (note)) != 0) {
2161 // extend is false because we've taken care of it,
2162 // since it extends by time range, not pitch.
2163 note_selected (cne, add, false);
2167 add = true; // we need to add all remaining matching notes, even if the passed in value was false (for "set")
2171 _no_sound_notes = false;
2175 MidiRegionView::toggle_matching_notes (uint8_t notenum, uint16_t channel_mask)
2177 MidiModel::Notes& notes (_model->notes());
2178 _optimization_iterator = _events.begin();
2180 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
2182 boost::shared_ptr<NoteType> note (*n);
2185 if (note->note() == notenum && (((0x0001 << note->channel()) & channel_mask) != 0)) {
2186 if ((cne = find_canvas_note (note)) != 0) {
2187 if (cne->selected()) {
2188 note_deselected (cne);
2190 note_selected (cne, true, false);
2198 MidiRegionView::note_selected (NoteBase* ev, bool add, bool extend)
2201 clear_selection_except (ev);
2202 if (!_selection.empty()) {
2203 PublicEditor& editor (trackview.editor());
2204 editor.get_selection().add (this);
2210 if (!ev->selected()) {
2211 add_to_selection (ev);
2215 /* find end of latest note selected, select all between that and the start of "ev" */
2217 Evoral::MusicalTime earliest = Evoral::MaxMusicalTime;
2218 Evoral::MusicalTime latest = Evoral::MusicalTime();
2220 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2221 if ((*i)->note()->end_time() > latest) {
2222 latest = (*i)->note()->end_time();
2224 if ((*i)->note()->time() < earliest) {
2225 earliest = (*i)->note()->time();
2229 if (ev->note()->end_time() > latest) {
2230 latest = ev->note()->end_time();
2233 if (ev->note()->time() < earliest) {
2234 earliest = ev->note()->time();
2237 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2239 /* find notes entirely within OR spanning the earliest..latest range */
2241 if (((*i)->note()->time() >= earliest && (*i)->note()->end_time() <= latest) ||
2242 ((*i)->note()->time() <= earliest && (*i)->note()->end_time() >= latest)) {
2243 add_to_selection (*i);
2251 MidiRegionView::note_deselected(NoteBase* ev)
2253 remove_from_selection (ev);
2257 MidiRegionView::update_drag_selection(framepos_t start, framepos_t end, double gy0, double gy1, bool extend)
2259 PublicEditor& editor = trackview.editor();
2261 // Convert to local coordinates
2262 const framepos_t p = _region->position();
2263 const double y = midi_view()->y_position();
2264 const double x0 = editor.sample_to_pixel(max((framepos_t)0, start - p));
2265 const double x1 = editor.sample_to_pixel(max((framepos_t)0, end - p));
2266 const double y0 = max(0.0, gy0 - y);
2267 const double y1 = max(0.0, gy1 - y);
2269 // TODO: Make this faster by storing the last updated selection rect, and only
2270 // adjusting things that are in the area that appears/disappeared.
2271 // We probably need a tree to be able to find events in O(log(n)) time.
2273 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2274 if ((*i)->x0() < x1 && (*i)->x1() > x0 && (*i)->y0() < y1 && (*i)->y1() > y0) {
2275 // Rectangles intersect
2276 if (!(*i)->selected()) {
2277 add_to_selection (*i);
2279 } else if ((*i)->selected() && !extend) {
2280 // Rectangles do not intersect
2281 remove_from_selection (*i);
2285 typedef RouteTimeAxisView::AutomationTracks ATracks;
2286 typedef std::list<Selectable*> Selectables;
2288 /* Add control points to selection. */
2289 const ATracks& atracks = midi_view()->automation_tracks();
2290 Selectables selectables;
2291 editor.get_selection().clear_points();
2292 for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) {
2293 a->second->get_selectables(start, end, gy0, gy1, selectables);
2294 for (Selectables::const_iterator s = selectables.begin(); s != selectables.end(); ++s) {
2295 ControlPoint* cp = dynamic_cast<ControlPoint*>(*s);
2297 editor.get_selection().add(cp);
2300 a->second->set_selected_points(editor.get_selection().points);
2305 MidiRegionView::update_vertical_drag_selection (double y1, double y2, bool extend)
2311 // TODO: Make this faster by storing the last updated selection rect, and only
2312 // adjusting things that are in the area that appears/disappeared.
2313 // We probably need a tree to be able to find events in O(log(n)) time.
2315 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2316 if (((*i)->y1() >= y1 && (*i)->y1() <= y2)) {
2317 // within y- (note-) range
2318 if (!(*i)->selected()) {
2319 add_to_selection (*i);
2321 } else if ((*i)->selected() && !extend) {
2322 remove_from_selection (*i);
2328 MidiRegionView::remove_from_selection (NoteBase* ev)
2330 Selection::iterator i = _selection.find (ev);
2332 if (i != _selection.end()) {
2333 _selection.erase (i);
2334 if (_selection.empty() && _grabbed_keyboard) {
2336 Keyboard::magic_widget_drop_focus();
2337 _grabbed_keyboard = false;
2341 ev->set_selected (false);
2342 ev->hide_velocity ();
2344 if (_selection.empty()) {
2345 PublicEditor& editor (trackview.editor());
2346 editor.get_selection().remove (this);
2351 MidiRegionView::add_to_selection (NoteBase* ev)
2353 const bool selection_was_empty = _selection.empty();
2355 if (_selection.insert (ev).second) {
2356 ev->set_selected (true);
2357 start_playing_midi_note ((ev)->note());
2358 if (selection_was_empty && _entered) {
2359 // Grab keyboard for moving notes with arrow keys
2360 Keyboard::magic_widget_grab_focus();
2361 _grabbed_keyboard = true;
2365 if (selection_was_empty) {
2366 PublicEditor& editor (trackview.editor());
2367 editor.get_selection().add (this);
2372 MidiRegionView::move_selection(double dx, double dy, double cumulative_dy)
2374 typedef vector<boost::shared_ptr<NoteType> > PossibleChord;
2375 PossibleChord to_play;
2376 Evoral::MusicalTime earliest = Evoral::MaxMusicalTime;
2378 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2379 if ((*i)->note()->time() < earliest) {
2380 earliest = (*i)->note()->time();
2384 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2385 if ((*i)->note()->time() == earliest) {
2386 to_play.push_back ((*i)->note());
2388 (*i)->move_event(dx, dy);
2391 if (dy && !_selection.empty() && !_no_sound_notes && ARDOUR_UI::config()->get_sound_midi_notes()) {
2393 if (to_play.size() > 1) {
2395 PossibleChord shifted;
2397 for (PossibleChord::iterator n = to_play.begin(); n != to_play.end(); ++n) {
2398 boost::shared_ptr<NoteType> moved_note (new NoteType (**n));
2399 moved_note->set_note (moved_note->note() + cumulative_dy);
2400 shifted.push_back (moved_note);
2403 start_playing_midi_chord (shifted);
2405 } else if (!to_play.empty()) {
2407 boost::shared_ptr<NoteType> moved_note (new NoteType (*to_play.front()));
2408 moved_note->set_note (moved_note->note() + cumulative_dy);
2409 start_playing_midi_note (moved_note);
2415 MidiRegionView::note_dropped(NoteBase *, frameoffset_t dt, int8_t dnote)
2417 uint8_t lowest_note_in_selection = 127;
2418 uint8_t highest_note_in_selection = 0;
2419 uint8_t highest_note_difference = 0;
2421 // find highest and lowest notes first
2423 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2424 uint8_t pitch = (*i)->note()->note();
2425 lowest_note_in_selection = std::min(lowest_note_in_selection, pitch);
2426 highest_note_in_selection = std::max(highest_note_in_selection, pitch);
2430 cerr << "dnote: " << (int) dnote << endl;
2431 cerr << "lowest note (streamview): " << int(midi_stream_view()->lowest_note())
2432 << " highest note (streamview): " << int(midi_stream_view()->highest_note()) << endl;
2433 cerr << "lowest note (selection): " << int(lowest_note_in_selection) << " highest note(selection): "
2434 << int(highest_note_in_selection) << endl;
2435 cerr << "selection size: " << _selection.size() << endl;
2436 cerr << "Highest note in selection: " << (int) highest_note_in_selection << endl;
2439 // Make sure the note pitch does not exceed the MIDI standard range
2440 if (highest_note_in_selection + dnote > 127) {
2441 highest_note_difference = highest_note_in_selection - 127;
2444 start_note_diff_command (_("move notes"));
2446 for (Selection::iterator i = _selection.begin(); i != _selection.end() ; ++i) {
2448 framepos_t new_frames = source_beats_to_absolute_frames ((*i)->note()->time()) + dt;
2449 Evoral::MusicalTime new_time = absolute_frames_to_source_beats (new_frames);
2455 note_diff_add_change (*i, MidiModel::NoteDiffCommand::StartTime, new_time);
2457 uint8_t original_pitch = (*i)->note()->note();
2458 uint8_t new_pitch = original_pitch + dnote - highest_note_difference;
2460 // keep notes in standard midi range
2461 clamp_to_0_127(new_pitch);
2463 lowest_note_in_selection = std::min(lowest_note_in_selection, new_pitch);
2464 highest_note_in_selection = std::max(highest_note_in_selection, new_pitch);
2466 note_diff_add_change (*i, MidiModel::NoteDiffCommand::NoteNumber, new_pitch);
2471 // care about notes being moved beyond the upper/lower bounds on the canvas
2472 if (lowest_note_in_selection < midi_stream_view()->lowest_note() ||
2473 highest_note_in_selection > midi_stream_view()->highest_note()) {
2474 midi_stream_view()->set_note_range(MidiStreamView::ContentsRange);
2478 /** @param x Pixel relative to the region position.
2479 * @return Snapped frame relative to the region position.
2482 MidiRegionView::snap_pixel_to_sample(double x)
2484 PublicEditor& editor (trackview.editor());
2485 return snap_frame_to_frame (editor.pixel_to_sample (x));
2488 /** @param x Pixel relative to the region position.
2489 * @return Snapped pixel relative to the region position.
2492 MidiRegionView::snap_to_pixel(double x)
2494 return (double) trackview.editor().sample_to_pixel(snap_pixel_to_sample(x));
2498 MidiRegionView::get_position_pixels()
2500 framepos_t region_frame = get_position();
2501 return trackview.editor().sample_to_pixel(region_frame);
2505 MidiRegionView::get_end_position_pixels()
2507 framepos_t frame = get_position() + get_duration ();
2508 return trackview.editor().sample_to_pixel(frame);
2512 MidiRegionView::source_beats_to_absolute_frames(Evoral::MusicalTime beats) const
2514 /* the time converter will return the frame corresponding to `beats'
2515 relative to the start of the source. The start of the source
2516 is an implied position given by region->position - region->start
2518 const framepos_t source_start = _region->position() - _region->start();
2519 return source_start + _source_relative_time_converter.to (beats);
2523 MidiRegionView::absolute_frames_to_source_beats(framepos_t frames) const
2525 /* the `frames' argument needs to be converted into a frame count
2526 relative to the start of the source before being passed in to the
2529 const framepos_t source_start = _region->position() - _region->start();
2530 return _source_relative_time_converter.from (frames - source_start);
2534 MidiRegionView::region_beats_to_region_frames(Evoral::MusicalTime beats) const
2536 return _region_relative_time_converter.to(beats);
2540 MidiRegionView::region_frames_to_region_beats(framepos_t frames) const
2542 return _region_relative_time_converter.from(frames);
2546 MidiRegionView::begin_resizing (bool /*at_front*/)
2548 _resize_data.clear();
2550 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2551 Note *note = dynamic_cast<Note*> (*i);
2553 // only insert CanvasNotes into the map
2555 NoteResizeData *resize_data = new NoteResizeData();
2556 resize_data->note = note;
2558 // create a new SimpleRect from the note which will be the resize preview
2559 ArdourCanvas::Rectangle *resize_rect = new ArdourCanvas::Rectangle (_note_group,
2560 ArdourCanvas::Rect (note->x0(), note->y0(), note->x0(), note->y1()));
2562 // calculate the colors: get the color settings
2563 uint32_t fill_color = UINT_RGBA_CHANGE_A(
2564 ARDOUR_UI::config()->color ("midi note selected"),
2567 // make the resize preview notes more transparent and bright
2568 fill_color = UINT_INTERPOLATE(fill_color, 0xFFFFFF40, 0.5);
2570 // calculate color based on note velocity
2571 resize_rect->set_fill_color (UINT_INTERPOLATE(
2572 NoteBase::meter_style_fill_color(note->note()->velocity(), note->selected()),
2576 resize_rect->set_outline_color (NoteBase::calculate_outline (
2577 ARDOUR_UI::config()->color ("midi note selected")));
2579 resize_data->resize_rect = resize_rect;
2580 _resize_data.push_back(resize_data);
2585 /** Update resizing notes while user drags.
2586 * @param primary `primary' note for the drag; ie the one that is used as the reference in non-relative mode.
2587 * @param at_front which end of the note (true == note on, false == note off)
2588 * @param delta_x change in mouse position since the start of the drag
2589 * @param relative true if relative resizing is taking place, false if absolute resizing. This only makes
2590 * a difference when multiple notes are being resized; in relative mode, each note's length is changed by the
2591 * amount of the drag. In non-relative mode, all selected notes are set to have the same start or end point
2592 * as the \a primary note.
2595 MidiRegionView::update_resizing (NoteBase* primary, bool at_front, double delta_x, bool relative)
2597 bool cursor_set = false;
2599 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2600 ArdourCanvas::Rectangle* resize_rect = (*i)->resize_rect;
2601 Note* canvas_note = (*i)->note;
2606 current_x = canvas_note->x0() + delta_x;
2608 current_x = primary->x0() + delta_x;
2612 current_x = canvas_note->x1() + delta_x;
2614 current_x = primary->x1() + delta_x;
2618 if (current_x < 0) {
2619 // This works even with snapping because RegionView::snap_frame_to_frame()
2620 // snaps forward if the snapped sample is before the beginning of the region
2623 if (current_x > trackview.editor().sample_to_pixel(_region->length())) {
2624 current_x = trackview.editor().sample_to_pixel(_region->length());
2628 resize_rect->set_x0 (snap_to_pixel(current_x));
2629 resize_rect->set_x1 (canvas_note->x1());
2631 resize_rect->set_x1 (snap_to_pixel(current_x));
2632 resize_rect->set_x0 (canvas_note->x0());
2636 const double snapped_x = snap_pixel_to_sample (current_x);
2637 Evoral::MusicalTime beats = region_frames_to_region_beats (snapped_x);
2638 Evoral::MusicalTime len = Evoral::MusicalTime();
2641 if (beats < canvas_note->note()->end_time()) {
2642 len = canvas_note->note()->time() - beats;
2643 len += canvas_note->note()->length();
2646 if (beats >= canvas_note->note()->time()) {
2647 len = beats - canvas_note->note()->time();
2652 snprintf (buf, sizeof (buf), "%.3g beats", len.to_double());
2653 show_verbose_cursor (buf, 0, 0);
2662 /** Finish resizing notes when the user releases the mouse button.
2663 * Parameters the same as for \a update_resizing().
2666 MidiRegionView::commit_resizing (NoteBase* primary, bool at_front, double delta_x, bool relative)
2668 start_note_diff_command (_("resize notes"));
2670 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2671 Note* canvas_note = (*i)->note;
2672 ArdourCanvas::Rectangle* resize_rect = (*i)->resize_rect;
2674 /* Get the new x position for this resize, which is in pixels relative
2675 * to the region position.
2682 current_x = canvas_note->x0() + delta_x;
2684 current_x = primary->x0() + delta_x;
2688 current_x = canvas_note->x1() + delta_x;
2690 current_x = primary->x1() + delta_x;
2694 if (current_x < 0) {
2697 if (current_x > trackview.editor().sample_to_pixel(_region->length())) {
2698 current_x = trackview.editor().sample_to_pixel(_region->length());
2701 /* Convert that to a frame within the source */
2702 current_x = snap_pixel_to_sample (current_x) + _region->start ();
2704 /* and then to beats */
2705 const Evoral::MusicalTime x_beats = region_frames_to_region_beats (current_x);
2707 if (at_front && x_beats < canvas_note->note()->end_time()) {
2708 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::StartTime, x_beats);
2710 Evoral::MusicalTime len = canvas_note->note()->time() - x_beats;
2711 len += canvas_note->note()->length();
2714 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
2719 const Evoral::MusicalTime len = x_beats - canvas_note->note()->time();
2722 /* XXX convert to beats */
2723 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
2731 _resize_data.clear();
2736 MidiRegionView::abort_resizing ()
2738 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2739 delete (*i)->resize_rect;
2743 _resize_data.clear ();
2747 MidiRegionView::change_note_velocity(NoteBase* event, int8_t velocity, bool relative)
2749 uint8_t new_velocity;
2752 new_velocity = event->note()->velocity() + velocity;
2753 clamp_to_0_127(new_velocity);
2755 new_velocity = velocity;
2758 event->set_selected (event->selected()); // change color
2760 note_diff_add_change (event, MidiModel::NoteDiffCommand::Velocity, new_velocity);
2764 MidiRegionView::change_note_note (NoteBase* event, int8_t note, bool relative)
2769 new_note = event->note()->note() + note;
2774 clamp_to_0_127 (new_note);
2775 note_diff_add_change (event, MidiModel::NoteDiffCommand::NoteNumber, new_note);
2779 MidiRegionView::trim_note (NoteBase* event, Evoral::MusicalTime front_delta, Evoral::MusicalTime end_delta)
2781 bool change_start = false;
2782 bool change_length = false;
2783 Evoral::MusicalTime new_start;
2784 Evoral::MusicalTime new_length;
2786 /* NOTE: the semantics of the two delta arguments are slightly subtle:
2788 front_delta: if positive - move the start of the note later in time (shortening it)
2789 if negative - move the start of the note earlier in time (lengthening it)
2791 end_delta: if positive - move the end of the note later in time (lengthening it)
2792 if negative - move the end of the note earlier in time (shortening it)
2795 if (!!front_delta) {
2796 if (front_delta < 0) {
2798 if (event->note()->time() < -front_delta) {
2799 new_start = Evoral::MusicalTime();
2801 new_start = event->note()->time() + front_delta; // moves earlier
2804 /* start moved toward zero, so move the end point out to where it used to be.
2805 Note that front_delta is negative, so this increases the length.
2808 new_length = event->note()->length() - front_delta;
2809 change_start = true;
2810 change_length = true;
2814 Evoral::MusicalTime new_pos = event->note()->time() + front_delta;
2816 if (new_pos < event->note()->end_time()) {
2817 new_start = event->note()->time() + front_delta;
2818 /* start moved toward the end, so move the end point back to where it used to be */
2819 new_length = event->note()->length() - front_delta;
2820 change_start = true;
2821 change_length = true;
2828 bool can_change = true;
2829 if (end_delta < 0) {
2830 if (event->note()->length() < -end_delta) {
2836 new_length = event->note()->length() + end_delta;
2837 change_length = true;
2842 note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_start);
2845 if (change_length) {
2846 note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, new_length);
2851 MidiRegionView::change_note_channel (NoteBase* event, int8_t chn, bool relative)
2853 uint8_t new_channel;
2857 if (event->note()->channel() < -chn) {
2860 new_channel = event->note()->channel() + chn;
2863 new_channel = event->note()->channel() + chn;
2866 new_channel = (uint8_t) chn;
2869 note_diff_add_change (event, MidiModel::NoteDiffCommand::Channel, new_channel);
2873 MidiRegionView::change_note_time (NoteBase* event, Evoral::MusicalTime delta, bool relative)
2875 Evoral::MusicalTime new_time;
2879 if (event->note()->time() < -delta) {
2880 new_time = Evoral::MusicalTime();
2882 new_time = event->note()->time() + delta;
2885 new_time = event->note()->time() + delta;
2891 note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_time);
2895 MidiRegionView::change_note_length (NoteBase* event, Evoral::MusicalTime t)
2897 note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, t);
2901 MidiRegionView::change_velocities (bool up, bool fine, bool allow_smush, bool all_together)
2906 if (_selection.empty()) {
2921 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2922 if ((*i)->note()->velocity() < -delta || (*i)->note()->velocity() + delta > 127) {
2928 start_note_diff_command (_("change velocities"));
2930 for (Selection::iterator i = _selection.begin(); i != _selection.end();) {
2931 Selection::iterator next = i;
2935 if (i == _selection.begin()) {
2936 change_note_velocity (*i, delta, true);
2937 value = (*i)->note()->velocity() + delta;
2939 change_note_velocity (*i, value, false);
2943 change_note_velocity (*i, delta, true);
2952 if (!_selection.empty()) {
2954 snprintf (buf, sizeof (buf), "Vel %d",
2955 (int) (*_selection.begin())->note()->velocity());
2956 show_verbose_cursor (buf, 10, 10);
2962 MidiRegionView::transpose (bool up, bool fine, bool allow_smush)
2964 if (_selection.empty()) {
2981 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2983 if ((int8_t) (*i)->note()->note() + delta <= 0) {
2987 if ((int8_t) (*i)->note()->note() + delta > 127) {
2994 start_note_diff_command (_("transpose"));
2996 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2997 Selection::iterator next = i;
2999 change_note_note (*i, delta, true);
3007 MidiRegionView::change_note_lengths (bool fine, bool shorter, Evoral::MusicalTime delta, bool start, bool end)
3011 delta = Evoral::MusicalTime(1.0/128.0);
3013 /* grab the current grid distance */
3014 delta = get_grid_beats(_region->position());
3022 start_note_diff_command (_("change note lengths"));
3024 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3025 Selection::iterator next = i;
3028 /* note the negation of the delta for start */
3031 (start ? -delta : Evoral::MusicalTime()),
3032 (end ? delta : Evoral::MusicalTime()));
3041 MidiRegionView::nudge_notes (bool forward, bool fine)
3043 if (_selection.empty()) {
3047 /* pick a note as the point along the timeline to get the nudge distance.
3048 its not necessarily the earliest note, so we may want to pull the notes out
3049 into a vector and sort before using the first one.
3052 const framepos_t ref_point = source_beats_to_absolute_frames ((*(_selection.begin()))->note()->time());
3053 Evoral::MusicalTime delta;
3057 /* non-fine, move by 1 bar regardless of snap */
3058 delta = Evoral::MusicalTime(trackview.session()->tempo_map().meter_at(ref_point).divisions_per_bar());
3060 } else if (trackview.editor().snap_mode() == Editing::SnapOff) {
3062 /* grid is off - use nudge distance */
3065 const framecnt_t distance = trackview.editor().get_nudge_distance (ref_point, unused);
3066 delta = region_frames_to_region_beats (fabs ((double)distance));
3072 framepos_t next_pos = ref_point;
3075 if (max_framepos - 1 < next_pos) {
3079 if (next_pos == 0) {
3085 trackview.editor().snap_to (next_pos, (forward ? RoundUpAlways : RoundDownAlways), false);
3086 const framecnt_t distance = ref_point - next_pos;
3087 delta = region_frames_to_region_beats (fabs ((double)distance));
3098 start_note_diff_command (_("nudge"));
3100 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3101 Selection::iterator next = i;
3103 change_note_time (*i, delta, true);
3111 MidiRegionView::change_channel(uint8_t channel)
3113 start_note_diff_command(_("change channel"));
3114 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3115 note_diff_add_change (*i, MidiModel::NoteDiffCommand::Channel, channel);
3123 MidiRegionView::note_entered(NoteBase* ev)
3125 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3127 if (_mouse_state == SelectTouchDragging) {
3128 note_selected (ev, true);
3129 } else if (editor->current_mouse_mode() == MouseContent) {
3130 show_verbose_cursor (ev->note ());
3131 } else if (editor->current_mouse_mode() == MouseDraw) {
3132 show_verbose_cursor (ev->note ());
3137 MidiRegionView::note_left (NoteBase*)
3139 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3141 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3142 (*i)->hide_velocity ();
3145 editor->verbose_cursor()->hide ();
3149 MidiRegionView::patch_entered (PatchChange* p)
3152 /* XXX should get patch name if we can */
3153 s << _("Bank ") << (p->patch()->bank() + MIDI_BP_ZERO) << '\n'
3154 << _("Program ") << ((int) p->patch()->program()) + MIDI_BP_ZERO << '\n'
3155 << _("Channel ") << ((int) p->patch()->channel() + 1);
3156 show_verbose_cursor (s.str(), 10, 20);
3157 p->item().grab_focus();
3161 MidiRegionView::patch_left (PatchChange *)
3163 trackview.editor().verbose_cursor()->hide ();
3164 /* focus will transfer back via the enter-notify event sent to this
3170 MidiRegionView::sysex_entered (SysEx* p)
3174 // need a way to extract text from p->_flag->_text
3176 // show_verbose_cursor (s.str(), 10, 20);
3177 p->item().grab_focus();
3181 MidiRegionView::sysex_left (SysEx *)
3183 trackview.editor().verbose_cursor()->hide ();
3184 /* focus will transfer back via the enter-notify event sent to this
3190 MidiRegionView::note_mouse_position (float x_fraction, float /*y_fraction*/, bool can_set_cursor)
3192 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3193 Editing::MouseMode mm = editor->current_mouse_mode();
3194 bool trimmable = (mm == MouseContent || mm == MouseTimeFX || mm == MouseDraw);
3196 Editor::EnterContext* ctx = editor->get_enter_context(NoteItem);
3197 if (can_set_cursor && ctx) {
3198 if (trimmable && x_fraction > 0.0 && x_fraction < 0.2) {
3199 ctx->cursor_ctx->change(editor->cursors()->left_side_trim);
3200 } else if (trimmable && x_fraction >= 0.8 && x_fraction < 1.0) {
3201 ctx->cursor_ctx->change(editor->cursors()->right_side_trim);
3203 ctx->cursor_ctx->change(editor->cursors()->grabber_note);
3209 MidiRegionView::get_fill_color() const
3211 const std::string mod_name = (_dragging ? "dragging region" :
3212 trackview.editor().internal_editing() ? "editable region" :
3215 return ARDOUR_UI::config()->color_mod ("selected region base", mod_name);
3216 } else if (high_enough_for_name || !ARDOUR_UI::config()->get_color_regions_using_track_color()) {
3217 return ARDOUR_UI::config()->color_mod ("midi frame base", mod_name);
3219 return ARDOUR_UI::config()->color_mod (fill_color, mod_name);
3223 MidiRegionView::midi_channel_mode_changed ()
3225 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3226 uint16_t mask = mtv->midi_track()->get_playback_channel_mask();
3227 ChannelMode mode = mtv->midi_track()->get_playback_channel_mode ();
3229 if (mode == ForceChannel) {
3230 mask = 0xFFFF; // Show all notes as active (below)
3233 // Update notes for selection
3234 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3235 (*i)->on_channel_selection_change (mask);
3238 _patch_changes.clear ();
3239 display_patch_changes ();
3243 MidiRegionView::instrument_settings_changed ()
3249 MidiRegionView::cut_copy_clear (Editing::CutCopyOp op)
3251 if (_selection.empty()) {
3255 PublicEditor& editor (trackview.editor());
3259 /* XXX what to do ? */
3263 editor.get_cut_buffer().add (selection_as_cut_buffer());
3271 start_note_diff_command();
3273 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3280 note_diff_remove_note (*i);
3290 MidiRegionView::selection_as_cut_buffer () const
3294 for (Selection::const_iterator i = _selection.begin(); i != _selection.end(); ++i) {
3295 NoteType* n = (*i)->note().get();
3296 notes.insert (boost::shared_ptr<NoteType> (new NoteType (*n)));
3299 MidiCutBuffer* cb = new MidiCutBuffer (trackview.session());
3305 /** This method handles undo */
3307 MidiRegionView::paste (framepos_t pos, const ::Selection& selection, PasteContext& ctx)
3309 // Paste notes, if available
3310 MidiNoteSelection::const_iterator m = selection.midi_notes.get_nth(ctx.counts.n_notes());
3311 if (m != selection.midi_notes.end()) {
3312 ctx.counts.increase_n_notes();
3313 paste_internal(pos, ctx.count, ctx.times, **m);
3316 // Paste control points to automation children, if available
3317 typedef RouteTimeAxisView::AutomationTracks ATracks;
3318 const ATracks& atracks = midi_view()->automation_tracks();
3319 for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) {
3320 a->second->paste(pos, selection, ctx);
3326 /** This method handles undo */
3328 MidiRegionView::paste_internal (framepos_t pos, unsigned paste_count, float times, const MidiCutBuffer& mcb)
3334 start_note_diff_command (_("paste"));
3336 const Evoral::MusicalTime snap_beats = get_grid_beats(pos);
3337 const Evoral::MusicalTime first_time = (*mcb.notes().begin())->time();
3338 const Evoral::MusicalTime last_time = (*mcb.notes().rbegin())->end_time();
3339 const Evoral::MusicalTime duration = last_time - first_time;
3340 const Evoral::MusicalTime snap_duration = duration.snap_to(snap_beats);
3341 const Evoral::MusicalTime paste_offset = snap_duration * paste_count;
3342 const Evoral::MusicalTime pos_beats = absolute_frames_to_source_beats(pos) + paste_offset;
3343 Evoral::MusicalTime end_point = Evoral::MusicalTime();
3345 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste data spans from %1 to %2 (%3) ; paste pos beats = %4 (based on %5 - %6)\n",
3348 duration, pos, _region->position(),
3353 for (int n = 0; n < (int) times; ++n) {
3355 for (Notes::const_iterator i = mcb.notes().begin(); i != mcb.notes().end(); ++i) {
3357 boost::shared_ptr<NoteType> copied_note (new NoteType (*((*i).get())));
3358 copied_note->set_time (pos_beats + copied_note->time() - first_time);
3360 /* make all newly added notes selected */
3362 note_diff_add_note (copied_note, true);
3363 end_point = copied_note->end_time();
3367 /* if we pasted past the current end of the region, extend the region */
3369 framepos_t end_frame = source_beats_to_absolute_frames (end_point);
3370 framepos_t region_end = _region->position() + _region->length() - 1;
3372 if (end_frame > region_end) {
3374 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste extended region from %1 to %2\n", region_end, end_frame));
3376 _region->clear_changes ();
3377 _region->set_length (end_frame - _region->position());
3378 trackview.session()->add_command (new StatefulDiffCommand (_region));
3384 struct EventNoteTimeEarlyFirstComparator {
3385 bool operator() (NoteBase* a, NoteBase* b) {
3386 return a->note()->time() < b->note()->time();
3391 MidiRegionView::time_sort_events ()
3393 if (!_sort_needed) {
3397 EventNoteTimeEarlyFirstComparator cmp;
3400 _sort_needed = false;
3404 MidiRegionView::goto_next_note (bool add_to_selection)
3406 bool use_next = false;
3408 if (_events.back()->selected()) {
3412 time_sort_events ();
3414 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3415 uint16_t const channel_mask = mtv->midi_track()->get_playback_channel_mask();
3417 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3418 if ((*i)->selected()) {
3421 } else if (use_next) {
3422 if (channel_mask & (1 << (*i)->note()->channel())) {
3423 if (!add_to_selection) {
3426 note_selected (*i, true, false);
3433 /* use the first one */
3435 if (!_events.empty() && (channel_mask & (1 << _events.front()->note()->channel ()))) {
3436 unique_select (_events.front());
3441 MidiRegionView::goto_previous_note (bool add_to_selection)
3443 bool use_next = false;
3445 if (_events.front()->selected()) {
3449 time_sort_events ();
3451 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3452 uint16_t const channel_mask = mtv->midi_track()->get_playback_channel_mask ();
3454 for (Events::reverse_iterator i = _events.rbegin(); i != _events.rend(); ++i) {
3455 if ((*i)->selected()) {
3458 } else if (use_next) {
3459 if (channel_mask & (1 << (*i)->note()->channel())) {
3460 if (!add_to_selection) {
3463 note_selected (*i, true, false);
3470 /* use the last one */
3472 if (!_events.empty() && (channel_mask & (1 << (*_events.rbegin())->note()->channel ()))) {
3473 unique_select (*(_events.rbegin()));
3478 MidiRegionView::selection_as_notelist (Notes& selected, bool allow_all_if_none_selected)
3480 bool had_selected = false;
3482 time_sort_events ();
3484 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3485 if ((*i)->selected()) {
3486 selected.insert ((*i)->note());
3487 had_selected = true;
3491 if (allow_all_if_none_selected && !had_selected) {
3492 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3493 selected.insert ((*i)->note());
3499 MidiRegionView::update_ghost_note (double x, double y)
3501 x = std::max(0.0, x);
3503 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3508 _note_group->canvas_to_item (x, y);
3510 PublicEditor& editor = trackview.editor ();
3512 framepos_t const unsnapped_frame = editor.pixel_to_sample (x);
3513 framecnt_t grid_frames;
3514 framepos_t const f = snap_frame_to_grid_underneath (unsnapped_frame, grid_frames);
3516 /* use region_frames... because we are converting a delta within the region
3519 const Evoral::MusicalTime length = get_grid_beats(unsnapped_frame);
3521 /* note that this sets the time of the ghost note in beats relative to
3522 the start of the source; that is how all note times are stored.
3524 _ghost_note->note()->set_time (
3525 std::max(Evoral::MusicalTime(),
3526 absolute_frames_to_source_beats (f + _region->position ())));
3527 _ghost_note->note()->set_length (length);
3528 _ghost_note->note()->set_note (midi_stream_view()->y_to_note (y));
3529 _ghost_note->note()->set_channel (mtv->get_channel_for_add ());
3531 /* the ghost note does not appear in ghost regions, so pass false in here */
3532 update_note (_ghost_note, false);
3534 show_verbose_cursor (_ghost_note->note ());
3538 MidiRegionView::create_ghost_note (double x, double y)
3540 remove_ghost_note ();
3542 boost::shared_ptr<NoteType> g (new NoteType);
3543 _ghost_note = new Note (*this, _note_group, g);
3544 _ghost_note->set_ignore_events (true);
3545 _ghost_note->set_outline_color (0x000000aa);
3546 update_ghost_note (x, y);
3547 _ghost_note->show ();
3549 show_verbose_cursor (_ghost_note->note ());
3553 MidiRegionView::remove_ghost_note ()
3560 MidiRegionView::snap_changed ()
3566 create_ghost_note (_last_ghost_x, _last_ghost_y);
3570 MidiRegionView::drop_down_keys ()
3572 _mouse_state = None;
3576 MidiRegionView::maybe_select_by_position (GdkEventButton* ev, double /*x*/, double y)
3578 /* XXX: This is dead code. What was it for? */
3580 double note = midi_stream_view()->y_to_note(y);
3582 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3584 uint16_t chn_mask = mtv->midi_track()->get_playback_channel_mask();
3586 if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
3587 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchGreaterThanOrEqual, (uint8_t) floor (note), chn_mask);
3588 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
3589 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchLessThanOrEqual, (uint8_t) floor (note), chn_mask);
3594 bool add_mrv_selection = false;
3596 if (_selection.empty()) {
3597 add_mrv_selection = true;
3600 for (Events::iterator i = e.begin(); i != e.end(); ++i) {
3601 if (_selection.insert (*i).second) {
3602 (*i)->set_selected (true);
3606 if (add_mrv_selection) {
3607 PublicEditor& editor (trackview.editor());
3608 editor.get_selection().add (this);
3613 MidiRegionView::color_handler ()
3615 RegionView::color_handler ();
3617 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3618 (*i)->set_selected ((*i)->selected()); // will change color
3621 /* XXX probably more to do here */
3625 MidiRegionView::enable_display (bool yn)
3627 RegionView::enable_display (yn);
3634 MidiRegionView::show_step_edit_cursor (Evoral::MusicalTime pos)
3636 if (_step_edit_cursor == 0) {
3637 ArdourCanvas::Item* const group = get_canvas_group();
3639 _step_edit_cursor = new ArdourCanvas::Rectangle (group);
3640 _step_edit_cursor->set_y0 (0);
3641 _step_edit_cursor->set_y1 (midi_stream_view()->contents_height());
3642 _step_edit_cursor->set_fill_color (RGBA_TO_UINT (45,0,0,90));
3643 _step_edit_cursor->set_outline_color (RGBA_TO_UINT (85,0,0,90));
3646 move_step_edit_cursor (pos);
3647 _step_edit_cursor->show ();
3651 MidiRegionView::move_step_edit_cursor (Evoral::MusicalTime pos)
3653 _step_edit_cursor_position = pos;
3655 if (_step_edit_cursor) {
3656 double pixel = trackview.editor().sample_to_pixel (region_beats_to_region_frames (pos));
3657 _step_edit_cursor->set_x0 (pixel);
3658 set_step_edit_cursor_width (_step_edit_cursor_width);
3663 MidiRegionView::hide_step_edit_cursor ()
3665 if (_step_edit_cursor) {
3666 _step_edit_cursor->hide ();
3671 MidiRegionView::set_step_edit_cursor_width (Evoral::MusicalTime beats)
3673 _step_edit_cursor_width = beats;
3675 if (_step_edit_cursor) {
3676 _step_edit_cursor->set_x1 (_step_edit_cursor->x0() + trackview.editor().sample_to_pixel (region_beats_to_region_frames (beats)));
3680 /** Called when a diskstream on our track has received some data. Update the view, if applicable.
3681 * @param w Source that the data will end up in.
3684 MidiRegionView::data_recorded (boost::weak_ptr<MidiSource> w)
3686 if (!_active_notes) {
3687 /* we aren't actively being recorded to */
3691 boost::shared_ptr<MidiSource> src = w.lock ();
3692 if (!src || src != midi_region()->midi_source()) {
3693 /* recorded data was not destined for our source */
3697 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (&trackview);
3699 boost::shared_ptr<MidiBuffer> buf = mtv->midi_track()->get_gui_feed_buffer ();
3701 framepos_t back = max_framepos;
3703 for (MidiBuffer::iterator i = buf->begin(); i != buf->end(); ++i) {
3704 Evoral::MIDIEvent<MidiBuffer::TimeType> const ev (*i, false);
3706 if (ev.is_channel_event()) {
3707 if (get_channel_mode() == FilterChannels) {
3708 if (((uint16_t(1) << ev.channel()) & get_selected_channels()) == 0) {
3714 /* convert from session frames to source beats */
3715 Evoral::MusicalTime const time_beats = _source_relative_time_converter.from(ev.time());
3717 if (ev.type() == MIDI_CMD_NOTE_ON) {
3718 boost::shared_ptr<NoteType> note (
3719 new NoteType (ev.channel(), time_beats, Evoral::MusicalTime(), ev.note(), ev.velocity()));
3721 add_note (note, true);
3723 /* fix up our note range */
3724 if (ev.note() < _current_range_min) {
3725 midi_stream_view()->apply_note_range (ev.note(), _current_range_max, true);
3726 } else if (ev.note() > _current_range_max) {
3727 midi_stream_view()->apply_note_range (_current_range_min, ev.note(), true);
3730 } else if (ev.type() == MIDI_CMD_NOTE_OFF) {
3731 resolve_note (ev.note (), time_beats);
3737 midi_stream_view()->check_record_layers (region(), back);
3741 MidiRegionView::trim_front_starting ()
3743 /* Reparent the note group to the region view's parent, so that it doesn't change
3744 when the region view is trimmed.
3746 _temporary_note_group = new ArdourCanvas::Container (group->parent ());
3747 _temporary_note_group->move (group->position ());
3748 _note_group->reparent (_temporary_note_group);
3752 MidiRegionView::trim_front_ending ()
3754 _note_group->reparent (group);
3755 delete _temporary_note_group;
3756 _temporary_note_group = 0;
3758 if (_region->start() < 0) {
3759 /* Trim drag made start time -ve; fix this */
3760 midi_region()->fix_negative_start ();
3765 MidiRegionView::edit_patch_change (PatchChange* pc)
3767 PatchChangeDialog d (&_source_relative_time_converter, trackview.session(), *pc->patch (), instrument_info(), Gtk::Stock::APPLY, true);
3769 int response = d.run();
3772 case Gtk::RESPONSE_ACCEPT:
3774 case Gtk::RESPONSE_REJECT:
3775 delete_patch_change (pc);
3781 change_patch_change (pc->patch(), d.patch ());
3785 MidiRegionView::delete_sysex (SysEx* /*sysex*/)
3788 // sysyex object doesn't have a pointer to a sysex event
3789 // MidiModel::SysExDiffCommand* c = _model->new_sysex_diff_command (_("delete sysex"));
3790 // c->remove (sysex->sysex());
3791 // _model->apply_command (*trackview.session(), c);
3793 //_sys_exes.clear ();
3794 // display_sysexes();
3798 MidiRegionView::show_verbose_cursor (boost::shared_ptr<NoteType> n) const
3800 using namespace MIDI::Name;
3804 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3806 boost::shared_ptr<MasterDeviceNames> device_names(mtv->get_device_names());
3808 MIDI::Name::PatchPrimaryKey patch_key;
3809 get_patch_key_at(n->time(), n->channel(), patch_key);
3810 name = device_names->note_name(mtv->gui_property(X_("midnam-custom-device-mode")),
3813 patch_key.program(),
3819 snprintf (buf, sizeof (buf), "%d %s\nCh %d Vel %d",
3821 name.empty() ? Evoral::midi_note_name (n->note()).c_str() : name.c_str(),
3822 (int) n->channel() + 1,
3823 (int) n->velocity());
3825 show_verbose_cursor(buf, 10, 20);
3829 MidiRegionView::show_verbose_cursor (string const & text, double xoffset, double yoffset) const
3831 trackview.editor().verbose_cursor()->set (text);
3832 trackview.editor().verbose_cursor()->show ();
3833 trackview.editor().verbose_cursor()->set_offset (ArdourCanvas::Duple (xoffset, yoffset));
3836 /** @param p A session framepos.
3837 * @param grid_frames Filled in with the number of frames that a grid interval is at p.
3838 * @return p snapped to the grid subdivision underneath it.
3841 MidiRegionView::snap_frame_to_grid_underneath (framepos_t p, framecnt_t& grid_frames) const
3843 PublicEditor& editor = trackview.editor ();
3845 const Evoral::MusicalTime grid_beats = get_grid_beats(p);
3847 grid_frames = region_beats_to_region_frames (grid_beats);
3849 /* Hack so that we always snap to the note that we are over, instead of snapping
3850 to the next one if we're more than halfway through the one we're over.
3852 if (editor.snap_mode() == SnapNormal && p >= grid_frames / 2) {
3853 p -= grid_frames / 2;
3856 return snap_frame_to_frame (p);
3859 /** Called when the selection has been cleared in any MidiRegionView.
3860 * @param rv MidiRegionView that the selection was cleared in.
3863 MidiRegionView::selection_cleared (MidiRegionView* rv)
3869 /* Clear our selection in sympathy; but don't signal the fact */
3870 clear_selection (false);
3874 MidiRegionView::note_button_release ()
3876 _note_player.reset();
3880 MidiRegionView::get_channel_mode () const
3882 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (&trackview);
3883 return rtav->midi_track()->get_playback_channel_mode();
3887 MidiRegionView::get_selected_channels () const
3889 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (&trackview);
3890 return rtav->midi_track()->get_playback_channel_mask();
3895 MidiRegionView::get_grid_beats(framepos_t pos) const
3897 PublicEditor& editor = trackview.editor();
3898 bool success = false;
3899 Evoral::MusicalTime beats = editor.get_grid_type_as_beats(success, pos);
3901 beats = Evoral::MusicalTime(1);