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)
125 , pre_enter_cursor (0)
126 , pre_press_cursor (0)
127 , pre_note_enter_cursor (0)
130 CANVAS_DEBUG_NAME (_note_group, string_compose ("note group for %1", get_item_name()));
131 _note_group->raise_to_top();
132 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
134 Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&MidiRegionView::parameter_changed, this, _1), gui_context());
135 connect_to_diskstream ();
137 SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), boost::bind (&MidiRegionView::selection_cleared, this, _1), gui_context ());
139 PublicEditor& editor (trackview.editor());
140 editor.get_selection().ClearMidiNoteSelection.connect (_clear_midi_selection_connection, invalidator (*this), boost::bind (&MidiRegionView::clear_midi_selection, this), gui_context ());
143 MidiRegionView::MidiRegionView (ArdourCanvas::Container* parent,
144 RouteTimeAxisView& tv,
145 boost::shared_ptr<MidiRegion> r,
147 uint32_t basic_color,
149 TimeAxisViewItem::Visibility visibility)
150 : RegionView (parent, tv, r, spu, basic_color, recording, visibility)
151 , _current_range_min(0)
152 , _current_range_max(0)
153 , _region_relative_time_converter(r->session().tempo_map(), r->position())
154 , _source_relative_time_converter(r->session().tempo_map(), r->position() - r->start())
156 , _note_group (new ArdourCanvas::Container (parent))
157 , _note_diff_command (0)
159 , _step_edit_cursor (0)
160 , _step_edit_cursor_width (1.0)
161 , _step_edit_cursor_position (0.0)
162 , _channel_selection_scoped_note (0)
163 , _temporary_note_group (0)
166 , _sort_needed (true)
167 , _optimization_iterator (_events.end())
169 , _no_sound_notes (false)
172 , _grabbed_keyboard (false)
174 , pre_enter_cursor (0)
175 , pre_press_cursor (0)
176 , pre_note_enter_cursor (0)
179 CANVAS_DEBUG_NAME (_note_group, string_compose ("note group for %1", get_item_name()));
180 _note_group->raise_to_top();
182 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
184 connect_to_diskstream ();
186 SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), boost::bind (&MidiRegionView::selection_cleared, this, _1), gui_context ());
188 PublicEditor& editor (trackview.editor());
189 editor.get_selection().ClearMidiNoteSelection.connect (_clear_midi_selection_connection, invalidator (*this), boost::bind (&MidiRegionView::clear_midi_selection, this), gui_context ());
193 MidiRegionView::parameter_changed (std::string const & p)
195 if (p == "display-first-midi-bank-as-zero") {
196 if (_enable_display) {
202 MidiRegionView::MidiRegionView (const MidiRegionView& other)
203 : sigc::trackable(other)
205 , _current_range_min(0)
206 , _current_range_max(0)
207 , _region_relative_time_converter(other.region_relative_time_converter())
208 , _source_relative_time_converter(other.source_relative_time_converter())
210 , _note_group (new ArdourCanvas::Container (get_canvas_group()))
211 , _note_diff_command (0)
213 , _step_edit_cursor (0)
214 , _step_edit_cursor_width (1.0)
215 , _step_edit_cursor_position (0.0)
216 , _channel_selection_scoped_note (0)
217 , _temporary_note_group (0)
220 , _sort_needed (true)
221 , _optimization_iterator (_events.end())
223 , _no_sound_notes (false)
226 , _grabbed_keyboard (false)
228 , pre_enter_cursor (0)
229 , pre_press_cursor (0)
230 , pre_note_enter_cursor (0)
236 MidiRegionView::MidiRegionView (const MidiRegionView& other, boost::shared_ptr<MidiRegion> region)
237 : RegionView (other, boost::shared_ptr<Region> (region))
238 , _current_range_min(0)
239 , _current_range_max(0)
240 , _region_relative_time_converter(other.region_relative_time_converter())
241 , _source_relative_time_converter(other.source_relative_time_converter())
243 , _note_group (new ArdourCanvas::Container (get_canvas_group()))
244 , _note_diff_command (0)
246 , _step_edit_cursor (0)
247 , _step_edit_cursor_width (1.0)
248 , _step_edit_cursor_position (0.0)
249 , _channel_selection_scoped_note (0)
250 , _temporary_note_group (0)
253 , _sort_needed (true)
254 , _optimization_iterator (_events.end())
256 , _no_sound_notes (false)
259 , _grabbed_keyboard (false)
261 , pre_enter_cursor (0)
262 , pre_press_cursor (0)
263 , pre_note_enter_cursor (0)
270 MidiRegionView::init (bool wfd)
272 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
274 NoteBase::NoteBaseDeleted.connect (note_delete_connection, MISSING_INVALIDATOR,
275 boost::bind (&MidiRegionView::maybe_remove_deleted_note_from_selection, this, _1),
279 Glib::Threads::Mutex::Lock lm(midi_region()->midi_source(0)->mutex());
280 midi_region()->midi_source(0)->load_model(lm);
283 _model = midi_region()->midi_source(0)->model();
284 _enable_display = false;
285 _fill_color_name = "midi frame base";
287 RegionView::init (false);
289 set_height (trackview.current_height());
292 region_sync_changed ();
293 region_resized (ARDOUR::bounds_change);
298 _enable_display = true;
301 display_model (_model);
305 reset_width_dependent_items (_pixel_width);
307 group->raise_to_top();
309 midi_view()->midi_track()->PlaybackChannelModeChanged.connect (_channel_mode_changed_connection, invalidator (*this),
310 boost::bind (&MidiRegionView::midi_channel_mode_changed, this),
313 instrument_info().Changed.connect (_instrument_changed_connection, invalidator (*this),
314 boost::bind (&MidiRegionView::instrument_settings_changed, this), gui_context());
316 trackview.editor().SnapChanged.connect(snap_changed_connection, invalidator(*this),
317 boost::bind (&MidiRegionView::snap_changed, this),
320 trackview.editor().MouseModeChanged.connect(_mouse_mode_connection, invalidator (*this),
321 boost::bind (&MidiRegionView::mouse_mode_changed, this),
324 Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&MidiRegionView::parameter_changed, this, _1), gui_context());
325 connect_to_diskstream ();
327 SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), boost::bind (&MidiRegionView::selection_cleared, this, _1), gui_context ());
329 PublicEditor& editor (trackview.editor());
330 editor.get_selection().ClearMidiNoteSelection.connect (_clear_midi_selection_connection, invalidator (*this), boost::bind (&MidiRegionView::clear_midi_selection, this), gui_context ());
334 MidiRegionView::instrument_info () const
336 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
337 return route_ui->route()->instrument_info();
340 const boost::shared_ptr<ARDOUR::MidiRegion>
341 MidiRegionView::midi_region() const
343 return boost::dynamic_pointer_cast<ARDOUR::MidiRegion>(_region);
347 MidiRegionView::connect_to_diskstream ()
349 midi_view()->midi_track()->DataRecorded.connect(
350 *this, invalidator(*this),
351 boost::bind (&MidiRegionView::data_recorded, this, _1),
356 MidiRegionView::canvas_group_event(GdkEvent* ev)
358 if (in_destructor || _recregion) {
362 if (!trackview.editor().internal_editing()) {
363 // not in internal edit mode, so just act like a normal region
364 return RegionView::canvas_group_event (ev);
370 case GDK_ENTER_NOTIFY:
371 _last_event_x = ev->crossing.x;
372 _last_event_y = ev->crossing.y;
373 enter_notify(&ev->crossing);
374 // set entered_regionview (among other things)
375 return RegionView::canvas_group_event (ev);
377 case GDK_LEAVE_NOTIFY:
378 _last_event_x = ev->crossing.x;
379 _last_event_y = ev->crossing.y;
380 leave_notify(&ev->crossing);
381 // reset entered_regionview (among other things)
382 return RegionView::canvas_group_event (ev);
385 if (scroll (&ev->scroll)) {
391 return key_press (&ev->key);
393 case GDK_KEY_RELEASE:
394 return key_release (&ev->key);
396 case GDK_BUTTON_PRESS:
397 return button_press (&ev->button);
399 case GDK_BUTTON_RELEASE:
400 r = button_release (&ev->button);
405 case GDK_MOTION_NOTIFY:
406 _last_event_x = ev->motion.x;
407 _last_event_y = ev->motion.y;
408 return motion (&ev->motion);
414 return RegionView::canvas_group_event (ev);
418 MidiRegionView::enter_notify (GdkEventCrossing* ev)
427 MidiRegionView::leave_notify (GdkEventCrossing*)
436 MidiRegionView::mouse_mode_changed ()
438 // Adjust frame colour (become more transparent for internal tools)
442 if (trackview.editor().internal_editing()) {
443 // Switched in to internal editing mode while entered
446 // Switched out of internal editing mode while entered
453 MidiRegionView::enter_internal()
455 if (trackview.editor().current_mouse_mode() == MouseDraw && _mouse_state != AddDragging) {
456 // Show ghost note under pencil
457 create_ghost_note(_last_event_x, _last_event_y);
460 if (!_selection.empty()) {
461 // Grab keyboard for moving selected notes with arrow keys
462 Keyboard::magic_widget_grab_focus();
463 _grabbed_keyboard = true;
468 MidiRegionView::leave_internal()
470 trackview.editor().verbose_cursor()->hide ();
471 remove_ghost_note ();
473 if (_grabbed_keyboard) {
474 Keyboard::magic_widget_drop_focus();
475 _grabbed_keyboard = false;
480 MidiRegionView::button_press (GdkEventButton* ev)
482 if (ev->button != 1) {
486 Editor* editor = dynamic_cast<Editor *> (&trackview.editor());
487 MouseMode m = editor->current_mouse_mode();
489 if (m == MouseContent && Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())) {
490 pre_press_cursor = editor->get_canvas_cursor ();
491 editor->set_canvas_cursor (editor->cursors()->midi_pencil);
494 if (_mouse_state != SelectTouchDragging) {
496 _pressed_button = ev->button;
497 _mouse_state = Pressed;
502 _pressed_button = ev->button;
508 MidiRegionView::button_release (GdkEventButton* ev)
510 double event_x, event_y;
512 if (ev->button != 1) {
519 group->canvas_to_item (event_x, event_y);
522 PublicEditor& editor = trackview.editor ();
524 if (pre_press_cursor) {
525 dynamic_cast<Editor*>(&editor)->set_canvas_cursor (pre_press_cursor, false);
526 pre_press_cursor = 0;
529 switch (_mouse_state) {
530 case Pressed: // Clicked
532 switch (editor.current_mouse_mode()) {
534 /* no motion occured - simple click */
543 if (Keyboard::is_insert_note_event(ev)) {
545 double event_x, event_y;
549 group->canvas_to_item (event_x, event_y);
551 Evoral::MusicalTime beats = get_grid_beats(editor.pixel_to_sample(event_x));
553 /* Shorten the length by 1 tick so that we can add a new note at the next
554 grid snap without it overlapping this one.
556 beats -= Evoral::MusicalTime::tick();
558 create_note_at (editor.pixel_to_sample (event_x), event_y, beats, true);
565 Evoral::MusicalTime beats = get_grid_beats(editor.pixel_to_sample(event_x));
567 /* Shorten the length by 1 tick so that we can add a new note at the next
568 grid snap without it overlapping this one.
570 beats -= Evoral::MusicalTime::tick();
572 create_note_at (editor.pixel_to_sample (event_x), event_y, beats, true);
583 case SelectRectDragging:
585 editor.drags()->end_grab ((GdkEvent *) ev);
587 create_ghost_note (ev->x, ev->y);
599 MidiRegionView::motion (GdkEventMotion* ev)
601 PublicEditor& editor = trackview.editor ();
603 if (!_ghost_note && editor.current_mouse_mode() == MouseContent &&
604 Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier()) &&
605 _mouse_state != AddDragging) {
607 create_ghost_note (ev->x, ev->y);
609 } else if (_ghost_note && editor.current_mouse_mode() == MouseContent &&
610 Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())) {
612 update_ghost_note (ev->x, ev->y);
614 } else if (_ghost_note && editor.current_mouse_mode() == MouseContent) {
616 remove_ghost_note ();
617 editor.verbose_cursor()->hide ();
619 } else if (_ghost_note && editor.current_mouse_mode() == MouseDraw) {
621 update_ghost_note (ev->x, ev->y);
624 /* any motion immediately hides velocity text that may have been visible */
626 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
627 (*i)->hide_velocity ();
630 switch (_mouse_state) {
633 if (_pressed_button == 1) {
635 MouseMode m = editor.current_mouse_mode();
637 if (m == MouseDraw || (m == MouseContent && Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier()))) {
638 editor.drags()->set (new NoteCreateDrag (dynamic_cast<Editor *> (&editor), group, this), (GdkEvent *) ev);
639 _mouse_state = AddDragging;
640 remove_ghost_note ();
641 editor.verbose_cursor()->hide ();
643 } else if (m == MouseContent) {
644 editor.drags()->set (new MidiRubberbandSelectDrag (dynamic_cast<Editor *> (&editor), this), (GdkEvent *) ev);
645 if (!Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
648 _mouse_state = SelectRectDragging;
650 } else if (m == MouseRange) {
651 editor.drags()->set (new MidiVerticalSelectDrag (dynamic_cast<Editor *> (&editor), this), (GdkEvent *) ev);
652 _mouse_state = SelectVerticalDragging;
659 case SelectRectDragging:
660 case SelectVerticalDragging:
662 editor.drags()->motion_handler ((GdkEvent *) ev, false);
665 case SelectTouchDragging:
673 /* we may be dragging some non-note object (eg. patch-change, sysex)
676 return editor.drags()->motion_handler ((GdkEvent *) ev, false);
681 MidiRegionView::scroll (GdkEventScroll* ev)
683 if (_selection.empty()) {
687 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
688 /* XXX: bit of a hack; allow PrimaryModifier scroll through so that
689 it still works for zoom.
694 trackview.editor().verbose_cursor()->hide ();
696 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
697 bool together = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
699 if (ev->direction == GDK_SCROLL_UP) {
700 change_velocities (true, fine, false, together);
701 } else if (ev->direction == GDK_SCROLL_DOWN) {
702 change_velocities (false, fine, false, together);
704 /* left, right: we don't use them */
712 MidiRegionView::key_press (GdkEventKey* ev)
714 /* since GTK bindings are generally activated on press, and since
715 detectable auto-repeat is the name of the game and only sends
716 repeated presses, carry out key actions at key press, not release.
719 bool unmodified = Keyboard::no_modifier_keys_pressed (ev);
721 if (unmodified && (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R)) {
722 _mouse_state = SelectTouchDragging;
725 } else if (ev->keyval == GDK_Escape && unmodified) {
729 } else if (unmodified && (ev->keyval == GDK_comma || ev->keyval == GDK_period)) {
731 bool start = (ev->keyval == GDK_comma);
732 bool end = (ev->keyval == GDK_period);
733 bool shorter = Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier);
734 bool fine = Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
736 change_note_lengths (fine, shorter, Evoral::MusicalTime(), start, end);
740 } else if ((ev->keyval == GDK_BackSpace || ev->keyval == GDK_Delete) && unmodified) {
742 if (_selection.empty()) {
749 } else if (ev->keyval == GDK_Tab) {
751 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
752 goto_previous_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
754 goto_next_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
758 } else if (ev->keyval == GDK_ISO_Left_Tab) {
760 /* Shift-TAB generates ISO Left Tab, for some reason */
762 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
763 goto_previous_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
765 goto_next_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
771 } else if (ev->keyval == GDK_Up) {
773 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
774 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
775 bool together = Keyboard::modifier_state_contains (ev->state, Keyboard::Level4Modifier);
777 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
778 change_velocities (true, fine, allow_smush, together);
780 transpose (true, fine, allow_smush);
784 } else if (ev->keyval == GDK_Down) {
786 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
787 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
788 bool together = Keyboard::modifier_state_contains (ev->state, Keyboard::Level4Modifier);
790 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
791 change_velocities (false, fine, allow_smush, together);
793 transpose (false, fine, allow_smush);
797 } else if (ev->keyval == GDK_Left) {
799 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
800 nudge_notes (false, fine);
803 } else if (ev->keyval == GDK_Right) {
805 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
806 nudge_notes (true, fine);
809 } else if (ev->keyval == GDK_c && unmodified) {
813 } else if (ev->keyval == GDK_v && unmodified) {
822 MidiRegionView::key_release (GdkEventKey* ev)
824 if ((_mouse_state == SelectTouchDragging) && (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R)) {
832 MidiRegionView::channel_edit ()
834 if (_selection.empty()) {
838 /* pick a note somewhat at random (since Selection is a set<>) to
839 * provide the "current" channel for the dialog.
842 uint8_t current_channel = (*_selection.begin())->note()->channel ();
843 MidiChannelDialog channel_dialog (current_channel);
844 int ret = channel_dialog.run ();
847 case Gtk::RESPONSE_OK:
853 uint8_t new_channel = channel_dialog.active_channel ();
855 start_note_diff_command (_("channel edit"));
857 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
858 Selection::iterator next = i;
860 change_note_channel (*i, new_channel);
868 MidiRegionView::velocity_edit ()
870 if (_selection.empty()) {
874 /* pick a note somewhat at random (since Selection is a set<>) to
875 * provide the "current" velocity for the dialog.
878 uint8_t current_velocity = (*_selection.begin())->note()->velocity ();
879 MidiVelocityDialog velocity_dialog (current_velocity);
880 int ret = velocity_dialog.run ();
883 case Gtk::RESPONSE_OK:
889 uint8_t new_velocity = velocity_dialog.velocity ();
891 start_note_diff_command (_("velocity edit"));
893 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
894 Selection::iterator next = i;
896 change_note_velocity (*i, new_velocity, false);
904 MidiRegionView::show_list_editor ()
907 _list_editor = new MidiListEditor (trackview.session(), midi_region(), midi_view()->midi_track());
909 _list_editor->present ();
912 /** Add a note to the model, and the view, at a canvas (click) coordinate.
913 * \param t time in frames relative to the position of the region
914 * \param y vertical position in pixels
915 * \param length duration of the note in beats
916 * \param snap_t true to snap t to the grid, otherwise false.
919 MidiRegionView::create_note_at (framepos_t t, double y, Evoral::MusicalTime length, bool snap_t)
921 if (length < 2 * DBL_EPSILON) {
925 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
926 MidiStreamView* const view = mtv->midi_view();
928 const double note = view->y_to_note(y);
930 // Start of note in frames relative to region start
932 framecnt_t grid_frames;
933 t = snap_frame_to_grid_underneath (t, grid_frames);
936 const boost::shared_ptr<NoteType> new_note (
937 new NoteType (mtv->get_channel_for_add (),
938 region_frames_to_region_beats(t + _region->start()),
940 (uint8_t)note, 0x40));
942 if (_model->contains (new_note)) {
946 view->update_note_range(new_note->note());
948 MidiModel::NoteDiffCommand* cmd = _model->new_note_diff_command(_("add note"));
950 _model->apply_command(*trackview.session(), cmd);
952 play_midi_note (new_note);
956 MidiRegionView::clear_events (bool with_selection_signal)
958 clear_selection (with_selection_signal);
961 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
962 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
967 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
972 _patch_changes.clear();
974 _optimization_iterator = _events.end();
978 MidiRegionView::display_model(boost::shared_ptr<MidiModel> model)
982 content_connection.disconnect ();
983 _model->ContentsChanged.connect (content_connection, invalidator (*this), boost::bind (&MidiRegionView::redisplay_model, this), gui_context());
987 if (_enable_display) {
993 MidiRegionView::start_note_diff_command (string name)
995 if (!_note_diff_command) {
996 _note_diff_command = _model->new_note_diff_command (name);
1001 MidiRegionView::note_diff_add_note (const boost::shared_ptr<NoteType> note, bool selected, bool show_velocity)
1003 if (_note_diff_command) {
1004 _note_diff_command->add (note);
1007 _marked_for_selection.insert(note);
1009 if (show_velocity) {
1010 _marked_for_velocity.insert(note);
1015 MidiRegionView::note_diff_remove_note (NoteBase* ev)
1017 if (_note_diff_command && ev->note()) {
1018 _note_diff_command->remove(ev->note());
1023 MidiRegionView::note_diff_add_change (NoteBase* ev,
1024 MidiModel::NoteDiffCommand::Property property,
1027 if (_note_diff_command) {
1028 _note_diff_command->change (ev->note(), property, val);
1033 MidiRegionView::note_diff_add_change (NoteBase* ev,
1034 MidiModel::NoteDiffCommand::Property property,
1035 Evoral::MusicalTime val)
1037 if (_note_diff_command) {
1038 _note_diff_command->change (ev->note(), property, val);
1043 MidiRegionView::apply_diff (bool as_subcommand)
1047 if (!_note_diff_command) {
1051 if ((add_or_remove = _note_diff_command->adds_or_removes())) {
1052 // Mark all selected notes for selection when model reloads
1053 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1054 _marked_for_selection.insert((*i)->note());
1058 if (as_subcommand) {
1059 _model->apply_command_as_subcommand (*trackview.session(), _note_diff_command);
1061 _model->apply_command (*trackview.session(), _note_diff_command);
1064 _note_diff_command = 0;
1065 midi_view()->midi_track()->playlist_modified();
1067 if (add_or_remove) {
1068 _marked_for_selection.clear();
1071 _marked_for_velocity.clear();
1075 MidiRegionView::abort_command()
1077 delete _note_diff_command;
1078 _note_diff_command = 0;
1083 MidiRegionView::find_canvas_note (boost::shared_ptr<NoteType> note)
1085 if (_optimization_iterator != _events.end()) {
1086 ++_optimization_iterator;
1089 if (_optimization_iterator != _events.end() && (*_optimization_iterator)->note() == note) {
1090 return *_optimization_iterator;
1093 for (_optimization_iterator = _events.begin(); _optimization_iterator != _events.end(); ++_optimization_iterator) {
1094 if ((*_optimization_iterator)->note() == note) {
1095 return *_optimization_iterator;
1103 MidiRegionView::get_events (Events& e, Evoral::Sequence<Evoral::MusicalTime>::NoteOperator op, uint8_t val, int chan_mask)
1105 MidiModel::Notes notes;
1106 _model->get_notes (notes, op, val, chan_mask);
1108 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1109 NoteBase* cne = find_canvas_note (*n);
1117 MidiRegionView::redisplay_model()
1119 // Don't redisplay the model if we're currently recording and displaying that
1120 if (_active_notes) {
1128 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1129 (*i)->invalidate ();
1132 MidiModel::ReadLock lock(_model->read_lock());
1134 MidiModel::Notes& notes (_model->notes());
1135 _optimization_iterator = _events.begin();
1137 bool empty_when_starting = _events.empty();
1139 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1141 boost::shared_ptr<NoteType> note (*n);
1145 if (note_in_region_range (note, visible)) {
1147 if (!empty_when_starting && (cne = find_canvas_note (note)) != 0) {
1154 if ((cn = dynamic_cast<Note*>(cne)) != 0) {
1156 } else if ((ch = dynamic_cast<Hit*>(cne)) != 0) {
1168 add_note (note, visible);
1173 if (!empty_when_starting && (cne = find_canvas_note (note)) != 0) {
1181 /* remove note items that are no longer valid */
1183 if (!empty_when_starting) {
1184 for (Events::iterator i = _events.begin(); i != _events.end(); ) {
1185 if (!(*i)->valid ()) {
1187 for (vector<GhostRegion*>::iterator j = ghosts.begin(); j != ghosts.end(); ++j) {
1188 MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*j);
1190 gr->remove_note (*i);
1195 i = _events.erase (i);
1203 _patch_changes.clear();
1207 display_patch_changes ();
1209 _marked_for_selection.clear ();
1210 _marked_for_velocity.clear ();
1212 /* we may have caused _events to contain things out of order (e.g. if a note
1213 moved earlier or later). we don't generally need them in time order, but
1214 make a note that a sort is required for those cases that require it.
1217 _sort_needed = true;
1221 MidiRegionView::display_patch_changes ()
1223 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1224 uint16_t chn_mask = mtv->midi_track()->get_playback_channel_mask();
1226 for (uint8_t i = 0; i < 16; ++i) {
1227 display_patch_changes_on_channel (i, chn_mask & (1 << i));
1231 /** @param active_channel true to display patch changes fully, false to display
1232 * them `greyed-out' (as on an inactive channel)
1235 MidiRegionView::display_patch_changes_on_channel (uint8_t channel, bool active_channel)
1237 for (MidiModel::PatchChanges::const_iterator i = _model->patch_changes().begin(); i != _model->patch_changes().end(); ++i) {
1239 if ((*i)->channel() != channel) {
1243 const string patch_name = instrument_info().get_patch_name ((*i)->bank(), (*i)->program(), channel);
1244 add_canvas_patch_change (*i, patch_name, active_channel);
1249 MidiRegionView::display_sysexes()
1251 bool have_periodic_system_messages = false;
1252 bool display_periodic_messages = true;
1254 if (!Config->get_never_display_periodic_midi()) {
1256 for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
1257 const boost::shared_ptr<const Evoral::MIDIEvent<Evoral::MusicalTime> > mev =
1258 boost::static_pointer_cast<const Evoral::MIDIEvent<Evoral::MusicalTime> > (*i);
1261 if (mev->is_spp() || mev->is_mtc_quarter() || mev->is_mtc_full()) {
1262 have_periodic_system_messages = true;
1268 if (have_periodic_system_messages) {
1269 double zoom = trackview.editor().get_current_zoom (); // frames per pixel
1271 /* get an approximate value for the number of samples per video frame */
1273 double video_frame = trackview.session()->frame_rate() * (1.0/30);
1275 /* if we are zoomed out beyond than the cutoff (i.e. more
1276 * frames per pixel than frames per 4 video frames), don't
1277 * show periodic sysex messages.
1280 if (zoom > (video_frame*4)) {
1281 display_periodic_messages = false;
1285 display_periodic_messages = false;
1288 for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
1290 const boost::shared_ptr<const Evoral::MIDIEvent<Evoral::MusicalTime> > mev =
1291 boost::static_pointer_cast<const Evoral::MIDIEvent<Evoral::MusicalTime> > (*i);
1293 Evoral::MusicalTime time = (*i)->time();
1296 if (mev->is_spp() || mev->is_mtc_quarter() || mev->is_mtc_full()) {
1297 if (!display_periodic_messages) {
1305 for (uint32_t b = 0; b < (*i)->size(); ++b) {
1306 str << int((*i)->buffer()[b]);
1307 if (b != (*i)->size() -1) {
1311 string text = str.str();
1313 const double x = trackview.editor().sample_to_pixel(source_beats_to_region_frames(time));
1315 double height = midi_stream_view()->contents_height();
1317 // CAIROCANVAS: no longer passing *i (the sysex event) to the
1318 // SysEx canvas object!!!
1320 boost::shared_ptr<SysEx> sysex = boost::shared_ptr<SysEx>(
1321 new SysEx (*this, _note_group, text, height, x, 1.0));
1323 // Show unless message is beyond the region bounds
1324 if (time - _region->start() >= _region->length() || time < _region->start()) {
1330 _sys_exes.push_back(sysex);
1334 MidiRegionView::~MidiRegionView ()
1336 in_destructor = true;
1338 trackview.editor().verbose_cursor()->hide ();
1340 note_delete_connection.disconnect ();
1342 delete _list_editor;
1344 RegionViewGoingAway (this); /* EMIT_SIGNAL */
1346 if (_active_notes) {
1350 _selection_cleared_connection.disconnect ();
1353 clear_events (false);
1356 delete _note_diff_command;
1357 delete _step_edit_cursor;
1358 delete _temporary_note_group;
1362 MidiRegionView::region_resized (const PropertyChange& what_changed)
1364 RegionView::region_resized(what_changed);
1366 if (what_changed.contains (ARDOUR::Properties::position)) {
1367 _region_relative_time_converter.set_origin_b(_region->position());
1368 set_duration(_region->length(), 0);
1369 if (_enable_display) {
1374 if (what_changed.contains (ARDOUR::Properties::start) ||
1375 what_changed.contains (ARDOUR::Properties::position)) {
1376 _source_relative_time_converter.set_origin_b (_region->position() - _region->start());
1381 MidiRegionView::reset_width_dependent_items (double pixel_width)
1383 RegionView::reset_width_dependent_items(pixel_width);
1385 if (_enable_display) {
1389 for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
1390 if ((*x)->canvas_item()->width() >= _pixel_width) {
1397 move_step_edit_cursor (_step_edit_cursor_position);
1398 set_step_edit_cursor_width (_step_edit_cursor_width);
1402 MidiRegionView::set_height (double height)
1404 double old_height = _height;
1405 RegionView::set_height(height);
1407 apply_note_range (midi_stream_view()->lowest_note(),
1408 midi_stream_view()->highest_note(),
1409 height != old_height);
1412 name_text->raise_to_top();
1415 for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
1416 (*x)->set_height (midi_stream_view()->contents_height());
1419 if (_step_edit_cursor) {
1420 _step_edit_cursor->set_y1 (midi_stream_view()->contents_height());
1425 /** Apply the current note range from the stream view
1426 * by repositioning/hiding notes as necessary
1429 MidiRegionView::apply_note_range (uint8_t min, uint8_t max, bool force)
1431 if (!_enable_display) {
1435 if (!force && _current_range_min == min && _current_range_max == max) {
1439 _current_range_min = min;
1440 _current_range_max = max;
1442 for (Events::const_iterator i = _events.begin(); i != _events.end(); ++i) {
1443 NoteBase* event = *i;
1444 boost::shared_ptr<NoteType> note (event->note());
1446 if (note->note() < _current_range_min ||
1447 note->note() > _current_range_max) {
1453 if (Note* cnote = dynamic_cast<Note*>(event)) {
1455 const double y0 = 1. + floor (midi_stream_view()->note_to_y(note->note()));
1456 const double y1 = y0 + std::max(1., floor(midi_stream_view()->note_height()) - 1.);
1461 } else if (Hit* chit = dynamic_cast<Hit*>(event)) {
1468 MidiRegionView::add_ghost (TimeAxisView& tv)
1472 double unit_position = _region->position () / samples_per_pixel;
1473 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(&tv);
1474 MidiGhostRegion* ghost;
1476 if (mtv && mtv->midi_view()) {
1477 /* if ghost is inserted into midi track, use a dedicated midi ghost canvas group
1478 to allow having midi notes on top of note lines and waveforms.
1480 ghost = new MidiGhostRegion (*mtv->midi_view(), trackview, unit_position);
1482 ghost = new MidiGhostRegion (tv, trackview, unit_position);
1485 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1486 if ((note = dynamic_cast<Note*>(*i)) != 0) {
1487 ghost->add_note(note);
1491 ghost->set_height ();
1492 ghost->set_duration (_region->length() / samples_per_pixel);
1493 ghosts.push_back (ghost);
1495 GhostRegion::CatchDeletion.connect (*this, invalidator (*this), boost::bind (&RegionView::remove_ghost, this, _1), gui_context());
1501 /** Begin tracking note state for successive calls to add_event
1504 MidiRegionView::begin_write()
1506 if (_active_notes) {
1507 delete[] _active_notes;
1509 _active_notes = new Note*[128];
1510 for (unsigned i = 0; i < 128; ++i) {
1511 _active_notes[i] = 0;
1516 /** Destroy note state for add_event
1519 MidiRegionView::end_write()
1521 delete[] _active_notes;
1523 _marked_for_selection.clear();
1524 _marked_for_velocity.clear();
1528 /** Resolve an active MIDI note (while recording).
1531 MidiRegionView::resolve_note(uint8_t note, Evoral::MusicalTime end_time)
1533 if (midi_view()->note_mode() != Sustained) {
1537 if (_active_notes && _active_notes[note]) {
1539 /* XXX is end_time really region-centric? I think so, because
1540 this is a new region that we're recording, so source zero is
1541 the same as region zero
1543 const framepos_t end_time_frames = region_beats_to_region_frames(end_time);
1545 _active_notes[note]->set_x1 (trackview.editor().sample_to_pixel(end_time_frames));
1546 _active_notes[note]->set_outline_all ();
1547 _active_notes[note] = 0;
1553 /** Extend active notes to rightmost edge of region (if length is changed)
1556 MidiRegionView::extend_active_notes()
1558 if (!_active_notes) {
1562 for (unsigned i=0; i < 128; ++i) {
1563 if (_active_notes[i]) {
1564 _active_notes[i]->set_x1(
1565 trackview.editor().sample_to_pixel(_region->position() + _region->length()));
1572 MidiRegionView::play_midi_note(boost::shared_ptr<NoteType> note)
1574 if (_no_sound_notes || !Config->get_sound_midi_notes()) {
1578 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1580 if (!route_ui || !route_ui->midi_track()) {
1584 NotePlayer* np = new NotePlayer (route_ui->midi_track ());
1588 /* NotePlayer deletes itself */
1592 MidiRegionView::start_playing_midi_note(boost::shared_ptr<NoteType> note)
1594 if (_no_sound_notes || !Config->get_sound_midi_notes()) {
1598 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1600 if (!route_ui || !route_ui->midi_track()) {
1604 delete _note_player;
1605 _note_player = new NotePlayer (route_ui->midi_track ());
1606 _note_player->add (note);
1607 _note_player->on ();
1611 MidiRegionView::start_playing_midi_chord (vector<boost::shared_ptr<NoteType> > notes)
1613 if (_no_sound_notes || !Config->get_sound_midi_notes()) {
1617 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1619 if (!route_ui || !route_ui->midi_track()) {
1623 delete _note_player;
1624 _note_player = new NotePlayer (route_ui->midi_track());
1626 for (vector<boost::shared_ptr<NoteType> >::iterator n = notes.begin(); n != notes.end(); ++n) {
1627 _note_player->add (*n);
1630 _note_player->on ();
1635 MidiRegionView::note_in_region_range (const boost::shared_ptr<NoteType> note, bool& visible) const
1637 const framepos_t note_start_frames = source_beats_to_region_frames (note->time());
1638 bool outside = (note_start_frames < 0) || (note_start_frames > _region->last_frame());
1640 visible = (note->note() >= midi_stream_view()->lowest_note()) &&
1641 (note->note() <= midi_stream_view()->highest_note());
1646 /** Update a canvas note's size from its model note.
1647 * @param ev Canvas note to update.
1648 * @param update_ghost_regions true to update the note in any ghost regions that we have, otherwise false.
1651 MidiRegionView::update_note (Note* ev, bool update_ghost_regions)
1653 boost::shared_ptr<NoteType> note = ev->note();
1654 const double x = trackview.editor().sample_to_pixel (source_beats_to_region_frames (note->time()));
1655 const double y0 = 1 + floor(midi_stream_view()->note_to_y(note->note()));
1660 /* trim note display to not overlap the end of its region */
1662 if (note->length() > 0) {
1663 const framepos_t note_end_frames = min (source_beats_to_region_frames (note->end_time()), _region->length());
1664 ev->set_x1 (trackview.editor().sample_to_pixel (note_end_frames));
1666 ev->set_x1 (trackview.editor().sample_to_pixel (_region->length()));
1669 ev->set_y1 (y0 + std::max(1., floor(midi_stream_view()->note_height()) - 1));
1671 if (!note->length()) {
1672 if (_active_notes && note->note() < 128) {
1673 // If this note is already active there's a stuck note,
1674 // finish the old note rectangle
1675 if (_active_notes[note->note()]) {
1676 Note* const old_rect = _active_notes[note->note()];
1677 boost::shared_ptr<NoteType> old_note = old_rect->note();
1678 old_rect->set_x1 (x);
1679 old_rect->set_outline_all ();
1681 _active_notes[note->note()] = ev;
1683 /* outline all but right edge */
1684 ev->set_outline_what (ArdourCanvas::Rectangle::What (
1685 ArdourCanvas::Rectangle::TOP|
1686 ArdourCanvas::Rectangle::LEFT|
1687 ArdourCanvas::Rectangle::BOTTOM));
1689 /* outline all edges */
1690 ev->set_outline_all ();
1693 if (update_ghost_regions) {
1694 for (std::vector<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
1695 MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*i);
1697 gr->update_note (ev);
1704 MidiRegionView::update_hit (Hit* ev)
1706 boost::shared_ptr<NoteType> note = ev->note();
1708 const framepos_t note_start_frames = source_beats_to_region_frames(note->time());
1709 const double x = trackview.editor().sample_to_pixel(note_start_frames);
1710 const double diamond_size = std::max(1., floor(midi_stream_view()->note_height()) - 2.);
1711 const double y = 1.5 + floor(midi_stream_view()->note_to_y(note->note())) + diamond_size * .5;
1713 ev->set_position (ArdourCanvas::Duple (x, y));
1714 ev->set_height (diamond_size);
1717 /** Add a MIDI note to the view (with length).
1719 * If in sustained mode, notes with length 0 will be considered active
1720 * notes, and resolve_note should be called when the corresponding note off
1721 * event arrives, to properly display the note.
1724 MidiRegionView::add_note(const boost::shared_ptr<NoteType> note, bool visible)
1726 NoteBase* event = 0;
1728 if (midi_view()->note_mode() == Sustained) {
1730 Note* ev_rect = new Note (*this, _note_group, note);
1732 update_note (ev_rect);
1736 MidiGhostRegion* gr;
1738 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
1739 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
1740 gr->add_note(ev_rect);
1744 } else if (midi_view()->note_mode() == Percussive) {
1746 const double diamond_size = std::max(1., floor(midi_stream_view()->note_height()) - 2.);
1748 Hit* ev_diamond = new Hit (*this, _note_group, diamond_size, note);
1750 update_hit (ev_diamond);
1759 if (_marked_for_selection.find(note) != _marked_for_selection.end()) {
1760 note_selected(event, true);
1763 if (_marked_for_velocity.find(note) != _marked_for_velocity.end()) {
1764 event->show_velocity();
1767 event->on_channel_selection_change (get_selected_channels());
1768 _events.push_back(event);
1777 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1778 MidiStreamView* const view = mtv->midi_view();
1780 view->update_note_range (note->note());
1784 MidiRegionView::step_add_note (uint8_t channel, uint8_t number, uint8_t velocity,
1785 Evoral::MusicalTime pos, Evoral::MusicalTime len)
1787 boost::shared_ptr<NoteType> new_note (new NoteType (channel, pos, len, number, velocity));
1789 /* potentially extend region to hold new note */
1791 framepos_t end_frame = source_beats_to_absolute_frames (new_note->end_time());
1792 framepos_t region_end = _region->last_frame();
1794 if (end_frame > region_end) {
1795 _region->set_length (end_frame - _region->position());
1798 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1799 MidiStreamView* const view = mtv->midi_view();
1801 view->update_note_range(new_note->note());
1803 _marked_for_selection.clear ();
1806 start_note_diff_command (_("step add"));
1807 note_diff_add_note (new_note, true, false);
1810 // last_step_edit_note = new_note;
1814 MidiRegionView::step_sustain (Evoral::MusicalTime beats)
1816 change_note_lengths (false, false, beats, false, true);
1819 /** Add a new patch change flag to the canvas.
1820 * @param patch the patch change to add
1821 * @param the text to display in the flag
1822 * @param active_channel true to display the flag as on an active channel, false to grey it out for an inactive channel.
1825 MidiRegionView::add_canvas_patch_change (MidiModel::PatchChangePtr patch, const string& displaytext, bool /*active_channel*/)
1827 framecnt_t region_frames = source_beats_to_region_frames (patch->time());
1828 const double x = trackview.editor().sample_to_pixel (region_frames);
1830 double const height = midi_stream_view()->contents_height();
1832 // CAIROCANVAS: active_channel info removed from PatcChange constructor
1833 // so we need to do something more sophisticated to keep its color
1834 // appearance (MidiPatchChangeFill/MidiPatchChangeInactiveChannelFill)
1837 boost::shared_ptr<PatchChange> patch_change = boost::shared_ptr<PatchChange>(
1838 new PatchChange(*this, group,
1845 if (patch_change->item().width() < _pixel_width) {
1846 // Show unless patch change is beyond the region bounds
1847 if (region_frames < 0 || region_frames >= _region->length()) {
1848 patch_change->hide();
1850 patch_change->show();
1853 patch_change->hide ();
1856 _patch_changes.push_back (patch_change);
1859 MIDI::Name::PatchPrimaryKey
1860 MidiRegionView::patch_change_to_patch_key (MidiModel::PatchChangePtr p)
1862 return MIDI::Name::PatchPrimaryKey (p->program(), p->bank());
1865 /// Return true iff @p pc applies to the given time on the given channel.
1867 patch_applies (const ARDOUR::MidiModel::constPatchChangePtr pc, Evoral::MusicalTime time, uint8_t channel)
1869 return pc->time() <= time && pc->channel() == channel;
1873 MidiRegionView::get_patch_key_at (Evoral::MusicalTime time, uint8_t channel, MIDI::Name::PatchPrimaryKey& key) const
1875 // The earliest event not before time
1876 MidiModel::PatchChanges::iterator i = _model->patch_change_lower_bound (time);
1878 // Go backwards until we find the latest PC for this channel, or the start
1879 while (i != _model->patch_changes().begin() &&
1880 (i == _model->patch_changes().end() ||
1881 !patch_applies(*i, time, channel))) {
1885 if (i != _model->patch_changes().end() && patch_applies(*i, time, channel)) {
1886 key.set_bank((*i)->bank());
1887 key.set_program((*i)->program ());
1895 MidiRegionView::change_patch_change (PatchChange& pc, const MIDI::Name::PatchPrimaryKey& new_patch)
1897 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("alter patch change"));
1899 if (pc.patch()->program() != new_patch.program()) {
1900 c->change_program (pc.patch (), new_patch.program());
1903 int const new_bank = new_patch.bank();
1904 if (pc.patch()->bank() != new_bank) {
1905 c->change_bank (pc.patch (), new_bank);
1908 _model->apply_command (*trackview.session(), c);
1910 _patch_changes.clear ();
1911 display_patch_changes ();
1915 MidiRegionView::change_patch_change (MidiModel::PatchChangePtr old_change, const Evoral::PatchChange<Evoral::MusicalTime> & new_change)
1917 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("alter patch change"));
1919 if (old_change->time() != new_change.time()) {
1920 c->change_time (old_change, new_change.time());
1923 if (old_change->channel() != new_change.channel()) {
1924 c->change_channel (old_change, new_change.channel());
1927 if (old_change->program() != new_change.program()) {
1928 c->change_program (old_change, new_change.program());
1931 if (old_change->bank() != new_change.bank()) {
1932 c->change_bank (old_change, new_change.bank());
1935 _model->apply_command (*trackview.session(), c);
1937 _patch_changes.clear ();
1938 display_patch_changes ();
1941 /** Add a patch change to the region.
1942 * @param t Time in frames relative to region position
1943 * @param patch Patch to add; time and channel are ignored (time is converted from t, and channel comes from
1944 * MidiTimeAxisView::get_channel_for_add())
1947 MidiRegionView::add_patch_change (framecnt_t t, Evoral::PatchChange<Evoral::MusicalTime> const & patch)
1949 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1951 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("add patch change"));
1952 c->add (MidiModel::PatchChangePtr (
1953 new Evoral::PatchChange<Evoral::MusicalTime> (
1954 absolute_frames_to_source_beats (_region->position() + t),
1955 mtv->get_channel_for_add(), patch.program(), patch.bank()
1960 _model->apply_command (*trackview.session(), c);
1962 _patch_changes.clear ();
1963 display_patch_changes ();
1967 MidiRegionView::move_patch_change (PatchChange& pc, Evoral::MusicalTime t)
1969 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("move patch change"));
1970 c->change_time (pc.patch (), t);
1971 _model->apply_command (*trackview.session(), c);
1973 _patch_changes.clear ();
1974 display_patch_changes ();
1978 MidiRegionView::delete_patch_change (PatchChange* pc)
1980 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("delete patch change"));
1981 c->remove (pc->patch ());
1982 _model->apply_command (*trackview.session(), c);
1984 _patch_changes.clear ();
1985 display_patch_changes ();
1989 MidiRegionView::step_patch (PatchChange& patch, bool bank, int delta)
1991 MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key(patch.patch());
1993 key.set_bank(key.bank() + delta);
1995 key.set_program(key.program() + delta);
1997 change_patch_change(patch, key);
2001 MidiRegionView::maybe_remove_deleted_note_from_selection (NoteBase* cne)
2003 if (_selection.empty()) {
2007 _selection.erase (cne);
2011 MidiRegionView::delete_selection()
2013 if (_selection.empty()) {
2017 start_note_diff_command (_("delete selection"));
2019 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2020 if ((*i)->selected()) {
2021 _note_diff_command->remove((*i)->note());
2031 MidiRegionView::delete_note (boost::shared_ptr<NoteType> n)
2033 start_note_diff_command (_("delete note"));
2034 _note_diff_command->remove (n);
2037 trackview.editor().verbose_cursor()->hide ();
2041 MidiRegionView::clear_selection_except (NoteBase* ev, bool signal)
2043 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2045 Selection::iterator tmp = i;
2048 (*i)->set_selected (false);
2049 (*i)->hide_velocity ();
2050 _selection.erase (i);
2058 if (!ev && _entered) {
2059 // Clearing selection entirely, ungrab keyboard
2060 Keyboard::magic_widget_drop_focus();
2061 _grabbed_keyboard = false;
2064 /* this does not change the status of this regionview w.r.t the editor
2069 SelectionCleared (this); /* EMIT SIGNAL */
2074 MidiRegionView::unique_select(NoteBase* ev)
2076 const bool selection_was_empty = _selection.empty();
2078 clear_selection_except (ev);
2080 /* don't bother with checking to see if we should remove this
2081 regionview from the editor selection, since we're about to add
2082 another note, and thus put/keep this regionview in the editor
2086 if (!ev->selected()) {
2087 add_to_selection (ev);
2088 if (selection_was_empty && _entered) {
2089 // Grab keyboard for moving notes with arrow keys
2090 Keyboard::magic_widget_grab_focus();
2091 _grabbed_keyboard = true;
2097 MidiRegionView::select_all_notes ()
2101 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2102 add_to_selection (*i);
2107 MidiRegionView::select_range (framepos_t start, framepos_t end)
2111 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2112 framepos_t t = source_beats_to_absolute_frames((*i)->note()->time());
2113 if (t >= start && t <= end) {
2114 add_to_selection (*i);
2120 MidiRegionView::invert_selection ()
2122 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2123 if ((*i)->selected()) {
2124 remove_from_selection(*i);
2126 add_to_selection (*i);
2132 MidiRegionView::select_matching_notes (uint8_t notenum, uint16_t channel_mask, bool add, bool extend)
2134 bool have_selection = !_selection.empty();
2135 uint8_t low_note = 127;
2136 uint8_t high_note = 0;
2137 MidiModel::Notes& notes (_model->notes());
2138 _optimization_iterator = _events.begin();
2140 if (extend && !have_selection) {
2144 /* scan existing selection to get note range */
2146 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2147 if ((*i)->note()->note() < low_note) {
2148 low_note = (*i)->note()->note();
2150 if ((*i)->note()->note() > high_note) {
2151 high_note = (*i)->note()->note();
2158 if (!extend && (low_note == high_note) && (high_note == notenum)) {
2159 /* only note previously selected is the one we are
2160 * reselecting. treat this as cancelling the selection.
2167 low_note = min (low_note, notenum);
2168 high_note = max (high_note, notenum);
2171 _no_sound_notes = true;
2173 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
2175 boost::shared_ptr<NoteType> note (*n);
2177 bool select = false;
2179 if (((1 << note->channel()) & channel_mask) != 0) {
2181 if ((note->note() >= low_note && note->note() <= high_note)) {
2184 } else if (note->note() == notenum) {
2190 if ((cne = find_canvas_note (note)) != 0) {
2191 // extend is false because we've taken care of it,
2192 // since it extends by time range, not pitch.
2193 note_selected (cne, add, false);
2197 add = true; // we need to add all remaining matching notes, even if the passed in value was false (for "set")
2201 _no_sound_notes = false;
2205 MidiRegionView::toggle_matching_notes (uint8_t notenum, uint16_t channel_mask)
2207 MidiModel::Notes& notes (_model->notes());
2208 _optimization_iterator = _events.begin();
2210 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
2212 boost::shared_ptr<NoteType> note (*n);
2215 if (note->note() == notenum && (((0x0001 << note->channel()) & channel_mask) != 0)) {
2216 if ((cne = find_canvas_note (note)) != 0) {
2217 if (cne->selected()) {
2218 note_deselected (cne);
2220 note_selected (cne, true, false);
2228 MidiRegionView::note_selected (NoteBase* ev, bool add, bool extend)
2231 clear_selection_except (ev);
2232 if (!_selection.empty()) {
2233 PublicEditor& editor (trackview.editor());
2234 editor.get_selection().add (this);
2240 if (!ev->selected()) {
2241 add_to_selection (ev);
2245 /* find end of latest note selected, select all between that and the start of "ev" */
2247 Evoral::MusicalTime earliest = Evoral::MaxMusicalTime;
2248 Evoral::MusicalTime latest = Evoral::MusicalTime();
2250 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2251 if ((*i)->note()->end_time() > latest) {
2252 latest = (*i)->note()->end_time();
2254 if ((*i)->note()->time() < earliest) {
2255 earliest = (*i)->note()->time();
2259 if (ev->note()->end_time() > latest) {
2260 latest = ev->note()->end_time();
2263 if (ev->note()->time() < earliest) {
2264 earliest = ev->note()->time();
2267 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2269 /* find notes entirely within OR spanning the earliest..latest range */
2271 if (((*i)->note()->time() >= earliest && (*i)->note()->end_time() <= latest) ||
2272 ((*i)->note()->time() <= earliest && (*i)->note()->end_time() >= latest)) {
2273 add_to_selection (*i);
2281 MidiRegionView::note_deselected(NoteBase* ev)
2283 remove_from_selection (ev);
2287 MidiRegionView::update_drag_selection(framepos_t start, framepos_t end, double gy0, double gy1, bool extend)
2289 PublicEditor& editor = trackview.editor();
2291 // Convert to local coordinates
2292 const framepos_t p = _region->position();
2293 const double y = midi_view()->y_position();
2294 const double x0 = editor.sample_to_pixel(max((framepos_t)0, start - p));
2295 const double x1 = editor.sample_to_pixel(max((framepos_t)0, end - p));
2296 const double y0 = max(0.0, gy0 - y);
2297 const double y1 = max(0.0, gy1 - y);
2299 // TODO: Make this faster by storing the last updated selection rect, and only
2300 // adjusting things that are in the area that appears/disappeared.
2301 // We probably need a tree to be able to find events in O(log(n)) time.
2303 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2304 if ((*i)->x0() < x1 && (*i)->x1() > x0 && (*i)->y0() < y1 && (*i)->y1() > y0) {
2305 // Rectangles intersect
2306 if (!(*i)->selected()) {
2307 add_to_selection (*i);
2309 } else if ((*i)->selected() && !extend) {
2310 // Rectangles do not intersect
2311 remove_from_selection (*i);
2315 typedef RouteTimeAxisView::AutomationTracks ATracks;
2316 typedef std::list<Selectable*> Selectables;
2318 /* Add control points to selection. */
2319 const ATracks& atracks = midi_view()->automation_tracks();
2320 Selectables selectables;
2321 editor.get_selection().clear_points();
2322 for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) {
2323 a->second->get_selectables(start, end, gy0, gy1, selectables);
2324 for (Selectables::const_iterator s = selectables.begin(); s != selectables.end(); ++s) {
2325 ControlPoint* cp = dynamic_cast<ControlPoint*>(*s);
2327 editor.get_selection().add(cp);
2330 a->second->set_selected_points(editor.get_selection().points);
2335 MidiRegionView::update_vertical_drag_selection (double y1, double y2, bool extend)
2341 // TODO: Make this faster by storing the last updated selection rect, and only
2342 // adjusting things that are in the area that appears/disappeared.
2343 // We probably need a tree to be able to find events in O(log(n)) time.
2345 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2346 if (((*i)->y1() >= y1 && (*i)->y1() <= y2)) {
2347 // within y- (note-) range
2348 if (!(*i)->selected()) {
2349 add_to_selection (*i);
2351 } else if ((*i)->selected() && !extend) {
2352 remove_from_selection (*i);
2358 MidiRegionView::remove_from_selection (NoteBase* ev)
2360 Selection::iterator i = _selection.find (ev);
2362 if (i != _selection.end()) {
2363 _selection.erase (i);
2364 if (_selection.empty() && _grabbed_keyboard) {
2366 Keyboard::magic_widget_drop_focus();
2367 _grabbed_keyboard = false;
2371 ev->set_selected (false);
2372 ev->hide_velocity ();
2374 if (_selection.empty()) {
2375 PublicEditor& editor (trackview.editor());
2376 editor.get_selection().remove (this);
2381 MidiRegionView::add_to_selection (NoteBase* ev)
2383 const bool selection_was_empty = _selection.empty();
2385 if (_selection.insert (ev).second) {
2386 ev->set_selected (true);
2387 start_playing_midi_note ((ev)->note());
2388 if (selection_was_empty && _entered) {
2389 // Grab keyboard for moving notes with arrow keys
2390 Keyboard::magic_widget_grab_focus();
2391 _grabbed_keyboard = true;
2395 if (selection_was_empty) {
2396 PublicEditor& editor (trackview.editor());
2397 editor.get_selection().add (this);
2402 MidiRegionView::move_selection(double dx, double dy, double cumulative_dy)
2404 typedef vector<boost::shared_ptr<NoteType> > PossibleChord;
2405 PossibleChord to_play;
2406 Evoral::MusicalTime earliest = Evoral::MaxMusicalTime;
2408 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2409 if ((*i)->note()->time() < earliest) {
2410 earliest = (*i)->note()->time();
2414 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2415 if ((*i)->note()->time() == earliest) {
2416 to_play.push_back ((*i)->note());
2418 (*i)->move_event(dx, dy);
2421 if (dy && !_selection.empty() && !_no_sound_notes && Config->get_sound_midi_notes()) {
2423 if (to_play.size() > 1) {
2425 PossibleChord shifted;
2427 for (PossibleChord::iterator n = to_play.begin(); n != to_play.end(); ++n) {
2428 boost::shared_ptr<NoteType> moved_note (new NoteType (**n));
2429 moved_note->set_note (moved_note->note() + cumulative_dy);
2430 shifted.push_back (moved_note);
2433 start_playing_midi_chord (shifted);
2435 } else if (!to_play.empty()) {
2437 boost::shared_ptr<NoteType> moved_note (new NoteType (*to_play.front()));
2438 moved_note->set_note (moved_note->note() + cumulative_dy);
2439 start_playing_midi_note (moved_note);
2445 MidiRegionView::note_dropped(NoteBase *, frameoffset_t dt, int8_t dnote)
2447 uint8_t lowest_note_in_selection = 127;
2448 uint8_t highest_note_in_selection = 0;
2449 uint8_t highest_note_difference = 0;
2451 // find highest and lowest notes first
2453 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2454 uint8_t pitch = (*i)->note()->note();
2455 lowest_note_in_selection = std::min(lowest_note_in_selection, pitch);
2456 highest_note_in_selection = std::max(highest_note_in_selection, pitch);
2460 cerr << "dnote: " << (int) dnote << endl;
2461 cerr << "lowest note (streamview): " << int(midi_stream_view()->lowest_note())
2462 << " highest note (streamview): " << int(midi_stream_view()->highest_note()) << endl;
2463 cerr << "lowest note (selection): " << int(lowest_note_in_selection) << " highest note(selection): "
2464 << int(highest_note_in_selection) << endl;
2465 cerr << "selection size: " << _selection.size() << endl;
2466 cerr << "Highest note in selection: " << (int) highest_note_in_selection << endl;
2469 // Make sure the note pitch does not exceed the MIDI standard range
2470 if (highest_note_in_selection + dnote > 127) {
2471 highest_note_difference = highest_note_in_selection - 127;
2474 start_note_diff_command (_("move notes"));
2476 for (Selection::iterator i = _selection.begin(); i != _selection.end() ; ++i) {
2478 framepos_t new_frames = source_beats_to_absolute_frames ((*i)->note()->time()) + dt;
2479 Evoral::MusicalTime new_time = absolute_frames_to_source_beats (new_frames);
2485 note_diff_add_change (*i, MidiModel::NoteDiffCommand::StartTime, new_time);
2487 uint8_t original_pitch = (*i)->note()->note();
2488 uint8_t new_pitch = original_pitch + dnote - highest_note_difference;
2490 // keep notes in standard midi range
2491 clamp_to_0_127(new_pitch);
2493 lowest_note_in_selection = std::min(lowest_note_in_selection, new_pitch);
2494 highest_note_in_selection = std::max(highest_note_in_selection, new_pitch);
2496 note_diff_add_change (*i, MidiModel::NoteDiffCommand::NoteNumber, new_pitch);
2501 // care about notes being moved beyond the upper/lower bounds on the canvas
2502 if (lowest_note_in_selection < midi_stream_view()->lowest_note() ||
2503 highest_note_in_selection > midi_stream_view()->highest_note()) {
2504 midi_stream_view()->set_note_range(MidiStreamView::ContentsRange);
2508 /** @param x Pixel relative to the region position.
2509 * @return Snapped frame relative to the region position.
2512 MidiRegionView::snap_pixel_to_sample(double x)
2514 PublicEditor& editor (trackview.editor());
2515 return snap_frame_to_frame (editor.pixel_to_sample (x));
2518 /** @param x Pixel relative to the region position.
2519 * @return Snapped pixel relative to the region position.
2522 MidiRegionView::snap_to_pixel(double x)
2524 return (double) trackview.editor().sample_to_pixel(snap_pixel_to_sample(x));
2528 MidiRegionView::get_position_pixels()
2530 framepos_t region_frame = get_position();
2531 return trackview.editor().sample_to_pixel(region_frame);
2535 MidiRegionView::get_end_position_pixels()
2537 framepos_t frame = get_position() + get_duration ();
2538 return trackview.editor().sample_to_pixel(frame);
2542 MidiRegionView::source_beats_to_absolute_frames(Evoral::MusicalTime beats) const
2544 /* the time converter will return the frame corresponding to `beats'
2545 relative to the start of the source. The start of the source
2546 is an implied position given by region->position - region->start
2548 const framepos_t source_start = _region->position() - _region->start();
2549 return source_start + _source_relative_time_converter.to (beats);
2553 MidiRegionView::absolute_frames_to_source_beats(framepos_t frames) const
2555 /* the `frames' argument needs to be converted into a frame count
2556 relative to the start of the source before being passed in to the
2559 const framepos_t source_start = _region->position() - _region->start();
2560 return _source_relative_time_converter.from (frames - source_start);
2564 MidiRegionView::region_beats_to_region_frames(Evoral::MusicalTime beats) const
2566 return _region_relative_time_converter.to(beats);
2570 MidiRegionView::region_frames_to_region_beats(framepos_t frames) const
2572 return _region_relative_time_converter.from(frames);
2576 MidiRegionView::begin_resizing (bool /*at_front*/)
2578 _resize_data.clear();
2580 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2581 Note *note = dynamic_cast<Note*> (*i);
2583 // only insert CanvasNotes into the map
2585 NoteResizeData *resize_data = new NoteResizeData();
2586 resize_data->note = note;
2588 // create a new SimpleRect from the note which will be the resize preview
2589 ArdourCanvas::Rectangle *resize_rect = new ArdourCanvas::Rectangle (_note_group,
2590 ArdourCanvas::Rect (note->x0(), note->y0(), note->x0(), note->y1()));
2592 // calculate the colors: get the color settings
2593 uint32_t fill_color = UINT_RGBA_CHANGE_A(
2594 ARDOUR_UI::config()->color ("midi note selected"),
2597 // make the resize preview notes more transparent and bright
2598 fill_color = UINT_INTERPOLATE(fill_color, 0xFFFFFF40, 0.5);
2600 // calculate color based on note velocity
2601 resize_rect->set_fill_color (UINT_INTERPOLATE(
2602 NoteBase::meter_style_fill_color(note->note()->velocity(), note->selected()),
2606 resize_rect->set_outline_color (NoteBase::calculate_outline (
2607 ARDOUR_UI::config()->color ("midi note selected")));
2609 resize_data->resize_rect = resize_rect;
2610 _resize_data.push_back(resize_data);
2615 /** Update resizing notes while user drags.
2616 * @param primary `primary' note for the drag; ie the one that is used as the reference in non-relative mode.
2617 * @param at_front which end of the note (true == note on, false == note off)
2618 * @param delta_x change in mouse position since the start of the drag
2619 * @param relative true if relative resizing is taking place, false if absolute resizing. This only makes
2620 * a difference when multiple notes are being resized; in relative mode, each note's length is changed by the
2621 * amount of the drag. In non-relative mode, all selected notes are set to have the same start or end point
2622 * as the \a primary note.
2625 MidiRegionView::update_resizing (NoteBase* primary, bool at_front, double delta_x, bool relative)
2627 bool cursor_set = false;
2629 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2630 ArdourCanvas::Rectangle* resize_rect = (*i)->resize_rect;
2631 Note* canvas_note = (*i)->note;
2636 current_x = canvas_note->x0() + delta_x;
2638 current_x = primary->x0() + delta_x;
2642 current_x = canvas_note->x1() + delta_x;
2644 current_x = primary->x1() + delta_x;
2648 if (current_x < 0) {
2649 // This works even with snapping because RegionView::snap_frame_to_frame()
2650 // snaps forward if the snapped sample is before the beginning of the region
2653 if (current_x > trackview.editor().sample_to_pixel(_region->length())) {
2654 current_x = trackview.editor().sample_to_pixel(_region->length());
2658 resize_rect->set_x0 (snap_to_pixel(current_x));
2659 resize_rect->set_x1 (canvas_note->x1());
2661 resize_rect->set_x1 (snap_to_pixel(current_x));
2662 resize_rect->set_x0 (canvas_note->x0());
2666 const double snapped_x = snap_pixel_to_sample (current_x);
2667 Evoral::MusicalTime beats = region_frames_to_region_beats (snapped_x);
2668 Evoral::MusicalTime len = Evoral::MusicalTime();
2671 if (beats < canvas_note->note()->end_time()) {
2672 len = canvas_note->note()->time() - beats;
2673 len += canvas_note->note()->length();
2676 if (beats >= canvas_note->note()->time()) {
2677 len = beats - canvas_note->note()->time();
2682 snprintf (buf, sizeof (buf), "%.3g beats", len.to_double());
2683 show_verbose_cursor (buf, 0, 0);
2692 /** Finish resizing notes when the user releases the mouse button.
2693 * Parameters the same as for \a update_resizing().
2696 MidiRegionView::commit_resizing (NoteBase* primary, bool at_front, double delta_x, bool relative)
2698 start_note_diff_command (_("resize notes"));
2700 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2701 Note* canvas_note = (*i)->note;
2702 ArdourCanvas::Rectangle* resize_rect = (*i)->resize_rect;
2704 /* Get the new x position for this resize, which is in pixels relative
2705 * to the region position.
2712 current_x = canvas_note->x0() + delta_x;
2714 current_x = primary->x0() + delta_x;
2718 current_x = canvas_note->x1() + delta_x;
2720 current_x = primary->x1() + delta_x;
2724 if (current_x < 0) {
2727 if (current_x > trackview.editor().sample_to_pixel(_region->length())) {
2728 current_x = trackview.editor().sample_to_pixel(_region->length());
2731 /* Convert that to a frame within the source */
2732 current_x = snap_pixel_to_sample (current_x) + _region->start ();
2734 /* and then to beats */
2735 const Evoral::MusicalTime x_beats = region_frames_to_region_beats (current_x);
2737 if (at_front && x_beats < canvas_note->note()->end_time()) {
2738 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::StartTime, x_beats);
2740 Evoral::MusicalTime len = canvas_note->note()->time() - x_beats;
2741 len += canvas_note->note()->length();
2744 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
2749 const Evoral::MusicalTime len = x_beats - canvas_note->note()->time();
2752 /* XXX convert to beats */
2753 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
2761 _resize_data.clear();
2766 MidiRegionView::abort_resizing ()
2768 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2769 delete (*i)->resize_rect;
2773 _resize_data.clear ();
2777 MidiRegionView::change_note_velocity(NoteBase* event, int8_t velocity, bool relative)
2779 uint8_t new_velocity;
2782 new_velocity = event->note()->velocity() + velocity;
2783 clamp_to_0_127(new_velocity);
2785 new_velocity = velocity;
2788 event->set_selected (event->selected()); // change color
2790 note_diff_add_change (event, MidiModel::NoteDiffCommand::Velocity, new_velocity);
2794 MidiRegionView::change_note_note (NoteBase* event, int8_t note, bool relative)
2799 new_note = event->note()->note() + note;
2804 clamp_to_0_127 (new_note);
2805 note_diff_add_change (event, MidiModel::NoteDiffCommand::NoteNumber, new_note);
2809 MidiRegionView::trim_note (NoteBase* event, Evoral::MusicalTime front_delta, Evoral::MusicalTime end_delta)
2811 bool change_start = false;
2812 bool change_length = false;
2813 Evoral::MusicalTime new_start;
2814 Evoral::MusicalTime new_length;
2816 /* NOTE: the semantics of the two delta arguments are slightly subtle:
2818 front_delta: if positive - move the start of the note later in time (shortening it)
2819 if negative - move the start of the note earlier in time (lengthening it)
2821 end_delta: if positive - move the end of the note later in time (lengthening it)
2822 if negative - move the end of the note earlier in time (shortening it)
2825 if (!!front_delta) {
2826 if (front_delta < 0) {
2828 if (event->note()->time() < -front_delta) {
2829 new_start = Evoral::MusicalTime();
2831 new_start = event->note()->time() + front_delta; // moves earlier
2834 /* start moved toward zero, so move the end point out to where it used to be.
2835 Note that front_delta is negative, so this increases the length.
2838 new_length = event->note()->length() - front_delta;
2839 change_start = true;
2840 change_length = true;
2844 Evoral::MusicalTime new_pos = event->note()->time() + front_delta;
2846 if (new_pos < event->note()->end_time()) {
2847 new_start = event->note()->time() + front_delta;
2848 /* start moved toward the end, so move the end point back to where it used to be */
2849 new_length = event->note()->length() - front_delta;
2850 change_start = true;
2851 change_length = true;
2858 bool can_change = true;
2859 if (end_delta < 0) {
2860 if (event->note()->length() < -end_delta) {
2866 new_length = event->note()->length() + end_delta;
2867 change_length = true;
2872 note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_start);
2875 if (change_length) {
2876 note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, new_length);
2881 MidiRegionView::change_note_channel (NoteBase* event, int8_t chn, bool relative)
2883 uint8_t new_channel;
2887 if (event->note()->channel() < -chn) {
2890 new_channel = event->note()->channel() + chn;
2893 new_channel = event->note()->channel() + chn;
2896 new_channel = (uint8_t) chn;
2899 note_diff_add_change (event, MidiModel::NoteDiffCommand::Channel, new_channel);
2903 MidiRegionView::change_note_time (NoteBase* event, Evoral::MusicalTime delta, bool relative)
2905 Evoral::MusicalTime new_time;
2909 if (event->note()->time() < -delta) {
2910 new_time = Evoral::MusicalTime();
2912 new_time = event->note()->time() + delta;
2915 new_time = event->note()->time() + delta;
2921 note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_time);
2925 MidiRegionView::change_note_length (NoteBase* event, Evoral::MusicalTime t)
2927 note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, t);
2931 MidiRegionView::change_velocities (bool up, bool fine, bool allow_smush, bool all_together)
2936 if (_selection.empty()) {
2951 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2952 if ((*i)->note()->velocity() < -delta || (*i)->note()->velocity() + delta > 127) {
2958 start_note_diff_command (_("change velocities"));
2960 for (Selection::iterator i = _selection.begin(); i != _selection.end();) {
2961 Selection::iterator next = i;
2965 if (i == _selection.begin()) {
2966 change_note_velocity (*i, delta, true);
2967 value = (*i)->note()->velocity() + delta;
2969 change_note_velocity (*i, value, false);
2973 change_note_velocity (*i, delta, true);
2982 if (!_selection.empty()) {
2984 snprintf (buf, sizeof (buf), "Vel %d",
2985 (int) (*_selection.begin())->note()->velocity());
2986 show_verbose_cursor (buf, 10, 10);
2992 MidiRegionView::transpose (bool up, bool fine, bool allow_smush)
2994 if (_selection.empty()) {
3011 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3013 if ((int8_t) (*i)->note()->note() + delta <= 0) {
3017 if ((int8_t) (*i)->note()->note() + delta > 127) {
3024 start_note_diff_command (_("transpose"));
3026 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3027 Selection::iterator next = i;
3029 change_note_note (*i, delta, true);
3037 MidiRegionView::change_note_lengths (bool fine, bool shorter, Evoral::MusicalTime delta, bool start, bool end)
3041 delta = Evoral::MusicalTime(1.0/128.0);
3043 /* grab the current grid distance */
3044 delta = get_grid_beats(_region->position());
3052 start_note_diff_command (_("change note lengths"));
3054 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3055 Selection::iterator next = i;
3058 /* note the negation of the delta for start */
3061 (start ? -delta : Evoral::MusicalTime()),
3062 (end ? delta : Evoral::MusicalTime()));
3071 MidiRegionView::nudge_notes (bool forward, bool fine)
3073 if (_selection.empty()) {
3077 /* pick a note as the point along the timeline to get the nudge distance.
3078 its not necessarily the earliest note, so we may want to pull the notes out
3079 into a vector and sort before using the first one.
3082 const framepos_t ref_point = source_beats_to_absolute_frames ((*(_selection.begin()))->note()->time());
3083 Evoral::MusicalTime delta;
3087 /* non-fine, move by 1 bar regardless of snap */
3088 delta = Evoral::MusicalTime(trackview.session()->tempo_map().meter_at(ref_point).divisions_per_bar());
3090 } else if (trackview.editor().snap_mode() == Editing::SnapOff) {
3092 /* grid is off - use nudge distance */
3095 const framecnt_t distance = trackview.editor().get_nudge_distance (ref_point, unused);
3096 delta = region_frames_to_region_beats (fabs ((double)distance));
3102 framepos_t next_pos = ref_point;
3105 if (max_framepos - 1 < next_pos) {
3109 if (next_pos == 0) {
3115 trackview.editor().snap_to (next_pos, (forward ? RoundUpAlways : RoundDownAlways), false);
3116 const framecnt_t distance = ref_point - next_pos;
3117 delta = region_frames_to_region_beats (fabs ((double)distance));
3128 start_note_diff_command (_("nudge"));
3130 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3131 Selection::iterator next = i;
3133 change_note_time (*i, delta, true);
3141 MidiRegionView::change_channel(uint8_t channel)
3143 start_note_diff_command(_("change channel"));
3144 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3145 note_diff_add_change (*i, MidiModel::NoteDiffCommand::Channel, channel);
3153 MidiRegionView::note_entered(NoteBase* ev)
3155 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3157 pre_note_enter_cursor = editor->get_canvas_cursor ();
3159 if (_mouse_state == SelectTouchDragging) {
3160 note_selected (ev, true);
3163 show_verbose_cursor (ev->note ());
3167 MidiRegionView::note_left (NoteBase*)
3169 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3171 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3172 (*i)->hide_velocity ();
3175 editor->verbose_cursor()->hide ();
3177 if (pre_note_enter_cursor) {
3178 editor->set_canvas_cursor (pre_note_enter_cursor);
3179 pre_note_enter_cursor = 0;
3184 MidiRegionView::patch_entered (PatchChange* p)
3187 /* XXX should get patch name if we can */
3188 s << _("Bank ") << (p->patch()->bank() + MIDI_BP_ZERO) << '\n'
3189 << _("Program ") << ((int) p->patch()->program()) + MIDI_BP_ZERO << '\n'
3190 << _("Channel ") << ((int) p->patch()->channel() + 1);
3191 show_verbose_cursor (s.str(), 10, 20);
3192 p->item().grab_focus();
3196 MidiRegionView::patch_left (PatchChange *)
3198 trackview.editor().verbose_cursor()->hide ();
3199 /* focus will transfer back via the enter-notify event sent to this
3205 MidiRegionView::sysex_entered (SysEx* p)
3209 // need a way to extract text from p->_flag->_text
3211 // show_verbose_cursor (s.str(), 10, 20);
3212 p->item().grab_focus();
3216 MidiRegionView::sysex_left (SysEx *)
3218 trackview.editor().verbose_cursor()->hide ();
3219 /* focus will transfer back via the enter-notify event sent to this
3225 MidiRegionView::note_mouse_position (float x_fraction, float /*y_fraction*/, bool can_set_cursor)
3227 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3228 Editing::MouseMode mm = editor->current_mouse_mode();
3229 bool trimmable = (mm == MouseContent || mm == MouseTimeFX || mm == MouseDraw);
3231 if (can_set_cursor) {
3232 if (trimmable && x_fraction > 0.0 && x_fraction < 0.2) {
3233 editor->set_canvas_cursor (editor->cursors()->left_side_trim);
3234 } else if (trimmable && x_fraction >= 0.8 && x_fraction < 1.0) {
3235 editor->set_canvas_cursor (editor->cursors()->right_side_trim);
3236 } else if (pre_note_enter_cursor) {
3237 editor->set_canvas_cursor (pre_note_enter_cursor);
3243 MidiRegionView::get_fill_color() const
3245 const std::string mod_name = (_dragging ? "dragging region" :
3246 trackview.editor().internal_editing() ? "editable region" :
3249 return ARDOUR_UI::config()->color_mod ("selected region base", mod_name);
3250 } else if (high_enough_for_name || !ARDOUR_UI::config()->get_color_regions_using_track_color()) {
3251 return ARDOUR_UI::config()->color_mod ("midi frame base", mod_name);
3253 return ARDOUR_UI::config()->color_mod (fill_color, mod_name);
3257 MidiRegionView::midi_channel_mode_changed ()
3259 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3260 uint16_t mask = mtv->midi_track()->get_playback_channel_mask();
3261 ChannelMode mode = mtv->midi_track()->get_playback_channel_mode ();
3263 if (mode == ForceChannel) {
3264 mask = 0xFFFF; // Show all notes as active (below)
3267 // Update notes for selection
3268 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3269 (*i)->on_channel_selection_change (mask);
3272 _patch_changes.clear ();
3273 display_patch_changes ();
3277 MidiRegionView::instrument_settings_changed ()
3283 MidiRegionView::cut_copy_clear (Editing::CutCopyOp op)
3285 if (_selection.empty()) {
3289 PublicEditor& editor (trackview.editor());
3293 /* XXX what to do ? */
3297 editor.get_cut_buffer().add (selection_as_cut_buffer());
3305 start_note_diff_command();
3307 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3314 note_diff_remove_note (*i);
3324 MidiRegionView::selection_as_cut_buffer () const
3328 for (Selection::const_iterator i = _selection.begin(); i != _selection.end(); ++i) {
3329 NoteType* n = (*i)->note().get();
3330 notes.insert (boost::shared_ptr<NoteType> (new NoteType (*n)));
3333 MidiCutBuffer* cb = new MidiCutBuffer (trackview.session());
3339 /** This method handles undo */
3341 MidiRegionView::paste (framepos_t pos, const ::Selection& selection, PasteContext& ctx)
3343 // Paste notes, if available
3344 MidiNoteSelection::const_iterator m = selection.midi_notes.get_nth(ctx.counts.n_notes());
3345 if (m != selection.midi_notes.end()) {
3346 ctx.counts.increase_n_notes();
3347 paste_internal(pos, ctx.count, ctx.times, **m);
3350 // Paste control points to automation children, if available
3351 typedef RouteTimeAxisView::AutomationTracks ATracks;
3352 const ATracks& atracks = midi_view()->automation_tracks();
3353 for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) {
3354 a->second->paste(pos, selection, ctx);
3360 /** This method handles undo */
3362 MidiRegionView::paste_internal (framepos_t pos, unsigned paste_count, float times, const MidiCutBuffer& mcb)
3368 start_note_diff_command (_("paste"));
3370 const Evoral::MusicalTime snap_beats = get_grid_beats(pos);
3371 const Evoral::MusicalTime first_time = (*mcb.notes().begin())->time();
3372 const Evoral::MusicalTime last_time = (*mcb.notes().rbegin())->end_time();
3373 const Evoral::MusicalTime duration = last_time - first_time;
3374 const Evoral::MusicalTime snap_duration = duration.snap_to(snap_beats);
3375 const Evoral::MusicalTime paste_offset = snap_duration * paste_count;
3376 const Evoral::MusicalTime pos_beats = absolute_frames_to_source_beats(pos) + paste_offset;
3377 Evoral::MusicalTime end_point = Evoral::MusicalTime();
3379 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste data spans from %1 to %2 (%3) ; paste pos beats = %4 (based on %5 - %6)\n",
3382 duration, pos, _region->position(),
3387 for (int n = 0; n < (int) times; ++n) {
3389 for (Notes::const_iterator i = mcb.notes().begin(); i != mcb.notes().end(); ++i) {
3391 boost::shared_ptr<NoteType> copied_note (new NoteType (*((*i).get())));
3392 copied_note->set_time (pos_beats + copied_note->time() - first_time);
3394 /* make all newly added notes selected */
3396 note_diff_add_note (copied_note, true);
3397 end_point = copied_note->end_time();
3401 /* if we pasted past the current end of the region, extend the region */
3403 framepos_t end_frame = source_beats_to_absolute_frames (end_point);
3404 framepos_t region_end = _region->position() + _region->length() - 1;
3406 if (end_frame > region_end) {
3408 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste extended region from %1 to %2\n", region_end, end_frame));
3410 _region->clear_changes ();
3411 _region->set_length (end_frame - _region->position());
3412 trackview.session()->add_command (new StatefulDiffCommand (_region));
3418 struct EventNoteTimeEarlyFirstComparator {
3419 bool operator() (NoteBase* a, NoteBase* b) {
3420 return a->note()->time() < b->note()->time();
3425 MidiRegionView::time_sort_events ()
3427 if (!_sort_needed) {
3431 EventNoteTimeEarlyFirstComparator cmp;
3434 _sort_needed = false;
3438 MidiRegionView::goto_next_note (bool add_to_selection)
3440 bool use_next = false;
3442 if (_events.back()->selected()) {
3446 time_sort_events ();
3448 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3449 uint16_t const channel_mask = mtv->midi_track()->get_playback_channel_mask();
3451 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3452 if ((*i)->selected()) {
3455 } else if (use_next) {
3456 if (channel_mask & (1 << (*i)->note()->channel())) {
3457 if (!add_to_selection) {
3460 note_selected (*i, true, false);
3467 /* use the first one */
3469 if (!_events.empty() && (channel_mask & (1 << _events.front()->note()->channel ()))) {
3470 unique_select (_events.front());
3475 MidiRegionView::goto_previous_note (bool add_to_selection)
3477 bool use_next = false;
3479 if (_events.front()->selected()) {
3483 time_sort_events ();
3485 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3486 uint16_t const channel_mask = mtv->midi_track()->get_playback_channel_mask ();
3488 for (Events::reverse_iterator i = _events.rbegin(); i != _events.rend(); ++i) {
3489 if ((*i)->selected()) {
3492 } else if (use_next) {
3493 if (channel_mask & (1 << (*i)->note()->channel())) {
3494 if (!add_to_selection) {
3497 note_selected (*i, true, false);
3504 /* use the last one */
3506 if (!_events.empty() && (channel_mask & (1 << (*_events.rbegin())->note()->channel ()))) {
3507 unique_select (*(_events.rbegin()));
3512 MidiRegionView::selection_as_notelist (Notes& selected, bool allow_all_if_none_selected)
3514 bool had_selected = false;
3516 time_sort_events ();
3518 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3519 if ((*i)->selected()) {
3520 selected.insert ((*i)->note());
3521 had_selected = true;
3525 if (allow_all_if_none_selected && !had_selected) {
3526 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3527 selected.insert ((*i)->note());
3533 MidiRegionView::update_ghost_note (double x, double y)
3535 x = std::max(0.0, x);
3537 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3542 _note_group->canvas_to_item (x, y);
3544 PublicEditor& editor = trackview.editor ();
3546 framepos_t const unsnapped_frame = editor.pixel_to_sample (x);
3547 framecnt_t grid_frames;
3548 framepos_t const f = snap_frame_to_grid_underneath (unsnapped_frame, grid_frames);
3550 /* use region_frames... because we are converting a delta within the region
3553 const Evoral::MusicalTime length = get_grid_beats(unsnapped_frame);
3555 /* note that this sets the time of the ghost note in beats relative to
3556 the start of the source; that is how all note times are stored.
3558 _ghost_note->note()->set_time (
3559 std::max(Evoral::MusicalTime(),
3560 absolute_frames_to_source_beats (f + _region->position ())));
3561 _ghost_note->note()->set_length (length);
3562 _ghost_note->note()->set_note (midi_stream_view()->y_to_note (y));
3563 _ghost_note->note()->set_channel (mtv->get_channel_for_add ());
3565 /* the ghost note does not appear in ghost regions, so pass false in here */
3566 update_note (_ghost_note, false);
3568 show_verbose_cursor (_ghost_note->note ());
3572 MidiRegionView::create_ghost_note (double x, double y)
3574 remove_ghost_note ();
3576 boost::shared_ptr<NoteType> g (new NoteType);
3577 _ghost_note = new Note (*this, _note_group, g);
3578 _ghost_note->set_ignore_events (true);
3579 _ghost_note->set_outline_color (0x000000aa);
3580 update_ghost_note (x, y);
3581 _ghost_note->show ();
3583 show_verbose_cursor (_ghost_note->note ());
3587 MidiRegionView::remove_ghost_note ()
3594 MidiRegionView::snap_changed ()
3600 create_ghost_note (_last_ghost_x, _last_ghost_y);
3604 MidiRegionView::drop_down_keys ()
3606 _mouse_state = None;
3610 MidiRegionView::maybe_select_by_position (GdkEventButton* ev, double /*x*/, double y)
3612 /* XXX: This is dead code. What was it for? */
3614 double note = midi_stream_view()->y_to_note(y);
3616 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3618 uint16_t chn_mask = mtv->midi_track()->get_playback_channel_mask();
3620 if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
3621 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchGreaterThanOrEqual, (uint8_t) floor (note), chn_mask);
3622 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
3623 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchLessThanOrEqual, (uint8_t) floor (note), chn_mask);
3628 bool add_mrv_selection = false;
3630 if (_selection.empty()) {
3631 add_mrv_selection = true;
3634 for (Events::iterator i = e.begin(); i != e.end(); ++i) {
3635 if (_selection.insert (*i).second) {
3636 (*i)->set_selected (true);
3640 if (add_mrv_selection) {
3641 PublicEditor& editor (trackview.editor());
3642 editor.get_selection().add (this);
3647 MidiRegionView::color_handler ()
3649 RegionView::color_handler ();
3651 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3652 (*i)->set_selected ((*i)->selected()); // will change color
3655 /* XXX probably more to do here */
3659 MidiRegionView::enable_display (bool yn)
3661 RegionView::enable_display (yn);
3668 MidiRegionView::show_step_edit_cursor (Evoral::MusicalTime pos)
3670 if (_step_edit_cursor == 0) {
3671 ArdourCanvas::Item* const group = get_canvas_group();
3673 _step_edit_cursor = new ArdourCanvas::Rectangle (group);
3674 _step_edit_cursor->set_y0 (0);
3675 _step_edit_cursor->set_y1 (midi_stream_view()->contents_height());
3676 _step_edit_cursor->set_fill_color (RGBA_TO_UINT (45,0,0,90));
3677 _step_edit_cursor->set_outline_color (RGBA_TO_UINT (85,0,0,90));
3680 move_step_edit_cursor (pos);
3681 _step_edit_cursor->show ();
3685 MidiRegionView::move_step_edit_cursor (Evoral::MusicalTime pos)
3687 _step_edit_cursor_position = pos;
3689 if (_step_edit_cursor) {
3690 double pixel = trackview.editor().sample_to_pixel (region_beats_to_region_frames (pos));
3691 _step_edit_cursor->set_x0 (pixel);
3692 set_step_edit_cursor_width (_step_edit_cursor_width);
3697 MidiRegionView::hide_step_edit_cursor ()
3699 if (_step_edit_cursor) {
3700 _step_edit_cursor->hide ();
3705 MidiRegionView::set_step_edit_cursor_width (Evoral::MusicalTime beats)
3707 _step_edit_cursor_width = beats;
3709 if (_step_edit_cursor) {
3710 _step_edit_cursor->set_x1 (_step_edit_cursor->x0() + trackview.editor().sample_to_pixel (region_beats_to_region_frames (beats)));
3714 /** Called when a diskstream on our track has received some data. Update the view, if applicable.
3715 * @param w Source that the data will end up in.
3718 MidiRegionView::data_recorded (boost::weak_ptr<MidiSource> w)
3720 if (!_active_notes) {
3721 /* we aren't actively being recorded to */
3725 boost::shared_ptr<MidiSource> src = w.lock ();
3726 if (!src || src != midi_region()->midi_source()) {
3727 /* recorded data was not destined for our source */
3731 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (&trackview);
3733 boost::shared_ptr<MidiBuffer> buf = mtv->midi_track()->get_gui_feed_buffer ();
3735 framepos_t back = max_framepos;
3737 for (MidiBuffer::iterator i = buf->begin(); i != buf->end(); ++i) {
3738 Evoral::MIDIEvent<MidiBuffer::TimeType> const ev (*i, false);
3740 if (ev.is_channel_event()) {
3741 if (get_channel_mode() == FilterChannels) {
3742 if (((uint16_t(1) << ev.channel()) & get_selected_channels()) == 0) {
3748 /* convert from session frames to source beats */
3749 Evoral::MusicalTime const time_beats = _source_relative_time_converter.from(ev.time());
3751 if (ev.type() == MIDI_CMD_NOTE_ON) {
3752 boost::shared_ptr<NoteType> note (
3753 new NoteType (ev.channel(), time_beats, Evoral::MusicalTime(), ev.note(), ev.velocity()));
3755 add_note (note, true);
3757 /* fix up our note range */
3758 if (ev.note() < _current_range_min) {
3759 midi_stream_view()->apply_note_range (ev.note(), _current_range_max, true);
3760 } else if (ev.note() > _current_range_max) {
3761 midi_stream_view()->apply_note_range (_current_range_min, ev.note(), true);
3764 } else if (ev.type() == MIDI_CMD_NOTE_OFF) {
3765 resolve_note (ev.note (), time_beats);
3771 midi_stream_view()->check_record_layers (region(), back);
3775 MidiRegionView::trim_front_starting ()
3777 /* Reparent the note group to the region view's parent, so that it doesn't change
3778 when the region view is trimmed.
3780 _temporary_note_group = new ArdourCanvas::Container (group->parent ());
3781 _temporary_note_group->move (group->position ());
3782 _note_group->reparent (_temporary_note_group);
3786 MidiRegionView::trim_front_ending ()
3788 _note_group->reparent (group);
3789 delete _temporary_note_group;
3790 _temporary_note_group = 0;
3792 if (_region->start() < 0) {
3793 /* Trim drag made start time -ve; fix this */
3794 midi_region()->fix_negative_start ();
3799 MidiRegionView::edit_patch_change (PatchChange* pc)
3801 PatchChangeDialog d (&_source_relative_time_converter, trackview.session(), *pc->patch (), instrument_info(), Gtk::Stock::APPLY, true);
3803 int response = d.run();
3806 case Gtk::RESPONSE_ACCEPT:
3808 case Gtk::RESPONSE_REJECT:
3809 delete_patch_change (pc);
3815 change_patch_change (pc->patch(), d.patch ());
3819 MidiRegionView::delete_sysex (SysEx* /*sysex*/)
3822 // sysyex object doesn't have a pointer to a sysex event
3823 // MidiModel::SysExDiffCommand* c = _model->new_sysex_diff_command (_("delete sysex"));
3824 // c->remove (sysex->sysex());
3825 // _model->apply_command (*trackview.session(), c);
3827 //_sys_exes.clear ();
3828 // display_sysexes();
3832 MidiRegionView::show_verbose_cursor (boost::shared_ptr<NoteType> n) const
3834 using namespace MIDI::Name;
3838 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3840 boost::shared_ptr<MasterDeviceNames> device_names(mtv->get_device_names());
3842 MIDI::Name::PatchPrimaryKey patch_key;
3843 get_patch_key_at(n->time(), n->channel(), patch_key);
3844 name = device_names->note_name(mtv->gui_property(X_("midnam-custom-device-mode")),
3847 patch_key.program(),
3853 snprintf (buf, sizeof (buf), "%d %s\nCh %d Vel %d",
3855 name.empty() ? Evoral::midi_note_name (n->note()).c_str() : name.c_str(),
3856 (int) n->channel() + 1,
3857 (int) n->velocity());
3859 show_verbose_cursor(buf, 10, 20);
3863 MidiRegionView::show_verbose_cursor (string const & text, double xoffset, double yoffset) const
3865 trackview.editor().verbose_cursor()->set (text);
3866 trackview.editor().verbose_cursor()->show ();
3867 trackview.editor().verbose_cursor()->set_offset (ArdourCanvas::Duple (xoffset, yoffset));
3870 /** @param p A session framepos.
3871 * @param grid_frames Filled in with the number of frames that a grid interval is at p.
3872 * @return p snapped to the grid subdivision underneath it.
3875 MidiRegionView::snap_frame_to_grid_underneath (framepos_t p, framecnt_t& grid_frames) const
3877 PublicEditor& editor = trackview.editor ();
3879 const Evoral::MusicalTime grid_beats = get_grid_beats(p);
3881 grid_frames = region_beats_to_region_frames (grid_beats);
3883 /* Hack so that we always snap to the note that we are over, instead of snapping
3884 to the next one if we're more than halfway through the one we're over.
3886 if (editor.snap_mode() == SnapNormal && p >= grid_frames / 2) {
3887 p -= grid_frames / 2;
3890 return snap_frame_to_frame (p);
3893 /** Called when the selection has been cleared in any MidiRegionView.
3894 * @param rv MidiRegionView that the selection was cleared in.
3897 MidiRegionView::selection_cleared (MidiRegionView* rv)
3903 /* Clear our selection in sympathy; but don't signal the fact */
3904 clear_selection (false);
3908 MidiRegionView::note_button_release ()
3910 delete _note_player;
3915 MidiRegionView::get_channel_mode () const
3917 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (&trackview);
3918 return rtav->midi_track()->get_playback_channel_mode();
3922 MidiRegionView::get_selected_channels () const
3924 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (&trackview);
3925 return rtav->midi_track()->get_playback_channel_mask();
3930 MidiRegionView::get_grid_beats(framepos_t pos) const
3932 PublicEditor& editor = trackview.editor();
3933 bool success = false;
3934 Evoral::MusicalTime beats = editor.get_grid_type_as_beats(success, pos);
3936 beats = Evoral::MusicalTime(1);