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 ());
140 MidiRegionView::MidiRegionView (ArdourCanvas::Container* parent,
141 RouteTimeAxisView& tv,
142 boost::shared_ptr<MidiRegion> r,
144 uint32_t basic_color,
145 TimeAxisViewItem::Visibility visibility)
146 : RegionView (parent, tv, r, spu, basic_color, false, visibility)
147 , _current_range_min(0)
148 , _current_range_max(0)
149 , _region_relative_time_converter(r->session().tempo_map(), r->position())
150 , _source_relative_time_converter(r->session().tempo_map(), r->position() - r->start())
152 , _note_group (new ArdourCanvas::Container (parent))
153 , _note_diff_command (0)
155 , _step_edit_cursor (0)
156 , _step_edit_cursor_width (1.0)
157 , _step_edit_cursor_position (0.0)
158 , _channel_selection_scoped_note (0)
159 , _temporary_note_group (0)
162 , _sort_needed (true)
163 , _optimization_iterator (_events.end())
165 , _no_sound_notes (false)
168 , _grabbed_keyboard (false)
170 , pre_enter_cursor (0)
171 , pre_press_cursor (0)
172 , pre_note_enter_cursor (0)
175 CANVAS_DEBUG_NAME (_note_group, string_compose ("note group for %1", get_item_name()));
176 _note_group->raise_to_top();
178 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
180 connect_to_diskstream ();
182 SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), boost::bind (&MidiRegionView::selection_cleared, this, _1), gui_context ());
186 MidiRegionView::parameter_changed (std::string const & p)
188 if (p == "display-first-midi-bank-as-zero") {
189 if (_enable_display) {
195 MidiRegionView::MidiRegionView (const MidiRegionView& other)
196 : sigc::trackable(other)
198 , _current_range_min(0)
199 , _current_range_max(0)
200 , _region_relative_time_converter(other.region_relative_time_converter())
201 , _source_relative_time_converter(other.source_relative_time_converter())
203 , _note_group (new ArdourCanvas::Container (get_canvas_group()))
204 , _note_diff_command (0)
206 , _step_edit_cursor (0)
207 , _step_edit_cursor_width (1.0)
208 , _step_edit_cursor_position (0.0)
209 , _channel_selection_scoped_note (0)
210 , _temporary_note_group (0)
213 , _sort_needed (true)
214 , _optimization_iterator (_events.end())
216 , _no_sound_notes (false)
219 , _grabbed_keyboard (false)
221 , pre_enter_cursor (0)
222 , pre_press_cursor (0)
223 , pre_note_enter_cursor (0)
229 MidiRegionView::MidiRegionView (const MidiRegionView& other, boost::shared_ptr<MidiRegion> region)
230 : RegionView (other, boost::shared_ptr<Region> (region))
231 , _current_range_min(0)
232 , _current_range_max(0)
233 , _region_relative_time_converter(other.region_relative_time_converter())
234 , _source_relative_time_converter(other.source_relative_time_converter())
236 , _note_group (new ArdourCanvas::Container (get_canvas_group()))
237 , _note_diff_command (0)
239 , _step_edit_cursor (0)
240 , _step_edit_cursor_width (1.0)
241 , _step_edit_cursor_position (0.0)
242 , _channel_selection_scoped_note (0)
243 , _temporary_note_group (0)
246 , _sort_needed (true)
247 , _optimization_iterator (_events.end())
249 , _no_sound_notes (false)
252 , _grabbed_keyboard (false)
254 , pre_enter_cursor (0)
255 , pre_press_cursor (0)
256 , pre_note_enter_cursor (0)
263 MidiRegionView::init (bool wfd)
265 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
267 NoteBase::NoteBaseDeleted.connect (note_delete_connection, MISSING_INVALIDATOR,
268 boost::bind (&MidiRegionView::maybe_remove_deleted_note_from_selection, this, _1),
272 midi_region()->midi_source(0)->load_model();
275 _model = midi_region()->midi_source(0)->model();
276 _enable_display = false;
277 _fill_color_name = "midi frame base";
279 RegionView::init (false);
281 set_height (trackview.current_height());
284 region_sync_changed ();
285 region_resized (ARDOUR::bounds_change);
290 _enable_display = true;
293 display_model (_model);
297 reset_width_dependent_items (_pixel_width);
299 group->raise_to_top();
301 midi_view()->midi_track()->PlaybackChannelModeChanged.connect (_channel_mode_changed_connection, invalidator (*this),
302 boost::bind (&MidiRegionView::midi_channel_mode_changed, this),
305 instrument_info().Changed.connect (_instrument_changed_connection, invalidator (*this),
306 boost::bind (&MidiRegionView::instrument_settings_changed, this), gui_context());
308 trackview.editor().SnapChanged.connect(snap_changed_connection, invalidator(*this),
309 boost::bind (&MidiRegionView::snap_changed, this),
312 Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&MidiRegionView::parameter_changed, this, _1), gui_context());
313 connect_to_diskstream ();
315 SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), boost::bind (&MidiRegionView::selection_cleared, this, _1), gui_context ());
319 MidiRegionView::instrument_info () const
321 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
322 return route_ui->route()->instrument_info();
325 const boost::shared_ptr<ARDOUR::MidiRegion>
326 MidiRegionView::midi_region() const
328 return boost::dynamic_pointer_cast<ARDOUR::MidiRegion>(_region);
332 MidiRegionView::connect_to_diskstream ()
334 midi_view()->midi_track()->DataRecorded.connect(
335 *this, invalidator(*this),
336 boost::bind (&MidiRegionView::data_recorded, this, _1),
341 MidiRegionView::canvas_group_event(GdkEvent* ev)
347 if (!trackview.editor().internal_editing()) {
348 // not in internal edit mode, so just act like a normal region
349 return RegionView::canvas_group_event (ev);
352 const MouseMode m = trackview.editor().current_mouse_mode();
356 case GDK_ENTER_NOTIFY:
357 _last_event_x = ev->crossing.x;
358 _last_event_y = ev->crossing.y;
359 enter_notify(&ev->crossing);
360 // set entered_regionview (among other things)
361 return RegionView::canvas_group_event (ev);
363 case GDK_LEAVE_NOTIFY:
364 _last_event_x = ev->crossing.x;
365 _last_event_y = ev->crossing.y;
366 leave_notify(&ev->crossing);
367 // reset entered_regionview (among other things)
368 return RegionView::canvas_group_event (ev);
370 case GDK_2BUTTON_PRESS:
371 // cannot use double-click to exit internal mode if single-click is being used
372 if ((m != MouseDraw) &&
374 !Keyboard::modifier_state_contains (ev->button.state, Keyboard::insert_note_modifier()))) {
375 return trackview.editor().toggle_internal_editing_from_double_click (ev);
380 if (scroll (&ev->scroll)) {
386 return key_press (&ev->key);
388 case GDK_KEY_RELEASE:
389 return key_release (&ev->key);
391 case GDK_BUTTON_PRESS:
392 return button_press (&ev->button);
394 case GDK_BUTTON_RELEASE:
395 r = button_release (&ev->button);
400 case GDK_MOTION_NOTIFY:
401 _last_event_x = ev->motion.x;
402 _last_event_y = ev->motion.y;
403 return motion (&ev->motion);
409 return RegionView::canvas_group_event (ev);
413 MidiRegionView::enter_notify (GdkEventCrossing* ev)
415 trackview.editor().MouseModeChanged.connect (
416 _mouse_mode_connection, invalidator (*this), boost::bind (&MidiRegionView::mouse_mode_changed, this), gui_context ()
426 MidiRegionView::leave_notify (GdkEventCrossing*)
428 _mouse_mode_connection.disconnect ();
437 MidiRegionView::mouse_mode_changed ()
439 if (trackview.editor().internal_editing()) {
440 // Switched in to internal editing mode while entered
443 // Switched out of internal editing mode while entered
449 MidiRegionView::enter_internal()
451 if (trackview.editor().current_mouse_mode() == MouseDraw && _mouse_state != AddDragging) {
452 // Show ghost note under pencil
453 create_ghost_note(_last_event_x, _last_event_y);
456 if (!_selection.empty()) {
457 // Grab keyboard for moving selected notes with arrow keys
458 Keyboard::magic_widget_grab_focus();
459 _grabbed_keyboard = true;
464 MidiRegionView::leave_internal()
466 trackview.editor().verbose_cursor()->hide ();
467 remove_ghost_note ();
469 if (_grabbed_keyboard) {
470 Keyboard::magic_widget_drop_focus();
471 _grabbed_keyboard = false;
476 MidiRegionView::button_press (GdkEventButton* ev)
478 if (ev->button != 1) {
482 Editor* editor = dynamic_cast<Editor *> (&trackview.editor());
483 MouseMode m = editor->current_mouse_mode();
485 if (m == MouseObject && Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())) {
486 pre_press_cursor = editor->get_canvas_cursor ();
487 editor->set_canvas_cursor (editor->cursors()->midi_pencil);
490 if (_mouse_state != SelectTouchDragging) {
492 _pressed_button = ev->button;
493 _mouse_state = Pressed;
498 _pressed_button = ev->button;
504 MidiRegionView::button_release (GdkEventButton* ev)
506 double event_x, event_y;
508 if (ev->button != 1) {
515 group->canvas_to_item (event_x, event_y);
518 PublicEditor& editor = trackview.editor ();
520 if (pre_press_cursor) {
521 dynamic_cast<Editor*>(&editor)->set_canvas_cursor (pre_press_cursor, false);
522 pre_press_cursor = 0;
525 switch (_mouse_state) {
526 case Pressed: // Clicked
528 switch (editor.current_mouse_mode()) {
530 /* no motion occured - simple click */
539 if (Keyboard::is_insert_note_event(ev)) {
541 double event_x, event_y;
545 group->canvas_to_item (event_x, event_y);
547 Evoral::MusicalTime beats = get_grid_beats(editor.pixel_to_sample(event_x));
549 /* Shorten the length by 1 tick so that we can add a new note at the next
550 grid snap without it overlapping this one.
552 beats -= Evoral::MusicalTime::tick();
554 create_note_at (editor.pixel_to_sample (event_x), event_y, beats, true);
561 Evoral::MusicalTime beats = get_grid_beats(editor.pixel_to_sample(event_x));
563 /* Shorten the length by 1 tick so that we can add a new note at the next
564 grid snap without it overlapping this one.
566 beats -= Evoral::MusicalTime::tick();
568 create_note_at (editor.pixel_to_sample (event_x), event_y, beats, true);
579 case SelectRectDragging:
581 editor.drags()->end_grab ((GdkEvent *) ev);
583 create_ghost_note (ev->x, ev->y);
595 MidiRegionView::motion (GdkEventMotion* ev)
597 PublicEditor& editor = trackview.editor ();
599 if (!_ghost_note && editor.current_mouse_mode() == MouseObject &&
600 Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier()) &&
601 _mouse_state != AddDragging) {
603 create_ghost_note (ev->x, ev->y);
605 } else if (_ghost_note && editor.current_mouse_mode() == MouseObject &&
606 Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())) {
608 update_ghost_note (ev->x, ev->y);
610 } else if (_ghost_note && editor.current_mouse_mode() == MouseObject) {
612 remove_ghost_note ();
613 editor.verbose_cursor()->hide ();
615 } else if (_ghost_note && editor.current_mouse_mode() == MouseDraw) {
617 update_ghost_note (ev->x, ev->y);
620 /* any motion immediately hides velocity text that may have been visible */
622 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
623 (*i)->hide_velocity ();
626 switch (_mouse_state) {
629 if (_pressed_button == 1) {
631 MouseMode m = editor.current_mouse_mode();
633 if (m == MouseDraw || (m == MouseObject && Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier()))) {
634 editor.drags()->set (new NoteCreateDrag (dynamic_cast<Editor *> (&editor), group, this), (GdkEvent *) ev);
635 _mouse_state = AddDragging;
636 remove_ghost_note ();
637 editor.verbose_cursor()->hide ();
639 } else if (m == MouseObject) {
640 editor.drags()->set (new MidiRubberbandSelectDrag (dynamic_cast<Editor *> (&editor), this), (GdkEvent *) ev);
641 if (!Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
644 _mouse_state = SelectRectDragging;
646 } else if (m == MouseRange) {
647 editor.drags()->set (new MidiVerticalSelectDrag (dynamic_cast<Editor *> (&editor), this), (GdkEvent *) ev);
648 _mouse_state = SelectVerticalDragging;
655 case SelectRectDragging:
656 case SelectVerticalDragging:
658 editor.drags()->motion_handler ((GdkEvent *) ev, false);
661 case SelectTouchDragging:
669 /* we may be dragging some non-note object (eg. patch-change, sysex)
672 return editor.drags()->motion_handler ((GdkEvent *) ev, false);
677 MidiRegionView::scroll (GdkEventScroll* ev)
679 if (_selection.empty()) {
683 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
684 /* XXX: bit of a hack; allow PrimaryModifier scroll through so that
685 it still works for zoom.
690 trackview.editor().verbose_cursor()->hide ();
692 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
693 bool together = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
695 if (ev->direction == GDK_SCROLL_UP) {
696 change_velocities (true, fine, false, together);
697 } else if (ev->direction == GDK_SCROLL_DOWN) {
698 change_velocities (false, fine, false, together);
700 /* left, right: we don't use them */
708 MidiRegionView::key_press (GdkEventKey* ev)
710 /* since GTK bindings are generally activated on press, and since
711 detectable auto-repeat is the name of the game and only sends
712 repeated presses, carry out key actions at key press, not release.
715 bool unmodified = Keyboard::no_modifier_keys_pressed (ev);
717 if (unmodified && (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R)) {
718 _mouse_state = SelectTouchDragging;
721 } else if (ev->keyval == GDK_Escape && unmodified) {
725 } else if (unmodified && (ev->keyval == GDK_comma || ev->keyval == GDK_period)) {
727 bool start = (ev->keyval == GDK_comma);
728 bool end = (ev->keyval == GDK_period);
729 bool shorter = Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier);
730 bool fine = Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
732 change_note_lengths (fine, shorter, Evoral::MusicalTime(), start, end);
736 } else if ((ev->keyval == GDK_BackSpace || ev->keyval == GDK_Delete) && unmodified) {
738 if (_selection.empty()) {
745 } else if (ev->keyval == GDK_Tab) {
747 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
748 goto_previous_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
750 goto_next_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
754 } else if (ev->keyval == GDK_ISO_Left_Tab) {
756 /* Shift-TAB generates ISO Left Tab, for some reason */
758 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
759 goto_previous_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
761 goto_next_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
767 } else if (ev->keyval == GDK_Up) {
769 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
770 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
771 bool together = Keyboard::modifier_state_contains (ev->state, Keyboard::Level4Modifier);
773 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
774 change_velocities (true, fine, allow_smush, together);
776 transpose (true, fine, allow_smush);
780 } else if (ev->keyval == GDK_Down) {
782 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
783 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
784 bool together = Keyboard::modifier_state_contains (ev->state, Keyboard::Level4Modifier);
786 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
787 change_velocities (false, fine, allow_smush, together);
789 transpose (false, fine, allow_smush);
793 } else if (ev->keyval == GDK_Left) {
795 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
796 nudge_notes (false, fine);
799 } else if (ev->keyval == GDK_Right) {
801 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
802 nudge_notes (true, fine);
805 } else if (ev->keyval == GDK_c && unmodified) {
809 } else if (ev->keyval == GDK_v && unmodified) {
818 MidiRegionView::key_release (GdkEventKey* ev)
820 if ((_mouse_state == SelectTouchDragging) && (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R)) {
828 MidiRegionView::channel_edit ()
830 if (_selection.empty()) {
834 /* pick a note somewhat at random (since Selection is a set<>) to
835 * provide the "current" channel for the dialog.
838 uint8_t current_channel = (*_selection.begin())->note()->channel ();
839 MidiChannelDialog channel_dialog (current_channel);
840 int ret = channel_dialog.run ();
843 case Gtk::RESPONSE_OK:
849 uint8_t new_channel = channel_dialog.active_channel ();
851 start_note_diff_command (_("channel edit"));
853 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
854 Selection::iterator next = i;
856 change_note_channel (*i, new_channel);
864 MidiRegionView::velocity_edit ()
866 if (_selection.empty()) {
870 /* pick a note somewhat at random (since Selection is a set<>) to
871 * provide the "current" velocity for the dialog.
874 uint8_t current_velocity = (*_selection.begin())->note()->velocity ();
875 MidiVelocityDialog velocity_dialog (current_velocity);
876 int ret = velocity_dialog.run ();
879 case Gtk::RESPONSE_OK:
885 uint8_t new_velocity = velocity_dialog.velocity ();
887 start_note_diff_command (_("velocity edit"));
889 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
890 Selection::iterator next = i;
892 change_note_velocity (*i, new_velocity, false);
900 MidiRegionView::show_list_editor ()
903 _list_editor = new MidiListEditor (trackview.session(), midi_region(), midi_view()->midi_track());
905 _list_editor->present ();
908 /** Add a note to the model, and the view, at a canvas (click) coordinate.
909 * \param t time in frames relative to the position of the region
910 * \param y vertical position in pixels
911 * \param length duration of the note in beats
912 * \param snap_t true to snap t to the grid, otherwise false.
915 MidiRegionView::create_note_at (framepos_t t, double y, Evoral::MusicalTime length, bool snap_t)
917 if (length < 2 * DBL_EPSILON) {
921 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
922 MidiStreamView* const view = mtv->midi_view();
924 const double note = view->y_to_note(y);
926 // Start of note in frames relative to region start
928 framecnt_t grid_frames;
929 t = snap_frame_to_grid_underneath (t, grid_frames);
932 const boost::shared_ptr<NoteType> new_note (
933 new NoteType (mtv->get_channel_for_add (),
934 region_frames_to_region_beats(t + _region->start()),
936 (uint8_t)note, 0x40));
938 if (_model->contains (new_note)) {
942 view->update_note_range(new_note->note());
944 MidiModel::NoteDiffCommand* cmd = _model->new_note_diff_command(_("add note"));
946 _model->apply_command(*trackview.session(), cmd);
948 play_midi_note (new_note);
952 MidiRegionView::clear_events (bool with_selection_signal)
954 clear_selection (with_selection_signal);
957 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
958 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
963 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
968 _patch_changes.clear();
970 _optimization_iterator = _events.end();
974 MidiRegionView::display_model(boost::shared_ptr<MidiModel> model)
978 content_connection.disconnect ();
979 _model->ContentsChanged.connect (content_connection, invalidator (*this), boost::bind (&MidiRegionView::redisplay_model, this), gui_context());
983 if (_enable_display) {
989 MidiRegionView::start_note_diff_command (string name)
991 if (!_note_diff_command) {
992 _note_diff_command = _model->new_note_diff_command (name);
997 MidiRegionView::note_diff_add_note (const boost::shared_ptr<NoteType> note, bool selected, bool show_velocity)
999 if (_note_diff_command) {
1000 _note_diff_command->add (note);
1003 _marked_for_selection.insert(note);
1005 if (show_velocity) {
1006 _marked_for_velocity.insert(note);
1011 MidiRegionView::note_diff_remove_note (NoteBase* ev)
1013 if (_note_diff_command && ev->note()) {
1014 _note_diff_command->remove(ev->note());
1019 MidiRegionView::note_diff_add_change (NoteBase* ev,
1020 MidiModel::NoteDiffCommand::Property property,
1023 if (_note_diff_command) {
1024 _note_diff_command->change (ev->note(), property, val);
1029 MidiRegionView::note_diff_add_change (NoteBase* ev,
1030 MidiModel::NoteDiffCommand::Property property,
1031 Evoral::MusicalTime val)
1033 if (_note_diff_command) {
1034 _note_diff_command->change (ev->note(), property, val);
1039 MidiRegionView::apply_diff (bool as_subcommand)
1043 if (!_note_diff_command) {
1047 if ((add_or_remove = _note_diff_command->adds_or_removes())) {
1048 // Mark all selected notes for selection when model reloads
1049 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1050 _marked_for_selection.insert((*i)->note());
1054 if (as_subcommand) {
1055 _model->apply_command_as_subcommand (*trackview.session(), _note_diff_command);
1057 _model->apply_command (*trackview.session(), _note_diff_command);
1060 _note_diff_command = 0;
1061 midi_view()->midi_track()->playlist_modified();
1063 if (add_or_remove) {
1064 _marked_for_selection.clear();
1067 _marked_for_velocity.clear();
1071 MidiRegionView::abort_command()
1073 delete _note_diff_command;
1074 _note_diff_command = 0;
1079 MidiRegionView::find_canvas_note (boost::shared_ptr<NoteType> note)
1081 if (_optimization_iterator != _events.end()) {
1082 ++_optimization_iterator;
1085 if (_optimization_iterator != _events.end() && (*_optimization_iterator)->note() == note) {
1086 return *_optimization_iterator;
1089 for (_optimization_iterator = _events.begin(); _optimization_iterator != _events.end(); ++_optimization_iterator) {
1090 if ((*_optimization_iterator)->note() == note) {
1091 return *_optimization_iterator;
1099 MidiRegionView::get_events (Events& e, Evoral::Sequence<Evoral::MusicalTime>::NoteOperator op, uint8_t val, int chan_mask)
1101 MidiModel::Notes notes;
1102 _model->get_notes (notes, op, val, chan_mask);
1104 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1105 NoteBase* cne = find_canvas_note (*n);
1113 MidiRegionView::redisplay_model()
1115 // Don't redisplay the model if we're currently recording and displaying that
1116 if (_active_notes) {
1124 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1125 (*i)->invalidate ();
1128 MidiModel::ReadLock lock(_model->read_lock());
1130 MidiModel::Notes& notes (_model->notes());
1131 _optimization_iterator = _events.begin();
1133 bool empty_when_starting = _events.empty();
1135 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1137 boost::shared_ptr<NoteType> note (*n);
1141 if (note_in_region_range (note, visible)) {
1143 if (!empty_when_starting && (cne = find_canvas_note (note)) != 0) {
1150 if ((cn = dynamic_cast<Note*>(cne)) != 0) {
1152 } else if ((ch = dynamic_cast<Hit*>(cne)) != 0) {
1164 add_note (note, visible);
1169 if (!empty_when_starting && (cne = find_canvas_note (note)) != 0) {
1177 /* remove note items that are no longer valid */
1179 if (!empty_when_starting) {
1180 for (Events::iterator i = _events.begin(); i != _events.end(); ) {
1181 if (!(*i)->valid ()) {
1183 for (vector<GhostRegion*>::iterator j = ghosts.begin(); j != ghosts.end(); ++j) {
1184 MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*j);
1186 gr->remove_note (*i);
1191 i = _events.erase (i);
1199 _patch_changes.clear();
1203 display_patch_changes ();
1205 _marked_for_selection.clear ();
1206 _marked_for_velocity.clear ();
1208 /* we may have caused _events to contain things out of order (e.g. if a note
1209 moved earlier or later). we don't generally need them in time order, but
1210 make a note that a sort is required for those cases that require it.
1213 _sort_needed = true;
1217 MidiRegionView::display_patch_changes ()
1219 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1220 uint16_t chn_mask = mtv->midi_track()->get_playback_channel_mask();
1222 for (uint8_t i = 0; i < 16; ++i) {
1223 display_patch_changes_on_channel (i, chn_mask & (1 << i));
1227 /** @param active_channel true to display patch changes fully, false to display
1228 * them `greyed-out' (as on an inactive channel)
1231 MidiRegionView::display_patch_changes_on_channel (uint8_t channel, bool active_channel)
1233 for (MidiModel::PatchChanges::const_iterator i = _model->patch_changes().begin(); i != _model->patch_changes().end(); ++i) {
1235 if ((*i)->channel() != channel) {
1239 const string patch_name = instrument_info().get_patch_name ((*i)->bank(), (*i)->program(), channel);
1240 add_canvas_patch_change (*i, patch_name, active_channel);
1245 MidiRegionView::display_sysexes()
1247 bool have_periodic_system_messages = false;
1248 bool display_periodic_messages = true;
1250 if (!Config->get_never_display_periodic_midi()) {
1252 for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
1253 const boost::shared_ptr<const Evoral::MIDIEvent<Evoral::MusicalTime> > mev =
1254 boost::static_pointer_cast<const Evoral::MIDIEvent<Evoral::MusicalTime> > (*i);
1257 if (mev->is_spp() || mev->is_mtc_quarter() || mev->is_mtc_full()) {
1258 have_periodic_system_messages = true;
1264 if (have_periodic_system_messages) {
1265 double zoom = trackview.editor().get_current_zoom (); // frames per pixel
1267 /* get an approximate value for the number of samples per video frame */
1269 double video_frame = trackview.session()->frame_rate() * (1.0/30);
1271 /* if we are zoomed out beyond than the cutoff (i.e. more
1272 * frames per pixel than frames per 4 video frames), don't
1273 * show periodic sysex messages.
1276 if (zoom > (video_frame*4)) {
1277 display_periodic_messages = false;
1281 display_periodic_messages = false;
1284 for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
1286 const boost::shared_ptr<const Evoral::MIDIEvent<Evoral::MusicalTime> > mev =
1287 boost::static_pointer_cast<const Evoral::MIDIEvent<Evoral::MusicalTime> > (*i);
1289 Evoral::MusicalTime time = (*i)->time();
1292 if (mev->is_spp() || mev->is_mtc_quarter() || mev->is_mtc_full()) {
1293 if (!display_periodic_messages) {
1301 for (uint32_t b = 0; b < (*i)->size(); ++b) {
1302 str << int((*i)->buffer()[b]);
1303 if (b != (*i)->size() -1) {
1307 string text = str.str();
1309 const double x = trackview.editor().sample_to_pixel(source_beats_to_region_frames(time));
1311 double height = midi_stream_view()->contents_height();
1313 // CAIROCANVAS: no longer passing *i (the sysex event) to the
1314 // SysEx canvas object!!!
1316 boost::shared_ptr<SysEx> sysex = boost::shared_ptr<SysEx>(
1317 new SysEx (*this, _note_group, text, height, x, 1.0));
1319 // Show unless message is beyond the region bounds
1320 if (time - _region->start() >= _region->length() || time < _region->start()) {
1326 _sys_exes.push_back(sysex);
1330 MidiRegionView::~MidiRegionView ()
1332 in_destructor = true;
1334 trackview.editor().verbose_cursor()->hide ();
1336 note_delete_connection.disconnect ();
1338 delete _list_editor;
1340 RegionViewGoingAway (this); /* EMIT_SIGNAL */
1342 if (_active_notes) {
1346 _selection_cleared_connection.disconnect ();
1349 clear_events (false);
1352 delete _note_diff_command;
1353 delete _step_edit_cursor;
1354 delete _temporary_note_group;
1358 MidiRegionView::region_resized (const PropertyChange& what_changed)
1360 RegionView::region_resized(what_changed);
1362 if (what_changed.contains (ARDOUR::Properties::position)) {
1363 _region_relative_time_converter.set_origin_b(_region->position());
1364 set_duration(_region->length(), 0);
1365 if (_enable_display) {
1370 if (what_changed.contains (ARDOUR::Properties::start) ||
1371 what_changed.contains (ARDOUR::Properties::position)) {
1372 _source_relative_time_converter.set_origin_b (_region->position() - _region->start());
1377 MidiRegionView::reset_width_dependent_items (double pixel_width)
1379 RegionView::reset_width_dependent_items(pixel_width);
1381 if (_enable_display) {
1385 for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
1386 if ((*x)->canvas_item()->width() >= _pixel_width) {
1393 move_step_edit_cursor (_step_edit_cursor_position);
1394 set_step_edit_cursor_width (_step_edit_cursor_width);
1398 MidiRegionView::set_height (double height)
1400 double old_height = _height;
1401 RegionView::set_height(height);
1403 apply_note_range (midi_stream_view()->lowest_note(),
1404 midi_stream_view()->highest_note(),
1405 height != old_height);
1408 name_text->raise_to_top();
1411 for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
1412 (*x)->set_height (midi_stream_view()->contents_height());
1415 if (_step_edit_cursor) {
1416 _step_edit_cursor->set_y1 (midi_stream_view()->contents_height());
1421 /** Apply the current note range from the stream view
1422 * by repositioning/hiding notes as necessary
1425 MidiRegionView::apply_note_range (uint8_t min, uint8_t max, bool force)
1427 if (!_enable_display) {
1431 if (!force && _current_range_min == min && _current_range_max == max) {
1435 _current_range_min = min;
1436 _current_range_max = max;
1438 for (Events::const_iterator i = _events.begin(); i != _events.end(); ++i) {
1439 NoteBase* event = *i;
1440 boost::shared_ptr<NoteType> note (event->note());
1442 if (note->note() < _current_range_min ||
1443 note->note() > _current_range_max) {
1449 if (Note* cnote = dynamic_cast<Note*>(event)) {
1451 const double y0 = 1. + floor (midi_stream_view()->note_to_y(note->note()));
1452 const double y1 = y0 + std::max(1., floor(midi_stream_view()->note_height()) - 1.);
1457 } else if (Hit* chit = dynamic_cast<Hit*>(event)) {
1464 MidiRegionView::add_ghost (TimeAxisView& tv)
1468 double unit_position = _region->position () / samples_per_pixel;
1469 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(&tv);
1470 MidiGhostRegion* ghost;
1472 if (mtv && mtv->midi_view()) {
1473 /* if ghost is inserted into midi track, use a dedicated midi ghost canvas group
1474 to allow having midi notes on top of note lines and waveforms.
1476 ghost = new MidiGhostRegion (*mtv->midi_view(), trackview, unit_position);
1478 ghost = new MidiGhostRegion (tv, trackview, unit_position);
1481 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1482 if ((note = dynamic_cast<Note*>(*i)) != 0) {
1483 ghost->add_note(note);
1487 ghost->set_height ();
1488 ghost->set_duration (_region->length() / samples_per_pixel);
1489 ghosts.push_back (ghost);
1491 GhostRegion::CatchDeletion.connect (*this, invalidator (*this), boost::bind (&RegionView::remove_ghost, this, _1), gui_context());
1497 /** Begin tracking note state for successive calls to add_event
1500 MidiRegionView::begin_write()
1502 if (_active_notes) {
1503 delete[] _active_notes;
1505 _active_notes = new Note*[128];
1506 for (unsigned i = 0; i < 128; ++i) {
1507 _active_notes[i] = 0;
1512 /** Destroy note state for add_event
1515 MidiRegionView::end_write()
1517 delete[] _active_notes;
1519 _marked_for_selection.clear();
1520 _marked_for_velocity.clear();
1524 /** Resolve an active MIDI note (while recording).
1527 MidiRegionView::resolve_note(uint8_t note, Evoral::MusicalTime end_time)
1529 if (midi_view()->note_mode() != Sustained) {
1533 if (_active_notes && _active_notes[note]) {
1535 /* XXX is end_time really region-centric? I think so, because
1536 this is a new region that we're recording, so source zero is
1537 the same as region zero
1539 const framepos_t end_time_frames = region_beats_to_region_frames(end_time);
1541 _active_notes[note]->set_x1 (trackview.editor().sample_to_pixel(end_time_frames));
1542 _active_notes[note]->set_outline_all ();
1543 _active_notes[note] = 0;
1549 /** Extend active notes to rightmost edge of region (if length is changed)
1552 MidiRegionView::extend_active_notes()
1554 if (!_active_notes) {
1558 for (unsigned i=0; i < 128; ++i) {
1559 if (_active_notes[i]) {
1560 _active_notes[i]->set_x1 (trackview.editor().sample_to_pixel(_region->length()));
1567 MidiRegionView::play_midi_note(boost::shared_ptr<NoteType> note)
1569 if (_no_sound_notes || !Config->get_sound_midi_notes()) {
1573 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1575 if (!route_ui || !route_ui->midi_track()) {
1579 NotePlayer* np = new NotePlayer (route_ui->midi_track ());
1583 /* NotePlayer deletes itself */
1587 MidiRegionView::start_playing_midi_note(boost::shared_ptr<NoteType> note)
1589 if (_no_sound_notes || !Config->get_sound_midi_notes()) {
1593 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1595 if (!route_ui || !route_ui->midi_track()) {
1599 delete _note_player;
1600 _note_player = new NotePlayer (route_ui->midi_track ());
1601 _note_player->add (note);
1602 _note_player->on ();
1606 MidiRegionView::start_playing_midi_chord (vector<boost::shared_ptr<NoteType> > notes)
1608 if (_no_sound_notes || !Config->get_sound_midi_notes()) {
1612 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1614 if (!route_ui || !route_ui->midi_track()) {
1618 delete _note_player;
1619 _note_player = new NotePlayer (route_ui->midi_track());
1621 for (vector<boost::shared_ptr<NoteType> >::iterator n = notes.begin(); n != notes.end(); ++n) {
1622 _note_player->add (*n);
1625 _note_player->on ();
1630 MidiRegionView::note_in_region_range (const boost::shared_ptr<NoteType> note, bool& visible) const
1632 const framepos_t note_start_frames = source_beats_to_region_frames (note->time());
1633 bool outside = (note_start_frames < 0) || (note_start_frames > _region->last_frame());
1635 visible = (note->note() >= midi_stream_view()->lowest_note()) &&
1636 (note->note() <= midi_stream_view()->highest_note());
1641 /** Update a canvas note's size from its model note.
1642 * @param ev Canvas note to update.
1643 * @param update_ghost_regions true to update the note in any ghost regions that we have, otherwise false.
1646 MidiRegionView::update_note (Note* ev, bool update_ghost_regions)
1648 boost::shared_ptr<NoteType> note = ev->note();
1649 const double x = trackview.editor().sample_to_pixel (source_beats_to_region_frames (note->time()));
1650 const double y0 = 1 + floor(midi_stream_view()->note_to_y(note->note()));
1655 /* trim note display to not overlap the end of its region */
1657 if (note->length() > 0) {
1658 const framepos_t note_end_frames = min (source_beats_to_region_frames (note->end_time()), _region->length());
1659 ev->set_x1 (trackview.editor().sample_to_pixel (note_end_frames));
1661 ev->set_x1 (trackview.editor().sample_to_pixel (_region->length()));
1664 ev->set_y1 (y0 + std::max(1., floor(midi_stream_view()->note_height()) - 1));
1666 if (!note->length()) {
1667 if (_active_notes && note->note() < 128) {
1668 // If this note is already active there's a stuck note,
1669 // finish the old note rectangle
1670 if (_active_notes[note->note()]) {
1671 Note* const old_rect = _active_notes[note->note()];
1672 boost::shared_ptr<NoteType> old_note = old_rect->note();
1673 old_rect->set_x1 (x);
1674 old_rect->set_outline_all ();
1676 _active_notes[note->note()] = ev;
1678 /* outline all but right edge */
1679 ev->set_outline_what (ArdourCanvas::Rectangle::What (
1680 ArdourCanvas::Rectangle::TOP|
1681 ArdourCanvas::Rectangle::LEFT|
1682 ArdourCanvas::Rectangle::BOTTOM));
1684 /* outline all edges */
1685 ev->set_outline_all ();
1688 if (update_ghost_regions) {
1689 for (std::vector<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
1690 MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*i);
1692 gr->update_note (ev);
1699 MidiRegionView::update_hit (Hit* ev)
1701 boost::shared_ptr<NoteType> note = ev->note();
1703 const framepos_t note_start_frames = source_beats_to_region_frames(note->time());
1704 const double x = trackview.editor().sample_to_pixel(note_start_frames);
1705 const double diamond_size = std::max(1., floor(midi_stream_view()->note_height()) - 2.);
1706 const double y = 1.5 + floor(midi_stream_view()->note_to_y(note->note())) + diamond_size * .5;
1708 ev->set_position (ArdourCanvas::Duple (x, y));
1709 ev->set_height (diamond_size);
1712 /** Add a MIDI note to the view (with length).
1714 * If in sustained mode, notes with length 0 will be considered active
1715 * notes, and resolve_note should be called when the corresponding note off
1716 * event arrives, to properly display the note.
1719 MidiRegionView::add_note(const boost::shared_ptr<NoteType> note, bool visible)
1721 NoteBase* event = 0;
1723 if (midi_view()->note_mode() == Sustained) {
1725 Note* ev_rect = new Note (*this, _note_group, note);
1727 update_note (ev_rect);
1731 MidiGhostRegion* gr;
1733 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
1734 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
1735 gr->add_note(ev_rect);
1739 } else if (midi_view()->note_mode() == Percussive) {
1741 const double diamond_size = std::max(1., floor(midi_stream_view()->note_height()) - 2.);
1743 Hit* ev_diamond = new Hit (*this, _note_group, diamond_size, note);
1745 update_hit (ev_diamond);
1754 if (_marked_for_selection.find(note) != _marked_for_selection.end()) {
1755 note_selected(event, true);
1758 if (_marked_for_velocity.find(note) != _marked_for_velocity.end()) {
1759 event->show_velocity();
1762 event->on_channel_selection_change (get_selected_channels());
1763 _events.push_back(event);
1772 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1773 MidiStreamView* const view = mtv->midi_view();
1775 view->update_note_range (note->note());
1779 MidiRegionView::step_add_note (uint8_t channel, uint8_t number, uint8_t velocity,
1780 Evoral::MusicalTime pos, Evoral::MusicalTime len)
1782 boost::shared_ptr<NoteType> new_note (new NoteType (channel, pos, len, number, velocity));
1784 /* potentially extend region to hold new note */
1786 framepos_t end_frame = source_beats_to_absolute_frames (new_note->end_time());
1787 framepos_t region_end = _region->last_frame();
1789 if (end_frame > region_end) {
1790 _region->set_length (end_frame - _region->position());
1793 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1794 MidiStreamView* const view = mtv->midi_view();
1796 view->update_note_range(new_note->note());
1798 _marked_for_selection.clear ();
1801 start_note_diff_command (_("step add"));
1802 note_diff_add_note (new_note, true, false);
1805 // last_step_edit_note = new_note;
1809 MidiRegionView::step_sustain (Evoral::MusicalTime beats)
1811 change_note_lengths (false, false, beats, false, true);
1814 /** Add a new patch change flag to the canvas.
1815 * @param patch the patch change to add
1816 * @param the text to display in the flag
1817 * @param active_channel true to display the flag as on an active channel, false to grey it out for an inactive channel.
1820 MidiRegionView::add_canvas_patch_change (MidiModel::PatchChangePtr patch, const string& displaytext, bool /*active_channel*/)
1822 framecnt_t region_frames = source_beats_to_region_frames (patch->time());
1823 const double x = trackview.editor().sample_to_pixel (region_frames);
1825 double const height = midi_stream_view()->contents_height();
1827 // CAIROCANVAS: active_channel info removed from PatcChange constructor
1828 // so we need to do something more sophisticated to keep its color
1829 // appearance (MidiPatchChangeFill/MidiPatchChangeInactiveChannelFill)
1832 boost::shared_ptr<PatchChange> patch_change = boost::shared_ptr<PatchChange>(
1833 new PatchChange(*this, group,
1840 if (patch_change->item().width() < _pixel_width) {
1841 // Show unless patch change is beyond the region bounds
1842 if (region_frames < 0 || region_frames >= _region->length()) {
1843 patch_change->hide();
1845 patch_change->show();
1848 patch_change->hide ();
1851 _patch_changes.push_back (patch_change);
1854 MIDI::Name::PatchPrimaryKey
1855 MidiRegionView::patch_change_to_patch_key (MidiModel::PatchChangePtr p)
1857 return MIDI::Name::PatchPrimaryKey (p->program(), p->bank());
1860 /// Return true iff @p pc applies to the given time on the given channel.
1862 patch_applies (const ARDOUR::MidiModel::constPatchChangePtr pc, Evoral::MusicalTime time, uint8_t channel)
1864 return pc->time() <= time && pc->channel() == channel;
1868 MidiRegionView::get_patch_key_at (Evoral::MusicalTime time, uint8_t channel, MIDI::Name::PatchPrimaryKey& key) const
1870 // The earliest event not before time
1871 MidiModel::PatchChanges::iterator i = _model->patch_change_lower_bound (time);
1873 // Go backwards until we find the latest PC for this channel, or the start
1874 while (i != _model->patch_changes().begin() &&
1875 (i == _model->patch_changes().end() ||
1876 !patch_applies(*i, time, channel))) {
1880 if (i != _model->patch_changes().end() && patch_applies(*i, time, channel)) {
1881 key.bank_number = (*i)->bank();
1882 key.program_number = (*i)->program ();
1884 key.bank_number = key.program_number = 0;
1887 if (!key.is_sane()) {
1888 error << string_compose(_("insane MIDI patch key %1:%2"),
1889 key.bank_number, key.program_number) << endmsg;
1894 MidiRegionView::change_patch_change (PatchChange& pc, const MIDI::Name::PatchPrimaryKey& new_patch)
1896 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("alter patch change"));
1898 if (pc.patch()->program() != new_patch.program_number) {
1899 c->change_program (pc.patch (), new_patch.program_number);
1902 int const new_bank = new_patch.bank_number;
1903 if (pc.patch()->bank() != new_bank) {
1904 c->change_bank (pc.patch (), new_bank);
1907 _model->apply_command (*trackview.session(), c);
1909 _patch_changes.clear ();
1910 display_patch_changes ();
1914 MidiRegionView::change_patch_change (MidiModel::PatchChangePtr old_change, const Evoral::PatchChange<Evoral::MusicalTime> & new_change)
1916 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("alter patch change"));
1918 if (old_change->time() != new_change.time()) {
1919 c->change_time (old_change, new_change.time());
1922 if (old_change->channel() != new_change.channel()) {
1923 c->change_channel (old_change, new_change.channel());
1926 if (old_change->program() != new_change.program()) {
1927 c->change_program (old_change, new_change.program());
1930 if (old_change->bank() != new_change.bank()) {
1931 c->change_bank (old_change, new_change.bank());
1934 _model->apply_command (*trackview.session(), c);
1936 _patch_changes.clear ();
1937 display_patch_changes ();
1940 /** Add a patch change to the region.
1941 * @param t Time in frames relative to region position
1942 * @param patch Patch to add; time and channel are ignored (time is converted from t, and channel comes from
1943 * MidiTimeAxisView::get_channel_for_add())
1946 MidiRegionView::add_patch_change (framecnt_t t, Evoral::PatchChange<Evoral::MusicalTime> const & patch)
1948 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1950 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("add patch change"));
1951 c->add (MidiModel::PatchChangePtr (
1952 new Evoral::PatchChange<Evoral::MusicalTime> (
1953 absolute_frames_to_source_beats (_region->position() + t),
1954 mtv->get_channel_for_add(), patch.program(), patch.bank()
1959 _model->apply_command (*trackview.session(), c);
1961 _patch_changes.clear ();
1962 display_patch_changes ();
1966 MidiRegionView::move_patch_change (PatchChange& pc, Evoral::MusicalTime t)
1968 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("move patch change"));
1969 c->change_time (pc.patch (), t);
1970 _model->apply_command (*trackview.session(), c);
1972 _patch_changes.clear ();
1973 display_patch_changes ();
1977 MidiRegionView::delete_patch_change (PatchChange* pc)
1979 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("delete patch change"));
1980 c->remove (pc->patch ());
1981 _model->apply_command (*trackview.session(), c);
1983 _patch_changes.clear ();
1984 display_patch_changes ();
1988 MidiRegionView::previous_patch (PatchChange& patch)
1990 if (patch.patch()->program() < 127) {
1991 MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch());
1992 key.program_number++;
1993 change_patch_change (patch, key);
1998 MidiRegionView::next_patch (PatchChange& patch)
2000 if (patch.patch()->program() > 0) {
2001 MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch());
2002 key.program_number--;
2003 change_patch_change (patch, key);
2008 MidiRegionView::next_bank (PatchChange& patch)
2010 if (patch.patch()->program() < 127) {
2011 MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch());
2012 if (key.bank_number > 0) {
2014 change_patch_change (patch, key);
2020 MidiRegionView::previous_bank (PatchChange& patch)
2022 if (patch.patch()->program() > 0) {
2023 MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch());
2024 if (key.bank_number < 127) {
2026 change_patch_change (patch, key);
2032 MidiRegionView::maybe_remove_deleted_note_from_selection (NoteBase* cne)
2034 if (_selection.empty()) {
2038 _selection.erase (cne);
2042 MidiRegionView::delete_selection()
2044 if (_selection.empty()) {
2048 start_note_diff_command (_("delete selection"));
2050 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2051 if ((*i)->selected()) {
2052 _note_diff_command->remove((*i)->note());
2062 MidiRegionView::delete_note (boost::shared_ptr<NoteType> n)
2064 start_note_diff_command (_("delete note"));
2065 _note_diff_command->remove (n);
2068 trackview.editor().verbose_cursor()->hide ();
2072 MidiRegionView::clear_selection_except (NoteBase* ev, bool signal)
2074 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2076 Selection::iterator tmp = i;
2079 (*i)->set_selected (false);
2080 (*i)->hide_velocity ();
2081 _selection.erase (i);
2089 if (!ev && _entered) {
2090 // Clearing selection entirely, ungrab keyboard
2091 Keyboard::magic_widget_drop_focus();
2092 _grabbed_keyboard = false;
2095 /* this does not change the status of this regionview w.r.t the editor
2100 SelectionCleared (this); /* EMIT SIGNAL */
2105 MidiRegionView::unique_select(NoteBase* ev)
2107 const bool selection_was_empty = _selection.empty();
2109 clear_selection_except (ev);
2111 /* don't bother with checking to see if we should remove this
2112 regionview from the editor selection, since we're about to add
2113 another note, and thus put/keep this regionview in the editor
2117 if (!ev->selected()) {
2118 add_to_selection (ev);
2119 if (selection_was_empty && _entered) {
2120 // Grab keyboard for moving notes with arrow keys
2121 Keyboard::magic_widget_grab_focus();
2122 _grabbed_keyboard = true;
2128 MidiRegionView::select_all_notes ()
2132 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2133 add_to_selection (*i);
2138 MidiRegionView::select_range (framepos_t start, framepos_t end)
2142 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2143 framepos_t t = source_beats_to_absolute_frames((*i)->note()->time());
2144 if (t >= start && t <= end) {
2145 add_to_selection (*i);
2151 MidiRegionView::invert_selection ()
2153 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2154 if ((*i)->selected()) {
2155 remove_from_selection(*i);
2157 add_to_selection (*i);
2163 MidiRegionView::select_matching_notes (uint8_t notenum, uint16_t channel_mask, bool add, bool extend)
2165 bool have_selection = !_selection.empty();
2166 uint8_t low_note = 127;
2167 uint8_t high_note = 0;
2168 MidiModel::Notes& notes (_model->notes());
2169 _optimization_iterator = _events.begin();
2171 if (extend && !have_selection) {
2175 /* scan existing selection to get note range */
2177 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2178 if ((*i)->note()->note() < low_note) {
2179 low_note = (*i)->note()->note();
2181 if ((*i)->note()->note() > high_note) {
2182 high_note = (*i)->note()->note();
2189 if (!extend && (low_note == high_note) && (high_note == notenum)) {
2190 /* only note previously selected is the one we are
2191 * reselecting. treat this as cancelling the selection.
2198 low_note = min (low_note, notenum);
2199 high_note = max (high_note, notenum);
2202 _no_sound_notes = true;
2204 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
2206 boost::shared_ptr<NoteType> note (*n);
2208 bool select = false;
2210 if (((1 << note->channel()) & channel_mask) != 0) {
2212 if ((note->note() >= low_note && note->note() <= high_note)) {
2215 } else if (note->note() == notenum) {
2221 if ((cne = find_canvas_note (note)) != 0) {
2222 // extend is false because we've taken care of it,
2223 // since it extends by time range, not pitch.
2224 note_selected (cne, add, false);
2228 add = true; // we need to add all remaining matching notes, even if the passed in value was false (for "set")
2232 _no_sound_notes = false;
2236 MidiRegionView::toggle_matching_notes (uint8_t notenum, uint16_t channel_mask)
2238 MidiModel::Notes& notes (_model->notes());
2239 _optimization_iterator = _events.begin();
2241 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
2243 boost::shared_ptr<NoteType> note (*n);
2246 if (note->note() == notenum && (((0x0001 << note->channel()) & channel_mask) != 0)) {
2247 if ((cne = find_canvas_note (note)) != 0) {
2248 if (cne->selected()) {
2249 note_deselected (cne);
2251 note_selected (cne, true, false);
2259 MidiRegionView::note_selected (NoteBase* ev, bool add, bool extend)
2262 clear_selection_except (ev);
2263 if (!_selection.empty()) {
2264 PublicEditor& editor (trackview.editor());
2265 editor.get_selection().add (this);
2271 if (!ev->selected()) {
2272 add_to_selection (ev);
2276 /* find end of latest note selected, select all between that and the start of "ev" */
2278 Evoral::MusicalTime earliest = Evoral::MaxMusicalTime;
2279 Evoral::MusicalTime latest = Evoral::MusicalTime();
2281 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2282 if ((*i)->note()->end_time() > latest) {
2283 latest = (*i)->note()->end_time();
2285 if ((*i)->note()->time() < earliest) {
2286 earliest = (*i)->note()->time();
2290 if (ev->note()->end_time() > latest) {
2291 latest = ev->note()->end_time();
2294 if (ev->note()->time() < earliest) {
2295 earliest = ev->note()->time();
2298 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2300 /* find notes entirely within OR spanning the earliest..latest range */
2302 if (((*i)->note()->time() >= earliest && (*i)->note()->end_time() <= latest) ||
2303 ((*i)->note()->time() <= earliest && (*i)->note()->end_time() >= latest)) {
2304 add_to_selection (*i);
2312 MidiRegionView::note_deselected(NoteBase* ev)
2314 remove_from_selection (ev);
2318 MidiRegionView::update_drag_selection(framepos_t start, framepos_t end, double gy0, double gy1, bool extend)
2320 PublicEditor& editor = trackview.editor();
2322 // Convert to local coordinates
2323 const framepos_t p = _region->position();
2324 const double y = midi_view()->y_position();
2325 const double x0 = editor.sample_to_pixel(max((framepos_t)0, start - p));
2326 const double x1 = editor.sample_to_pixel(max((framepos_t)0, end - p));
2327 const double y0 = max(0.0, gy0 - y);
2328 const double y1 = max(0.0, gy1 - y);
2330 // TODO: Make this faster by storing the last updated selection rect, and only
2331 // adjusting things that are in the area that appears/disappeared.
2332 // We probably need a tree to be able to find events in O(log(n)) time.
2334 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2335 if ((*i)->x0() < x1 && (*i)->x1() > x0 && (*i)->y0() < y1 && (*i)->y1() > y0) {
2336 // Rectangles intersect
2337 if (!(*i)->selected()) {
2338 add_to_selection (*i);
2340 } else if ((*i)->selected() && !extend) {
2341 // Rectangles do not intersect
2342 remove_from_selection (*i);
2346 typedef RouteTimeAxisView::AutomationTracks ATracks;
2347 typedef std::list<Selectable*> Selectables;
2349 /* Add control points to selection. */
2350 const ATracks& atracks = midi_view()->automation_tracks();
2351 Selectables selectables;
2352 editor.get_selection().clear_points();
2353 for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) {
2354 a->second->get_selectables(start, end, gy0, gy1, selectables);
2355 for (Selectables::const_iterator s = selectables.begin(); s != selectables.end(); ++s) {
2356 ControlPoint* cp = dynamic_cast<ControlPoint*>(*s);
2358 editor.get_selection().add(cp);
2361 a->second->set_selected_points(editor.get_selection().points);
2366 MidiRegionView::update_vertical_drag_selection (double y1, double y2, bool extend)
2372 // TODO: Make this faster by storing the last updated selection rect, and only
2373 // adjusting things that are in the area that appears/disappeared.
2374 // We probably need a tree to be able to find events in O(log(n)) time.
2376 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2377 if (((*i)->y1() >= y1 && (*i)->y1() <= y2)) {
2378 // within y- (note-) range
2379 if (!(*i)->selected()) {
2380 add_to_selection (*i);
2382 } else if ((*i)->selected() && !extend) {
2383 remove_from_selection (*i);
2389 MidiRegionView::remove_from_selection (NoteBase* ev)
2391 Selection::iterator i = _selection.find (ev);
2393 if (i != _selection.end()) {
2394 _selection.erase (i);
2395 if (_selection.empty() && _grabbed_keyboard) {
2397 Keyboard::magic_widget_drop_focus();
2398 _grabbed_keyboard = false;
2402 ev->set_selected (false);
2403 ev->hide_velocity ();
2405 if (_selection.empty()) {
2406 PublicEditor& editor (trackview.editor());
2407 editor.get_selection().remove (this);
2412 MidiRegionView::add_to_selection (NoteBase* ev)
2414 const bool selection_was_empty = _selection.empty();
2416 if (_selection.insert (ev).second) {
2417 ev->set_selected (true);
2418 start_playing_midi_note ((ev)->note());
2419 if (selection_was_empty && _entered) {
2420 // Grab keyboard for moving notes with arrow keys
2421 Keyboard::magic_widget_grab_focus();
2422 _grabbed_keyboard = true;
2426 if (selection_was_empty) {
2427 PublicEditor& editor (trackview.editor());
2428 editor.get_selection().add (this);
2433 MidiRegionView::move_selection(double dx, double dy, double cumulative_dy)
2435 typedef vector<boost::shared_ptr<NoteType> > PossibleChord;
2436 PossibleChord to_play;
2437 Evoral::MusicalTime earliest = Evoral::MaxMusicalTime;
2439 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2440 if ((*i)->note()->time() < earliest) {
2441 earliest = (*i)->note()->time();
2445 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2446 if ((*i)->note()->time() == earliest) {
2447 to_play.push_back ((*i)->note());
2449 (*i)->move_event(dx, dy);
2452 if (dy && !_selection.empty() && !_no_sound_notes && Config->get_sound_midi_notes()) {
2454 if (to_play.size() > 1) {
2456 PossibleChord shifted;
2458 for (PossibleChord::iterator n = to_play.begin(); n != to_play.end(); ++n) {
2459 boost::shared_ptr<NoteType> moved_note (new NoteType (**n));
2460 moved_note->set_note (moved_note->note() + cumulative_dy);
2461 shifted.push_back (moved_note);
2464 start_playing_midi_chord (shifted);
2466 } else if (!to_play.empty()) {
2468 boost::shared_ptr<NoteType> moved_note (new NoteType (*to_play.front()));
2469 moved_note->set_note (moved_note->note() + cumulative_dy);
2470 start_playing_midi_note (moved_note);
2476 MidiRegionView::note_dropped(NoteBase *, frameoffset_t dt, int8_t dnote)
2478 uint8_t lowest_note_in_selection = 127;
2479 uint8_t highest_note_in_selection = 0;
2480 uint8_t highest_note_difference = 0;
2482 // find highest and lowest notes first
2484 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2485 uint8_t pitch = (*i)->note()->note();
2486 lowest_note_in_selection = std::min(lowest_note_in_selection, pitch);
2487 highest_note_in_selection = std::max(highest_note_in_selection, pitch);
2491 cerr << "dnote: " << (int) dnote << endl;
2492 cerr << "lowest note (streamview): " << int(midi_stream_view()->lowest_note())
2493 << " highest note (streamview): " << int(midi_stream_view()->highest_note()) << endl;
2494 cerr << "lowest note (selection): " << int(lowest_note_in_selection) << " highest note(selection): "
2495 << int(highest_note_in_selection) << endl;
2496 cerr << "selection size: " << _selection.size() << endl;
2497 cerr << "Highest note in selection: " << (int) highest_note_in_selection << endl;
2500 // Make sure the note pitch does not exceed the MIDI standard range
2501 if (highest_note_in_selection + dnote > 127) {
2502 highest_note_difference = highest_note_in_selection - 127;
2505 start_note_diff_command (_("move notes"));
2507 for (Selection::iterator i = _selection.begin(); i != _selection.end() ; ++i) {
2509 framepos_t new_frames = source_beats_to_absolute_frames ((*i)->note()->time()) + dt;
2510 Evoral::MusicalTime new_time = absolute_frames_to_source_beats (new_frames);
2516 note_diff_add_change (*i, MidiModel::NoteDiffCommand::StartTime, new_time);
2518 uint8_t original_pitch = (*i)->note()->note();
2519 uint8_t new_pitch = original_pitch + dnote - highest_note_difference;
2521 // keep notes in standard midi range
2522 clamp_to_0_127(new_pitch);
2524 lowest_note_in_selection = std::min(lowest_note_in_selection, new_pitch);
2525 highest_note_in_selection = std::max(highest_note_in_selection, new_pitch);
2527 note_diff_add_change (*i, MidiModel::NoteDiffCommand::NoteNumber, new_pitch);
2532 // care about notes being moved beyond the upper/lower bounds on the canvas
2533 if (lowest_note_in_selection < midi_stream_view()->lowest_note() ||
2534 highest_note_in_selection > midi_stream_view()->highest_note()) {
2535 midi_stream_view()->set_note_range(MidiStreamView::ContentsRange);
2539 /** @param x Pixel relative to the region position.
2540 * @return Snapped frame relative to the region position.
2543 MidiRegionView::snap_pixel_to_sample(double x)
2545 PublicEditor& editor (trackview.editor());
2546 return snap_frame_to_frame (editor.pixel_to_sample (x));
2549 /** @param x Pixel relative to the region position.
2550 * @return Snapped pixel relative to the region position.
2553 MidiRegionView::snap_to_pixel(double x)
2555 return (double) trackview.editor().sample_to_pixel(snap_pixel_to_sample(x));
2559 MidiRegionView::get_position_pixels()
2561 framepos_t region_frame = get_position();
2562 return trackview.editor().sample_to_pixel(region_frame);
2566 MidiRegionView::get_end_position_pixels()
2568 framepos_t frame = get_position() + get_duration ();
2569 return trackview.editor().sample_to_pixel(frame);
2573 MidiRegionView::source_beats_to_absolute_frames(Evoral::MusicalTime beats) const
2575 /* the time converter will return the frame corresponding to `beats'
2576 relative to the start of the source. The start of the source
2577 is an implied position given by region->position - region->start
2579 const framepos_t source_start = _region->position() - _region->start();
2580 return source_start + _source_relative_time_converter.to (beats);
2584 MidiRegionView::absolute_frames_to_source_beats(framepos_t frames) const
2586 /* the `frames' argument needs to be converted into a frame count
2587 relative to the start of the source before being passed in to the
2590 const framepos_t source_start = _region->position() - _region->start();
2591 return _source_relative_time_converter.from (frames - source_start);
2595 MidiRegionView::region_beats_to_region_frames(Evoral::MusicalTime beats) const
2597 return _region_relative_time_converter.to(beats);
2601 MidiRegionView::region_frames_to_region_beats(framepos_t frames) const
2603 return _region_relative_time_converter.from(frames);
2607 MidiRegionView::begin_resizing (bool /*at_front*/)
2609 _resize_data.clear();
2611 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2612 Note *note = dynamic_cast<Note*> (*i);
2614 // only insert CanvasNotes into the map
2616 NoteResizeData *resize_data = new NoteResizeData();
2617 resize_data->note = note;
2619 // create a new SimpleRect from the note which will be the resize preview
2620 ArdourCanvas::Rectangle *resize_rect = new ArdourCanvas::Rectangle (_note_group,
2621 ArdourCanvas::Rect (note->x0(), note->y0(), note->x0(), note->y1()));
2623 // calculate the colors: get the color settings
2624 uint32_t fill_color = UINT_RGBA_CHANGE_A(
2625 ARDOUR_UI::config()->color ("midi note selected"),
2628 // make the resize preview notes more transparent and bright
2629 fill_color = UINT_INTERPOLATE(fill_color, 0xFFFFFF40, 0.5);
2631 // calculate color based on note velocity
2632 resize_rect->set_fill_color (UINT_INTERPOLATE(
2633 NoteBase::meter_style_fill_color(note->note()->velocity(), note->selected()),
2637 resize_rect->set_outline_color (NoteBase::calculate_outline (
2638 ARDOUR_UI::config()->color ("midi note selected")));
2640 resize_data->resize_rect = resize_rect;
2641 _resize_data.push_back(resize_data);
2646 /** Update resizing notes while user drags.
2647 * @param primary `primary' note for the drag; ie the one that is used as the reference in non-relative mode.
2648 * @param at_front which end of the note (true == note on, false == note off)
2649 * @param delta_x change in mouse position since the start of the drag
2650 * @param relative true if relative resizing is taking place, false if absolute resizing. This only makes
2651 * a difference when multiple notes are being resized; in relative mode, each note's length is changed by the
2652 * amount of the drag. In non-relative mode, all selected notes are set to have the same start or end point
2653 * as the \a primary note.
2656 MidiRegionView::update_resizing (NoteBase* primary, bool at_front, double delta_x, bool relative)
2658 bool cursor_set = false;
2660 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2661 ArdourCanvas::Rectangle* resize_rect = (*i)->resize_rect;
2662 Note* canvas_note = (*i)->note;
2667 current_x = canvas_note->x0() + delta_x;
2669 current_x = primary->x0() + delta_x;
2673 current_x = canvas_note->x1() + delta_x;
2675 current_x = primary->x1() + delta_x;
2679 if (current_x < 0) {
2680 // This works even with snapping because RegionView::snap_frame_to_frame()
2681 // snaps forward if the snapped sample is before the beginning of the region
2684 if (current_x > trackview.editor().sample_to_pixel(_region->length())) {
2685 current_x = trackview.editor().sample_to_pixel(_region->length());
2689 resize_rect->set_x0 (snap_to_pixel(current_x));
2690 resize_rect->set_x1 (canvas_note->x1());
2692 resize_rect->set_x1 (snap_to_pixel(current_x));
2693 resize_rect->set_x0 (canvas_note->x0());
2697 const double snapped_x = snap_pixel_to_sample (current_x);
2698 Evoral::MusicalTime beats = region_frames_to_region_beats (snapped_x);
2699 Evoral::MusicalTime len = Evoral::MusicalTime();
2702 if (beats < canvas_note->note()->end_time()) {
2703 len = canvas_note->note()->time() - beats;
2704 len += canvas_note->note()->length();
2707 if (beats >= canvas_note->note()->time()) {
2708 len = beats - canvas_note->note()->time();
2713 snprintf (buf, sizeof (buf), "%.3g beats", len.to_double());
2714 show_verbose_cursor (buf, 0, 0);
2723 /** Finish resizing notes when the user releases the mouse button.
2724 * Parameters the same as for \a update_resizing().
2727 MidiRegionView::commit_resizing (NoteBase* primary, bool at_front, double delta_x, bool relative)
2729 start_note_diff_command (_("resize notes"));
2731 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2732 Note* canvas_note = (*i)->note;
2733 ArdourCanvas::Rectangle* resize_rect = (*i)->resize_rect;
2735 /* Get the new x position for this resize, which is in pixels relative
2736 * to the region position.
2743 current_x = canvas_note->x0() + delta_x;
2745 current_x = primary->x0() + delta_x;
2749 current_x = canvas_note->x1() + delta_x;
2751 current_x = primary->x1() + delta_x;
2755 if (current_x < 0) {
2758 if (current_x > trackview.editor().sample_to_pixel(_region->length())) {
2759 current_x = trackview.editor().sample_to_pixel(_region->length());
2762 /* Convert that to a frame within the source */
2763 current_x = snap_pixel_to_sample (current_x) + _region->start ();
2765 /* and then to beats */
2766 const Evoral::MusicalTime x_beats = region_frames_to_region_beats (current_x);
2768 if (at_front && x_beats < canvas_note->note()->end_time()) {
2769 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::StartTime, x_beats);
2771 Evoral::MusicalTime len = canvas_note->note()->time() - x_beats;
2772 len += canvas_note->note()->length();
2775 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
2780 const Evoral::MusicalTime len = x_beats - canvas_note->note()->time();
2783 /* XXX convert to beats */
2784 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
2792 _resize_data.clear();
2797 MidiRegionView::abort_resizing ()
2799 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2800 delete (*i)->resize_rect;
2804 _resize_data.clear ();
2808 MidiRegionView::change_note_velocity(NoteBase* event, int8_t velocity, bool relative)
2810 uint8_t new_velocity;
2813 new_velocity = event->note()->velocity() + velocity;
2814 clamp_to_0_127(new_velocity);
2816 new_velocity = velocity;
2819 event->set_selected (event->selected()); // change color
2821 note_diff_add_change (event, MidiModel::NoteDiffCommand::Velocity, new_velocity);
2825 MidiRegionView::change_note_note (NoteBase* event, int8_t note, bool relative)
2830 new_note = event->note()->note() + note;
2835 clamp_to_0_127 (new_note);
2836 note_diff_add_change (event, MidiModel::NoteDiffCommand::NoteNumber, new_note);
2840 MidiRegionView::trim_note (NoteBase* event, Evoral::MusicalTime front_delta, Evoral::MusicalTime end_delta)
2842 bool change_start = false;
2843 bool change_length = false;
2844 Evoral::MusicalTime new_start;
2845 Evoral::MusicalTime new_length;
2847 /* NOTE: the semantics of the two delta arguments are slightly subtle:
2849 front_delta: if positive - move the start of the note later in time (shortening it)
2850 if negative - move the start of the note earlier in time (lengthening it)
2852 end_delta: if positive - move the end of the note later in time (lengthening it)
2853 if negative - move the end of the note earlier in time (shortening it)
2856 if (!!front_delta) {
2857 if (front_delta < 0) {
2859 if (event->note()->time() < -front_delta) {
2860 new_start = Evoral::MusicalTime();
2862 new_start = event->note()->time() + front_delta; // moves earlier
2865 /* start moved toward zero, so move the end point out to where it used to be.
2866 Note that front_delta is negative, so this increases the length.
2869 new_length = event->note()->length() - front_delta;
2870 change_start = true;
2871 change_length = true;
2875 Evoral::MusicalTime new_pos = event->note()->time() + front_delta;
2877 if (new_pos < event->note()->end_time()) {
2878 new_start = event->note()->time() + front_delta;
2879 /* start moved toward the end, so move the end point back to where it used to be */
2880 new_length = event->note()->length() - front_delta;
2881 change_start = true;
2882 change_length = true;
2889 bool can_change = true;
2890 if (end_delta < 0) {
2891 if (event->note()->length() < -end_delta) {
2897 new_length = event->note()->length() + end_delta;
2898 change_length = true;
2903 note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_start);
2906 if (change_length) {
2907 note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, new_length);
2912 MidiRegionView::change_note_channel (NoteBase* event, int8_t chn, bool relative)
2914 uint8_t new_channel;
2918 if (event->note()->channel() < -chn) {
2921 new_channel = event->note()->channel() + chn;
2924 new_channel = event->note()->channel() + chn;
2927 new_channel = (uint8_t) chn;
2930 note_diff_add_change (event, MidiModel::NoteDiffCommand::Channel, new_channel);
2934 MidiRegionView::change_note_time (NoteBase* event, Evoral::MusicalTime delta, bool relative)
2936 Evoral::MusicalTime new_time;
2940 if (event->note()->time() < -delta) {
2941 new_time = Evoral::MusicalTime();
2943 new_time = event->note()->time() + delta;
2946 new_time = event->note()->time() + delta;
2952 note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_time);
2956 MidiRegionView::change_note_length (NoteBase* event, Evoral::MusicalTime t)
2958 note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, t);
2962 MidiRegionView::change_velocities (bool up, bool fine, bool allow_smush, bool all_together)
2967 if (_selection.empty()) {
2982 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2983 if ((*i)->note()->velocity() < -delta || (*i)->note()->velocity() + delta > 127) {
2989 start_note_diff_command (_("change velocities"));
2991 for (Selection::iterator i = _selection.begin(); i != _selection.end();) {
2992 Selection::iterator next = i;
2996 if (i == _selection.begin()) {
2997 change_note_velocity (*i, delta, true);
2998 value = (*i)->note()->velocity() + delta;
3000 change_note_velocity (*i, value, false);
3004 change_note_velocity (*i, delta, true);
3013 if (!_selection.empty()) {
3015 snprintf (buf, sizeof (buf), "Vel %d",
3016 (int) (*_selection.begin())->note()->velocity());
3017 show_verbose_cursor (buf, 10, 10);
3023 MidiRegionView::transpose (bool up, bool fine, bool allow_smush)
3025 if (_selection.empty()) {
3042 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3044 if ((int8_t) (*i)->note()->note() + delta <= 0) {
3048 if ((int8_t) (*i)->note()->note() + delta > 127) {
3055 start_note_diff_command (_("transpose"));
3057 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3058 Selection::iterator next = i;
3060 change_note_note (*i, delta, true);
3068 MidiRegionView::change_note_lengths (bool fine, bool shorter, Evoral::MusicalTime delta, bool start, bool end)
3072 delta = Evoral::MusicalTime(1.0/128.0);
3074 /* grab the current grid distance */
3075 delta = get_grid_beats(_region->position());
3083 start_note_diff_command (_("change note lengths"));
3085 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3086 Selection::iterator next = i;
3089 /* note the negation of the delta for start */
3092 (start ? -delta : Evoral::MusicalTime()),
3093 (end ? delta : Evoral::MusicalTime()));
3102 MidiRegionView::nudge_notes (bool forward, bool fine)
3104 if (_selection.empty()) {
3108 /* pick a note as the point along the timeline to get the nudge distance.
3109 its not necessarily the earliest note, so we may want to pull the notes out
3110 into a vector and sort before using the first one.
3113 const framepos_t ref_point = source_beats_to_absolute_frames ((*(_selection.begin()))->note()->time());
3114 Evoral::MusicalTime delta;
3118 /* non-fine, move by 1 bar regardless of snap */
3119 delta = Evoral::MusicalTime(trackview.session()->tempo_map().meter_at(ref_point).divisions_per_bar());
3121 } else if (trackview.editor().snap_mode() == Editing::SnapOff) {
3123 /* grid is off - use nudge distance */
3126 const framecnt_t distance = trackview.editor().get_nudge_distance (ref_point, unused);
3127 delta = region_frames_to_region_beats (fabs ((double)distance));
3133 framepos_t next_pos = ref_point;
3136 if (max_framepos - 1 < next_pos) {
3140 if (next_pos == 0) {
3146 trackview.editor().snap_to (next_pos, (forward ? RoundUpAlways : RoundDownAlways), false);
3147 const framecnt_t distance = ref_point - next_pos;
3148 delta = region_frames_to_region_beats (fabs ((double)distance));
3159 start_note_diff_command (_("nudge"));
3161 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3162 Selection::iterator next = i;
3164 change_note_time (*i, delta, true);
3172 MidiRegionView::change_channel(uint8_t channel)
3174 start_note_diff_command(_("change channel"));
3175 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3176 note_diff_add_change (*i, MidiModel::NoteDiffCommand::Channel, channel);
3184 MidiRegionView::note_entered(NoteBase* ev)
3186 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3188 pre_note_enter_cursor = editor->get_canvas_cursor ();
3190 if (_mouse_state == SelectTouchDragging) {
3191 note_selected (ev, true);
3194 show_verbose_cursor (ev->note ());
3198 MidiRegionView::note_left (NoteBase*)
3200 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3202 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3203 (*i)->hide_velocity ();
3206 editor->verbose_cursor()->hide ();
3208 if (pre_note_enter_cursor) {
3209 editor->set_canvas_cursor (pre_note_enter_cursor);
3210 pre_note_enter_cursor = 0;
3215 MidiRegionView::patch_entered (PatchChange* p)
3218 /* XXX should get patch name if we can */
3219 s << _("Bank ") << (p->patch()->bank() + MIDI_BP_ZERO) << '\n'
3220 << _("Program ") << ((int) p->patch()->program()) + MIDI_BP_ZERO << '\n'
3221 << _("Channel ") << ((int) p->patch()->channel() + 1);
3222 show_verbose_cursor (s.str(), 10, 20);
3223 p->item().grab_focus();
3227 MidiRegionView::patch_left (PatchChange *)
3229 trackview.editor().verbose_cursor()->hide ();
3230 /* focus will transfer back via the enter-notify event sent to this
3236 MidiRegionView::sysex_entered (SysEx* p)
3240 // need a way to extract text from p->_flag->_text
3242 // show_verbose_cursor (s.str(), 10, 20);
3243 p->item().grab_focus();
3247 MidiRegionView::sysex_left (SysEx *)
3249 trackview.editor().verbose_cursor()->hide ();
3250 /* focus will transfer back via the enter-notify event sent to this
3256 MidiRegionView::note_mouse_position (float x_fraction, float /*y_fraction*/, bool can_set_cursor)
3258 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3259 Editing::MouseMode mm = editor->current_mouse_mode();
3260 bool trimmable = (mm == MouseObject || mm == MouseTimeFX || mm == MouseDraw);
3262 if (can_set_cursor) {
3263 if (trimmable && x_fraction > 0.0 && x_fraction < 0.2) {
3264 editor->set_canvas_cursor (editor->cursors()->left_side_trim);
3265 } else if (trimmable && x_fraction >= 0.8 && x_fraction < 1.0) {
3266 editor->set_canvas_cursor (editor->cursors()->right_side_trim);
3267 } else if (pre_note_enter_cursor) {
3268 editor->set_canvas_cursor (pre_note_enter_cursor);
3274 MidiRegionView::set_frame_color()
3278 TimeAxisViewItem::set_frame_color ();
3285 f = ARDOUR_UI::config()->color ("selected region base");
3286 } else if (high_enough_for_name || !ARDOUR_UI::config()->get_color_regions_using_track_color()) {
3287 f = ARDOUR_UI::config()->color_mod ("midi frame base", "midi frame base");
3292 if (!rect_visible) {
3293 f = UINT_RGBA_CHANGE_A (f, 80);
3296 frame->set_fill_color (f);
3300 MidiRegionView::midi_channel_mode_changed ()
3302 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3303 uint16_t mask = mtv->midi_track()->get_playback_channel_mask();
3304 ChannelMode mode = mtv->midi_track()->get_playback_channel_mode ();
3306 if (mode == ForceChannel) {
3307 mask = 0xFFFF; // Show all notes as active (below)
3310 // Update notes for selection
3311 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3312 (*i)->on_channel_selection_change (mask);
3315 _patch_changes.clear ();
3316 display_patch_changes ();
3320 MidiRegionView::instrument_settings_changed ()
3326 MidiRegionView::cut_copy_clear (Editing::CutCopyOp op)
3328 if (_selection.empty()) {
3332 PublicEditor& editor (trackview.editor());
3336 /* XXX what to do ? */
3340 editor.get_cut_buffer().add (selection_as_cut_buffer());
3348 start_note_diff_command();
3350 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3357 note_diff_remove_note (*i);
3367 MidiRegionView::selection_as_cut_buffer () const
3371 for (Selection::const_iterator i = _selection.begin(); i != _selection.end(); ++i) {
3372 NoteType* n = (*i)->note().get();
3373 notes.insert (boost::shared_ptr<NoteType> (new NoteType (*n)));
3376 MidiCutBuffer* cb = new MidiCutBuffer (trackview.session());
3382 /** This method handles undo */
3384 MidiRegionView::paste (framepos_t pos, const ::Selection& selection, PasteContext& ctx)
3386 trackview.session()->begin_reversible_command (Operations::paste);
3388 // Paste notes, if available
3389 MidiNoteSelection::const_iterator m = selection.midi_notes.get_nth(ctx.counts.n_notes());
3390 if (m != selection.midi_notes.end()) {
3391 ctx.counts.increase_n_notes();
3392 paste_internal(pos, ctx.count, ctx.times, **m);
3395 // Paste control points to automation children, if available
3396 typedef RouteTimeAxisView::AutomationTracks ATracks;
3397 const ATracks& atracks = midi_view()->automation_tracks();
3398 for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) {
3399 a->second->paste(pos, selection, ctx);
3402 trackview.session()->commit_reversible_command ();
3407 /** This method handles undo */
3409 MidiRegionView::paste_internal (framepos_t pos, unsigned paste_count, float times, const MidiCutBuffer& mcb)
3415 start_note_diff_command (_("paste"));
3417 const Evoral::MusicalTime snap_beats = get_grid_beats(pos);
3418 const Evoral::MusicalTime first_time = (*mcb.notes().begin())->time();
3419 const Evoral::MusicalTime last_time = (*mcb.notes().rbegin())->end_time();
3420 const Evoral::MusicalTime duration = last_time - first_time;
3421 const Evoral::MusicalTime snap_duration = duration.snap_to(snap_beats);
3422 const Evoral::MusicalTime paste_offset = snap_duration * paste_count;
3423 const Evoral::MusicalTime pos_beats = absolute_frames_to_source_beats(pos) + paste_offset;
3424 Evoral::MusicalTime end_point = Evoral::MusicalTime();
3426 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste data spans from %1 to %2 (%3) ; paste pos beats = %4 (based on %5 - %6)\n",
3429 duration, pos, _region->position(),
3434 for (int n = 0; n < (int) times; ++n) {
3436 for (Notes::const_iterator i = mcb.notes().begin(); i != mcb.notes().end(); ++i) {
3438 boost::shared_ptr<NoteType> copied_note (new NoteType (*((*i).get())));
3439 copied_note->set_time (pos_beats + copied_note->time() - first_time);
3441 /* make all newly added notes selected */
3443 note_diff_add_note (copied_note, true);
3444 end_point = copied_note->end_time();
3448 /* if we pasted past the current end of the region, extend the region */
3450 framepos_t end_frame = source_beats_to_absolute_frames (end_point);
3451 framepos_t region_end = _region->position() + _region->length() - 1;
3453 if (end_frame > region_end) {
3455 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste extended region from %1 to %2\n", region_end, end_frame));
3457 _region->clear_changes ();
3458 _region->set_length (end_frame - _region->position());
3459 trackview.session()->add_command (new StatefulDiffCommand (_region));
3465 struct EventNoteTimeEarlyFirstComparator {
3466 bool operator() (NoteBase* a, NoteBase* b) {
3467 return a->note()->time() < b->note()->time();
3472 MidiRegionView::time_sort_events ()
3474 if (!_sort_needed) {
3478 EventNoteTimeEarlyFirstComparator cmp;
3481 _sort_needed = false;
3485 MidiRegionView::goto_next_note (bool add_to_selection)
3487 bool use_next = false;
3489 if (_events.back()->selected()) {
3493 time_sort_events ();
3495 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3496 uint16_t const channel_mask = mtv->midi_track()->get_playback_channel_mask();
3498 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3499 if ((*i)->selected()) {
3502 } else if (use_next) {
3503 if (channel_mask & (1 << (*i)->note()->channel())) {
3504 if (!add_to_selection) {
3507 note_selected (*i, true, false);
3514 /* use the first one */
3516 if (!_events.empty() && (channel_mask & (1 << _events.front()->note()->channel ()))) {
3517 unique_select (_events.front());
3522 MidiRegionView::goto_previous_note (bool add_to_selection)
3524 bool use_next = false;
3526 if (_events.front()->selected()) {
3530 time_sort_events ();
3532 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3533 uint16_t const channel_mask = mtv->midi_track()->get_playback_channel_mask ();
3535 for (Events::reverse_iterator i = _events.rbegin(); i != _events.rend(); ++i) {
3536 if ((*i)->selected()) {
3539 } else if (use_next) {
3540 if (channel_mask & (1 << (*i)->note()->channel())) {
3541 if (!add_to_selection) {
3544 note_selected (*i, true, false);
3551 /* use the last one */
3553 if (!_events.empty() && (channel_mask & (1 << (*_events.rbegin())->note()->channel ()))) {
3554 unique_select (*(_events.rbegin()));
3559 MidiRegionView::selection_as_notelist (Notes& selected, bool allow_all_if_none_selected)
3561 bool had_selected = false;
3563 time_sort_events ();
3565 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3566 if ((*i)->selected()) {
3567 selected.insert ((*i)->note());
3568 had_selected = true;
3572 if (allow_all_if_none_selected && !had_selected) {
3573 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3574 selected.insert ((*i)->note());
3580 MidiRegionView::update_ghost_note (double x, double y)
3582 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3587 _note_group->canvas_to_item (x, y);
3589 PublicEditor& editor = trackview.editor ();
3591 framepos_t const unsnapped_frame = editor.pixel_to_sample (x);
3592 framecnt_t grid_frames;
3593 framepos_t const f = snap_frame_to_grid_underneath (unsnapped_frame, grid_frames);
3595 /* use region_frames... because we are converting a delta within the region
3598 const Evoral::MusicalTime length = get_grid_beats(unsnapped_frame);
3600 /* note that this sets the time of the ghost note in beats relative to
3601 the start of the source; that is how all note times are stored.
3603 _ghost_note->note()->set_time (
3604 std::max(Evoral::MusicalTime(),
3605 absolute_frames_to_source_beats (f + _region->position ())));
3606 _ghost_note->note()->set_length (length);
3607 _ghost_note->note()->set_note (midi_stream_view()->y_to_note (y));
3608 _ghost_note->note()->set_channel (mtv->get_channel_for_add ());
3610 /* the ghost note does not appear in ghost regions, so pass false in here */
3611 update_note (_ghost_note, false);
3613 show_verbose_cursor (_ghost_note->note ());
3617 MidiRegionView::create_ghost_note (double x, double y)
3619 remove_ghost_note ();
3621 boost::shared_ptr<NoteType> g (new NoteType);
3622 _ghost_note = new Note (*this, _note_group, g);
3623 _ghost_note->set_ignore_events (true);
3624 _ghost_note->set_outline_color (0x000000aa);
3625 if (x < 0) { x = 0; }
3626 update_ghost_note (x, y);
3627 _ghost_note->show ();
3632 show_verbose_cursor (_ghost_note->note ());
3636 MidiRegionView::remove_ghost_note ()
3643 MidiRegionView::snap_changed ()
3649 create_ghost_note (_last_ghost_x, _last_ghost_y);
3653 MidiRegionView::drop_down_keys ()
3655 _mouse_state = None;
3659 MidiRegionView::maybe_select_by_position (GdkEventButton* ev, double /*x*/, double y)
3661 /* XXX: This is dead code. What was it for? */
3663 double note = midi_stream_view()->y_to_note(y);
3665 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3667 uint16_t chn_mask = mtv->midi_track()->get_playback_channel_mask();
3669 if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
3670 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchGreaterThanOrEqual, (uint8_t) floor (note), chn_mask);
3671 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
3672 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchLessThanOrEqual, (uint8_t) floor (note), chn_mask);
3677 bool add_mrv_selection = false;
3679 if (_selection.empty()) {
3680 add_mrv_selection = true;
3683 for (Events::iterator i = e.begin(); i != e.end(); ++i) {
3684 if (_selection.insert (*i).second) {
3685 (*i)->set_selected (true);
3689 if (add_mrv_selection) {
3690 PublicEditor& editor (trackview.editor());
3691 editor.get_selection().add (this);
3696 MidiRegionView::color_handler ()
3698 RegionView::color_handler ();
3700 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3701 (*i)->set_selected ((*i)->selected()); // will change color
3704 /* XXX probably more to do here */
3708 MidiRegionView::enable_display (bool yn)
3710 RegionView::enable_display (yn);
3717 MidiRegionView::show_step_edit_cursor (Evoral::MusicalTime pos)
3719 if (_step_edit_cursor == 0) {
3720 ArdourCanvas::Item* const group = get_canvas_group();
3722 _step_edit_cursor = new ArdourCanvas::Rectangle (group);
3723 _step_edit_cursor->set_y0 (0);
3724 _step_edit_cursor->set_y1 (midi_stream_view()->contents_height());
3725 _step_edit_cursor->set_fill_color (RGBA_TO_UINT (45,0,0,90));
3726 _step_edit_cursor->set_outline_color (RGBA_TO_UINT (85,0,0,90));
3729 move_step_edit_cursor (pos);
3730 _step_edit_cursor->show ();
3734 MidiRegionView::move_step_edit_cursor (Evoral::MusicalTime pos)
3736 _step_edit_cursor_position = pos;
3738 if (_step_edit_cursor) {
3739 double pixel = trackview.editor().sample_to_pixel (region_beats_to_region_frames (pos));
3740 _step_edit_cursor->set_x0 (pixel);
3741 set_step_edit_cursor_width (_step_edit_cursor_width);
3746 MidiRegionView::hide_step_edit_cursor ()
3748 if (_step_edit_cursor) {
3749 _step_edit_cursor->hide ();
3754 MidiRegionView::set_step_edit_cursor_width (Evoral::MusicalTime beats)
3756 _step_edit_cursor_width = beats;
3758 if (_step_edit_cursor) {
3759 _step_edit_cursor->set_x1 (_step_edit_cursor->x0() + trackview.editor().sample_to_pixel (region_beats_to_region_frames (beats)));
3763 /** Called when a diskstream on our track has received some data. Update the view, if applicable.
3764 * @param w Source that the data will end up in.
3767 MidiRegionView::data_recorded (boost::weak_ptr<MidiSource> w)
3769 if (!_active_notes) {
3770 /* we aren't actively being recorded to */
3774 boost::shared_ptr<MidiSource> src = w.lock ();
3775 if (!src || src != midi_region()->midi_source()) {
3776 /* recorded data was not destined for our source */
3780 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (&trackview);
3782 boost::shared_ptr<MidiBuffer> buf = mtv->midi_track()->get_gui_feed_buffer ();
3784 BeatsFramesConverter converter (trackview.session()->tempo_map(), mtv->midi_track()->get_capture_start_frame (0));
3786 framepos_t back = max_framepos;
3788 for (MidiBuffer::iterator i = buf->begin(); i != buf->end(); ++i) {
3789 Evoral::MIDIEvent<MidiBuffer::TimeType> const ev (*i, false);
3791 if (ev.is_channel_event()) {
3792 if (get_channel_mode() == FilterChannels) {
3793 if (((uint16_t(1) << ev.channel()) & get_selected_channels()) == 0) {
3799 /* ev.time() is in session frames, so (ev.time() - converter.origin_b()) is
3800 frames from the start of the source, and so time_beats is in terms of the
3804 Evoral::MusicalTime const time_beats = converter.from (ev.time () - converter.origin_b ());
3806 if (ev.type() == MIDI_CMD_NOTE_ON) {
3807 boost::shared_ptr<NoteType> note (
3808 new NoteType (ev.channel(), time_beats, Evoral::MusicalTime(), ev.note(), ev.velocity()));
3810 add_note (note, true);
3812 /* fix up our note range */
3813 if (ev.note() < _current_range_min) {
3814 midi_stream_view()->apply_note_range (ev.note(), _current_range_max, true);
3815 } else if (ev.note() > _current_range_max) {
3816 midi_stream_view()->apply_note_range (_current_range_min, ev.note(), true);
3819 } else if (ev.type() == MIDI_CMD_NOTE_OFF) {
3820 resolve_note (ev.note (), time_beats);
3826 midi_stream_view()->check_record_layers (region(), back);
3830 MidiRegionView::trim_front_starting ()
3832 /* Reparent the note group to the region view's parent, so that it doesn't change
3833 when the region view is trimmed.
3835 _temporary_note_group = new ArdourCanvas::Container (group->parent ());
3836 _temporary_note_group->move (group->position ());
3837 _note_group->reparent (_temporary_note_group);
3841 MidiRegionView::trim_front_ending ()
3843 _note_group->reparent (group);
3844 delete _temporary_note_group;
3845 _temporary_note_group = 0;
3847 if (_region->start() < 0) {
3848 /* Trim drag made start time -ve; fix this */
3849 midi_region()->fix_negative_start ();
3854 MidiRegionView::edit_patch_change (PatchChange* pc)
3856 PatchChangeDialog d (&_source_relative_time_converter, trackview.session(), *pc->patch (), instrument_info(), Gtk::Stock::APPLY, true);
3858 int response = d.run();
3861 case Gtk::RESPONSE_ACCEPT:
3863 case Gtk::RESPONSE_REJECT:
3864 delete_patch_change (pc);
3870 change_patch_change (pc->patch(), d.patch ());
3874 MidiRegionView::delete_sysex (SysEx* /*sysex*/)
3877 // sysyex object doesn't have a pointer to a sysex event
3878 // MidiModel::SysExDiffCommand* c = _model->new_sysex_diff_command (_("delete sysex"));
3879 // c->remove (sysex->sysex());
3880 // _model->apply_command (*trackview.session(), c);
3882 //_sys_exes.clear ();
3883 // display_sysexes();
3887 MidiRegionView::show_verbose_cursor (boost::shared_ptr<NoteType> n) const
3889 using namespace MIDI::Name;
3893 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3895 boost::shared_ptr<MasterDeviceNames> device_names(mtv->get_device_names());
3897 MIDI::Name::PatchPrimaryKey patch_key;
3898 get_patch_key_at(n->time(), n->channel(), patch_key);
3899 name = device_names->note_name(mtv->gui_property(X_("midnam-custom-device-mode")),
3901 patch_key.bank_number,
3902 patch_key.program_number,
3908 snprintf (buf, sizeof (buf), "%d %s\nCh %d Vel %d",
3910 name.empty() ? Evoral::midi_note_name (n->note()).c_str() : name.c_str(),
3911 (int) n->channel() + 1,
3912 (int) n->velocity());
3914 show_verbose_cursor(buf, 10, 20);
3918 MidiRegionView::show_verbose_cursor (string const & text, double xoffset, double yoffset) const
3920 trackview.editor().verbose_cursor()->set (text);
3921 trackview.editor().verbose_cursor()->show ();
3922 trackview.editor().verbose_cursor()->set_offset (ArdourCanvas::Duple (xoffset, yoffset));
3925 /** @param p A session framepos.
3926 * @param grid_frames Filled in with the number of frames that a grid interval is at p.
3927 * @return p snapped to the grid subdivision underneath it.
3930 MidiRegionView::snap_frame_to_grid_underneath (framepos_t p, framecnt_t& grid_frames) const
3932 PublicEditor& editor = trackview.editor ();
3934 const Evoral::MusicalTime grid_beats = get_grid_beats(p);
3936 grid_frames = region_beats_to_region_frames (grid_beats);
3938 /* Hack so that we always snap to the note that we are over, instead of snapping
3939 to the next one if we're more than halfway through the one we're over.
3941 if (editor.snap_mode() == SnapNormal && p >= grid_frames / 2) {
3942 p -= grid_frames / 2;
3945 return snap_frame_to_frame (p);
3948 /** Called when the selection has been cleared in any MidiRegionView.
3949 * @param rv MidiRegionView that the selection was cleared in.
3952 MidiRegionView::selection_cleared (MidiRegionView* rv)
3958 /* Clear our selection in sympathy; but don't signal the fact */
3959 clear_selection (false);
3963 MidiRegionView::note_button_release ()
3965 delete _note_player;
3970 MidiRegionView::get_channel_mode () const
3972 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (&trackview);
3973 return rtav->midi_track()->get_playback_channel_mode();
3977 MidiRegionView::get_selected_channels () const
3979 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (&trackview);
3980 return rtav->midi_track()->get_playback_channel_mask();
3985 MidiRegionView::get_grid_beats(framepos_t pos) const
3987 PublicEditor& editor = trackview.editor();
3988 bool success = false;
3989 Evoral::MusicalTime beats = editor.get_grid_type_as_beats(success, pos);
3991 beats = Evoral::MusicalTime(1);