2 Copyright (C) 2001-2011 Paul Davis
3 Author: David Robillard
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 #include "gtkmm2ext/gtk_ui.h"
28 #include <sigc++/signal.h>
30 #include "pbd/memento_command.h"
31 #include "pbd/stateful_diff_command.h"
33 #include "ardour/midi_model.h"
34 #include "ardour/midi_patch_manager.h"
35 #include "ardour/midi_region.h"
36 #include "ardour/midi_source.h"
37 #include "ardour/midi_track.h"
38 #include "ardour/operations.h"
39 #include "ardour/session.h"
41 #include "evoral/Parameter.hpp"
42 #include "evoral/MIDIParameters.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 "public_editor.h"
71 #include "route_time_axis.h"
72 #include "rgb_macros.h"
73 #include "selection.h"
74 #include "streamview.h"
75 #include "patch_change_dialog.h"
76 #include "verbose_cursor.h"
77 #include "ardour_ui.h"
80 #include "patch_change.h"
85 using namespace ARDOUR;
87 using namespace Editing;
88 using Gtkmm2ext::Keyboard;
90 PBD::Signal1<void, MidiRegionView *> MidiRegionView::SelectionCleared;
92 #define MIDI_BP_ZERO ((Config->get_first_midi_bank_is_zero())?0:1)
94 MidiRegionView::MidiRegionView (ArdourCanvas::Container* parent,
95 RouteTimeAxisView& tv,
96 boost::shared_ptr<MidiRegion> r,
99 : RegionView (parent, tv, r, spu, basic_color)
100 , _current_range_min(0)
101 , _current_range_max(0)
102 , _region_relative_time_converter(r->session().tempo_map(), r->position())
103 , _source_relative_time_converter(r->session().tempo_map(), r->position() - r->start())
105 , _note_group (new ArdourCanvas::Container (group))
106 , _note_diff_command (0)
108 , _step_edit_cursor (0)
109 , _step_edit_cursor_width (1.0)
110 , _step_edit_cursor_position (0.0)
111 , _channel_selection_scoped_note (0)
112 , _temporary_note_group (0)
115 , _sort_needed (true)
116 , _optimization_iterator (_events.end())
118 , _no_sound_notes (false)
121 , pre_enter_cursor (0)
122 , pre_press_cursor (0)
123 , pre_note_enter_cursor (0)
126 CANVAS_DEBUG_NAME (_note_group, string_compose ("note group for %1", get_item_name()));
127 _note_group->raise_to_top();
128 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
130 Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&MidiRegionView::parameter_changed, this, _1), gui_context());
131 connect_to_diskstream ();
133 SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), boost::bind (&MidiRegionView::selection_cleared, this, _1), gui_context ());
136 MidiRegionView::MidiRegionView (ArdourCanvas::Container* parent,
137 RouteTimeAxisView& tv,
138 boost::shared_ptr<MidiRegion> r,
140 uint32_t basic_color,
141 TimeAxisViewItem::Visibility visibility)
142 : RegionView (parent, tv, r, spu, basic_color, false, visibility)
143 , _current_range_min(0)
144 , _current_range_max(0)
145 , _region_relative_time_converter(r->session().tempo_map(), r->position())
146 , _source_relative_time_converter(r->session().tempo_map(), r->position() - r->start())
148 , _note_group (new ArdourCanvas::Container (parent))
149 , _note_diff_command (0)
151 , _step_edit_cursor (0)
152 , _step_edit_cursor_width (1.0)
153 , _step_edit_cursor_position (0.0)
154 , _channel_selection_scoped_note (0)
155 , _temporary_note_group (0)
158 , _sort_needed (true)
159 , _optimization_iterator (_events.end())
161 , _no_sound_notes (false)
164 , pre_enter_cursor (0)
165 , pre_press_cursor (0)
166 , pre_note_enter_cursor (0)
169 CANVAS_DEBUG_NAME (_note_group, string_compose ("note group for %1", get_item_name()));
170 _note_group->raise_to_top();
172 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
174 connect_to_diskstream ();
176 SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), boost::bind (&MidiRegionView::selection_cleared, this, _1), gui_context ());
180 MidiRegionView::parameter_changed (std::string const & p)
182 if (p == "display-first-midi-bank-as-zero") {
183 if (_enable_display) {
189 MidiRegionView::MidiRegionView (const MidiRegionView& other)
190 : sigc::trackable(other)
192 , _current_range_min(0)
193 , _current_range_max(0)
194 , _region_relative_time_converter(other.region_relative_time_converter())
195 , _source_relative_time_converter(other.source_relative_time_converter())
197 , _note_group (new ArdourCanvas::Container (get_canvas_group()))
198 , _note_diff_command (0)
200 , _step_edit_cursor (0)
201 , _step_edit_cursor_width (1.0)
202 , _step_edit_cursor_position (0.0)
203 , _channel_selection_scoped_note (0)
204 , _temporary_note_group (0)
207 , _sort_needed (true)
208 , _optimization_iterator (_events.end())
210 , _no_sound_notes (false)
213 , pre_enter_cursor (0)
214 , pre_press_cursor (0)
215 , pre_note_enter_cursor (0)
221 MidiRegionView::MidiRegionView (const MidiRegionView& other, boost::shared_ptr<MidiRegion> region)
222 : RegionView (other, boost::shared_ptr<Region> (region))
223 , _current_range_min(0)
224 , _current_range_max(0)
225 , _region_relative_time_converter(other.region_relative_time_converter())
226 , _source_relative_time_converter(other.source_relative_time_converter())
228 , _note_group (new ArdourCanvas::Container (get_canvas_group()))
229 , _note_diff_command (0)
231 , _step_edit_cursor (0)
232 , _step_edit_cursor_width (1.0)
233 , _step_edit_cursor_position (0.0)
234 , _channel_selection_scoped_note (0)
235 , _temporary_note_group (0)
238 , _sort_needed (true)
239 , _optimization_iterator (_events.end())
241 , _no_sound_notes (false)
244 , pre_enter_cursor (0)
245 , pre_press_cursor (0)
246 , pre_note_enter_cursor (0)
253 MidiRegionView::init (bool wfd)
255 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
257 NoteBase::NoteBaseDeleted.connect (note_delete_connection, MISSING_INVALIDATOR,
258 boost::bind (&MidiRegionView::maybe_remove_deleted_note_from_selection, this, _1),
262 midi_region()->midi_source(0)->load_model();
265 _model = midi_region()->midi_source(0)->model();
266 _enable_display = false;
268 RegionView::init (false);
270 set_height (trackview.current_height());
273 region_sync_changed ();
274 region_resized (ARDOUR::bounds_change);
279 _enable_display = true;
282 display_model (_model);
286 reset_width_dependent_items (_pixel_width);
288 group->raise_to_top();
290 midi_view()->midi_track()->PlaybackChannelModeChanged.connect (_channel_mode_changed_connection, invalidator (*this),
291 boost::bind (&MidiRegionView::midi_channel_mode_changed, this),
294 instrument_info().Changed.connect (_instrument_changed_connection, invalidator (*this),
295 boost::bind (&MidiRegionView::instrument_settings_changed, this), gui_context());
297 trackview.editor().SnapChanged.connect(snap_changed_connection, invalidator(*this),
298 boost::bind (&MidiRegionView::snap_changed, this),
301 Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&MidiRegionView::parameter_changed, this, _1), gui_context());
302 connect_to_diskstream ();
304 SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), boost::bind (&MidiRegionView::selection_cleared, this, _1), gui_context ());
308 MidiRegionView::instrument_info () const
310 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
311 return route_ui->route()->instrument_info();
314 const boost::shared_ptr<ARDOUR::MidiRegion>
315 MidiRegionView::midi_region() const
317 return boost::dynamic_pointer_cast<ARDOUR::MidiRegion>(_region);
321 MidiRegionView::connect_to_diskstream ()
323 midi_view()->midi_track()->DataRecorded.connect(
324 *this, invalidator(*this),
325 boost::bind (&MidiRegionView::data_recorded, this, _1),
330 MidiRegionView::canvas_group_event(GdkEvent* ev)
339 case GDK_ENTER_NOTIFY:
340 case GDK_LEAVE_NOTIFY:
341 _last_event_x = ev->crossing.x;
342 _last_event_y = ev->crossing.y;
344 case GDK_MOTION_NOTIFY:
345 _last_event_x = ev->motion.x;
346 _last_event_y = ev->motion.y;
352 if (ev->type == GDK_2BUTTON_PRESS) {
353 // cannot use double-click to exit internal mode if single-click is being used
354 MouseMode m = trackview.editor().current_mouse_mode();
356 if ((m != MouseObject || !Keyboard::modifier_state_contains (ev->button.state, Keyboard::insert_note_modifier())) && (m != MouseDraw)) {
357 return trackview.editor().toggle_internal_editing_from_double_click (ev);
361 if ((!trackview.editor().internal_editing() && trackview.editor().current_mouse_mode() != MouseGain) ||
362 (trackview.editor().current_mouse_mode() == MouseTimeFX)) {
363 // handle non-internal-edit/non-draw modes elsewhere
364 return RegionView::canvas_group_event (ev);
369 if (scroll (&ev->scroll)) {
375 return key_press (&ev->key);
377 case GDK_KEY_RELEASE:
378 return key_release (&ev->key);
380 case GDK_BUTTON_PRESS:
381 return button_press (&ev->button);
383 case GDK_BUTTON_RELEASE:
384 r = button_release (&ev->button);
389 case GDK_ENTER_NOTIFY:
390 // set entered_regionview (among other things)
391 trackview.editor().canvas_region_view_event (ev, group, this);
392 return enter_notify (&ev->crossing);
394 case GDK_LEAVE_NOTIFY:
395 // reset entered_regionview (among other things)
396 trackview.editor().canvas_region_view_event (ev, group, this);
397 return leave_notify (&ev->crossing);
399 case GDK_MOTION_NOTIFY:
400 return motion (&ev->motion);
406 return trackview.editor().canvas_region_view_event (ev, group, this);
410 MidiRegionView::enter_notify (GdkEventCrossing* ev)
412 trackview.editor().MouseModeChanged.connect (
413 _mouse_mode_connection, invalidator (*this), boost::bind (&MidiRegionView::mouse_mode_changed, this), gui_context ()
416 if (trackview.editor().current_mouse_mode() == MouseDraw && _mouse_state != AddDragging) {
417 create_ghost_note (ev->x, ev->y);
420 if (!trackview.editor().internal_editing()) {
421 Keyboard::magic_widget_drop_focus();
423 Keyboard::magic_widget_grab_focus();
427 // if current operation is non-operational in a midi region, change the cursor to so indicate
428 if (trackview.editor().current_mouse_mode() == MouseGain) {
429 Editor* editor = dynamic_cast<Editor *> (&trackview.editor());
430 pre_enter_cursor = editor->get_canvas_cursor();
431 editor->set_canvas_cursor(editor->cursors()->timebar);
438 MidiRegionView::leave_notify (GdkEventCrossing*)
440 _mouse_mode_connection.disconnect ();
442 trackview.editor().verbose_cursor()->hide ();
443 remove_ghost_note ();
445 if (trackview.editor().internal_editing()) {
446 Keyboard::magic_widget_drop_focus();
449 if (pre_enter_cursor) {
450 Editor* editor = dynamic_cast<Editor *> (&trackview.editor());
451 editor->set_canvas_cursor(pre_enter_cursor);
452 pre_enter_cursor = 0;
459 MidiRegionView::mouse_mode_changed ()
461 if (trackview.editor().current_mouse_mode() == MouseDraw && trackview.editor().internal_editing()) {
462 create_ghost_note (_last_event_x, _last_event_y);
464 remove_ghost_note ();
465 trackview.editor().verbose_cursor()->hide ();
468 if (!trackview.editor().internal_editing()) {
469 Keyboard::magic_widget_drop_focus();
471 Keyboard::magic_widget_grab_focus();
477 MidiRegionView::button_press (GdkEventButton* ev)
479 if (ev->button != 1) {
483 Editor* editor = dynamic_cast<Editor *> (&trackview.editor());
484 MouseMode m = editor->current_mouse_mode();
486 if (m == MouseObject && Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())) {
487 pre_press_cursor = editor->get_canvas_cursor ();
488 editor->set_canvas_cursor (editor->cursors()->midi_pencil);
491 if (_mouse_state != SelectTouchDragging) {
493 _pressed_button = ev->button;
494 _mouse_state = Pressed;
499 _pressed_button = ev->button;
505 MidiRegionView::button_release (GdkEventButton* ev)
507 double event_x, event_y;
509 if (ev->button != 1) {
516 group->canvas_to_item (event_x, event_y);
519 PublicEditor& editor = trackview.editor ();
521 if (pre_press_cursor) {
522 dynamic_cast<Editor*>(&editor)->set_canvas_cursor (pre_press_cursor, false);
523 pre_press_cursor = 0;
526 switch (_mouse_state) {
527 case Pressed: // Clicked
529 switch (editor.current_mouse_mode()) {
531 /* no motion occured - simple click */
540 if (Keyboard::is_insert_note_event(ev)) {
542 double event_x, event_y;
546 group->canvas_to_item (event_x, event_y);
548 Evoral::MusicalTime beats = get_grid_beats(editor.pixel_to_sample(event_x));
550 /* Shorten the length by 1 tick so that we can add a new note at the next
551 grid snap without it overlapping this one.
553 beats -= Evoral::MusicalTime::tick();
555 create_note_at (editor.pixel_to_sample (event_x), event_y, beats, true);
562 Evoral::MusicalTime beats = get_grid_beats(editor.pixel_to_sample(event_x));
564 /* Shorten the length by 1 tick so that we can add a new note at the next
565 grid snap without it overlapping this one.
567 beats -= Evoral::MusicalTime::tick();
569 create_note_at (editor.pixel_to_sample (event_x), event_y, beats, true);
580 case SelectRectDragging:
582 editor.drags()->end_grab ((GdkEvent *) ev);
584 create_ghost_note (ev->x, ev->y);
596 MidiRegionView::motion (GdkEventMotion* ev)
598 PublicEditor& editor = trackview.editor ();
600 if (!_ghost_note && editor.current_mouse_mode() == MouseObject &&
601 Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier()) &&
602 _mouse_state != AddDragging) {
604 create_ghost_note (ev->x, ev->y);
606 } else if (_ghost_note && editor.current_mouse_mode() == MouseObject &&
607 Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())) {
609 update_ghost_note (ev->x, ev->y);
611 } else if (_ghost_note && editor.current_mouse_mode() == MouseObject) {
613 remove_ghost_note ();
614 editor.verbose_cursor()->hide ();
616 } else if (_ghost_note && editor.current_mouse_mode() == MouseDraw) {
618 update_ghost_note (ev->x, ev->y);
621 /* any motion immediately hides velocity text that may have been visible */
623 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
624 (*i)->hide_velocity ();
627 switch (_mouse_state) {
630 if (_pressed_button == 1) {
632 MouseMode m = editor.current_mouse_mode();
634 if (m == MouseDraw || (m == MouseObject && Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier()))) {
635 editor.drags()->set (new NoteCreateDrag (dynamic_cast<Editor *> (&editor), group, this), (GdkEvent *) ev);
636 _mouse_state = AddDragging;
637 remove_ghost_note ();
638 editor.verbose_cursor()->hide ();
640 } else if (m == MouseObject) {
641 editor.drags()->set (new MidiRubberbandSelectDrag (dynamic_cast<Editor *> (&editor), this), (GdkEvent *) ev);
643 _mouse_state = SelectRectDragging;
645 } else if (m == MouseRange) {
646 editor.drags()->set (new MidiVerticalSelectDrag (dynamic_cast<Editor *> (&editor), this), (GdkEvent *) ev);
647 _mouse_state = SelectVerticalDragging;
654 case SelectRectDragging:
655 case SelectVerticalDragging:
657 editor.drags()->motion_handler ((GdkEvent *) ev, false);
660 case SelectTouchDragging:
668 /* we may be dragging some non-note object (eg. patch-change, sysex)
671 return editor.drags()->motion_handler ((GdkEvent *) ev, false);
676 MidiRegionView::scroll (GdkEventScroll* ev)
678 if (_selection.empty()) {
682 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
683 /* XXX: bit of a hack; allow PrimaryModifier scroll through so that
684 it still works for zoom.
689 trackview.editor().verbose_cursor()->hide ();
691 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
692 bool together = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
694 if (ev->direction == GDK_SCROLL_UP) {
695 change_velocities (true, fine, false, together);
696 } else if (ev->direction == GDK_SCROLL_DOWN) {
697 change_velocities (false, fine, false, together);
699 /* left, right: we don't use them */
707 MidiRegionView::key_press (GdkEventKey* ev)
709 /* since GTK bindings are generally activated on press, and since
710 detectable auto-repeat is the name of the game and only sends
711 repeated presses, carry out key actions at key press, not release.
714 bool unmodified = Keyboard::no_modifier_keys_pressed (ev);
716 if (unmodified && (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R)) {
717 _mouse_state = SelectTouchDragging;
720 } else if (ev->keyval == GDK_Escape && unmodified) {
724 } else if (unmodified && (ev->keyval == GDK_comma || ev->keyval == GDK_period)) {
726 bool start = (ev->keyval == GDK_comma);
727 bool end = (ev->keyval == GDK_period);
728 bool shorter = Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier);
729 bool fine = Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
731 change_note_lengths (fine, shorter, Evoral::MusicalTime(), start, end);
735 } else if ((ev->keyval == GDK_BackSpace || ev->keyval == GDK_Delete) && unmodified) {
737 if (_selection.empty()) {
744 } else if (ev->keyval == GDK_Tab) {
746 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
747 goto_previous_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
749 goto_next_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
753 } else if (ev->keyval == GDK_ISO_Left_Tab) {
755 /* Shift-TAB generates ISO Left Tab, for some reason */
757 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
758 goto_previous_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
760 goto_next_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
766 } else if (ev->keyval == GDK_Up) {
768 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
769 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
770 bool together = Keyboard::modifier_state_contains (ev->state, Keyboard::Level4Modifier);
772 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
773 change_velocities (true, fine, allow_smush, together);
775 transpose (true, fine, allow_smush);
779 } else if (ev->keyval == GDK_Down) {
781 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
782 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
783 bool together = Keyboard::modifier_state_contains (ev->state, Keyboard::Level4Modifier);
785 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
786 change_velocities (false, fine, allow_smush, together);
788 transpose (false, fine, allow_smush);
792 } else if (ev->keyval == GDK_Left && unmodified) {
797 } else if (ev->keyval == GDK_Right && unmodified) {
802 } else if (ev->keyval == GDK_c && unmodified) {
806 } else if (ev->keyval == GDK_v && unmodified) {
815 MidiRegionView::key_release (GdkEventKey* ev)
817 if ((_mouse_state == SelectTouchDragging) && (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R)) {
825 MidiRegionView::channel_edit ()
827 if (_selection.empty()) {
831 /* pick a note somewhat at random (since Selection is a set<>) to
832 * provide the "current" channel for the dialog.
835 uint8_t current_channel = (*_selection.begin())->note()->channel ();
836 MidiChannelDialog channel_dialog (current_channel);
837 int ret = channel_dialog.run ();
840 case Gtk::RESPONSE_OK:
846 uint8_t new_channel = channel_dialog.active_channel ();
848 start_note_diff_command (_("channel edit"));
850 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
851 Selection::iterator next = i;
853 change_note_channel (*i, new_channel);
861 MidiRegionView::velocity_edit ()
863 if (_selection.empty()) {
867 /* pick a note somewhat at random (since Selection is a set<>) to
868 * provide the "current" velocity for the dialog.
871 uint8_t current_velocity = (*_selection.begin())->note()->velocity ();
872 MidiVelocityDialog velocity_dialog (current_velocity);
873 int ret = velocity_dialog.run ();
876 case Gtk::RESPONSE_OK:
882 uint8_t new_velocity = velocity_dialog.velocity ();
884 start_note_diff_command (_("velocity edit"));
886 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
887 Selection::iterator next = i;
889 change_note_velocity (*i, new_velocity, false);
897 MidiRegionView::show_list_editor ()
900 _list_editor = new MidiListEditor (trackview.session(), midi_region(), midi_view()->midi_track());
902 _list_editor->present ();
905 /** Add a note to the model, and the view, at a canvas (click) coordinate.
906 * \param t time in frames relative to the position of the region
907 * \param y vertical position in pixels
908 * \param length duration of the note in beats
909 * \param snap_t true to snap t to the grid, otherwise false.
912 MidiRegionView::create_note_at (framepos_t t, double y, Evoral::MusicalTime length, bool snap_t)
914 if (length < 2 * DBL_EPSILON) {
918 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
919 MidiStreamView* const view = mtv->midi_view();
921 const double note = view->y_to_note(y);
923 // Start of note in frames relative to region start
925 framecnt_t grid_frames;
926 t = snap_frame_to_grid_underneath (t, grid_frames);
929 const boost::shared_ptr<NoteType> new_note (
930 new NoteType (mtv->get_channel_for_add (),
931 region_frames_to_region_beats(t + _region->start()),
933 (uint8_t)note, 0x40));
935 if (_model->contains (new_note)) {
939 view->update_note_range(new_note->note());
941 MidiModel::NoteDiffCommand* cmd = _model->new_note_diff_command(_("add note"));
943 _model->apply_command(*trackview.session(), cmd);
945 play_midi_note (new_note);
949 MidiRegionView::clear_events (bool with_selection_signal)
951 clear_selection (with_selection_signal);
954 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
955 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
960 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
965 _patch_changes.clear();
967 _optimization_iterator = _events.end();
971 MidiRegionView::display_model(boost::shared_ptr<MidiModel> model)
975 content_connection.disconnect ();
976 _model->ContentsChanged.connect (content_connection, invalidator (*this), boost::bind (&MidiRegionView::redisplay_model, this), gui_context());
980 if (_enable_display) {
986 MidiRegionView::start_note_diff_command (string name)
988 if (!_note_diff_command) {
989 _note_diff_command = _model->new_note_diff_command (name);
994 MidiRegionView::note_diff_add_note (const boost::shared_ptr<NoteType> note, bool selected, bool show_velocity)
996 if (_note_diff_command) {
997 _note_diff_command->add (note);
1000 _marked_for_selection.insert(note);
1002 if (show_velocity) {
1003 _marked_for_velocity.insert(note);
1008 MidiRegionView::note_diff_remove_note (NoteBase* ev)
1010 if (_note_diff_command && ev->note()) {
1011 _note_diff_command->remove(ev->note());
1016 MidiRegionView::note_diff_add_change (NoteBase* ev,
1017 MidiModel::NoteDiffCommand::Property property,
1020 if (_note_diff_command) {
1021 _note_diff_command->change (ev->note(), property, val);
1026 MidiRegionView::note_diff_add_change (NoteBase* ev,
1027 MidiModel::NoteDiffCommand::Property property,
1028 Evoral::MusicalTime val)
1030 if (_note_diff_command) {
1031 _note_diff_command->change (ev->note(), property, val);
1036 MidiRegionView::apply_diff (bool as_subcommand)
1040 if (!_note_diff_command) {
1044 if ((add_or_remove = _note_diff_command->adds_or_removes())) {
1045 // Mark all selected notes for selection when model reloads
1046 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1047 _marked_for_selection.insert((*i)->note());
1051 if (as_subcommand) {
1052 _model->apply_command_as_subcommand (*trackview.session(), _note_diff_command);
1054 _model->apply_command (*trackview.session(), _note_diff_command);
1057 _note_diff_command = 0;
1058 midi_view()->midi_track()->playlist_modified();
1060 if (add_or_remove) {
1061 _marked_for_selection.clear();
1064 _marked_for_velocity.clear();
1068 MidiRegionView::abort_command()
1070 delete _note_diff_command;
1071 _note_diff_command = 0;
1076 MidiRegionView::find_canvas_note (boost::shared_ptr<NoteType> note)
1078 if (_optimization_iterator != _events.end()) {
1079 ++_optimization_iterator;
1082 if (_optimization_iterator != _events.end() && (*_optimization_iterator)->note() == note) {
1083 return *_optimization_iterator;
1086 for (_optimization_iterator = _events.begin(); _optimization_iterator != _events.end(); ++_optimization_iterator) {
1087 if ((*_optimization_iterator)->note() == note) {
1088 return *_optimization_iterator;
1096 MidiRegionView::get_events (Events& e, Evoral::Sequence<Evoral::MusicalTime>::NoteOperator op, uint8_t val, int chan_mask)
1098 MidiModel::Notes notes;
1099 _model->get_notes (notes, op, val, chan_mask);
1101 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1102 NoteBase* cne = find_canvas_note (*n);
1110 MidiRegionView::redisplay_model()
1112 // Don't redisplay the model if we're currently recording and displaying that
1113 if (_active_notes) {
1121 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1122 (*i)->invalidate ();
1125 MidiModel::ReadLock lock(_model->read_lock());
1127 MidiModel::Notes& notes (_model->notes());
1128 _optimization_iterator = _events.begin();
1130 bool empty_when_starting = _events.empty();
1132 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1134 boost::shared_ptr<NoteType> note (*n);
1138 if (note_in_region_range (note, visible)) {
1140 if (!empty_when_starting && (cne = find_canvas_note (note)) != 0) {
1147 if ((cn = dynamic_cast<Note*>(cne)) != 0) {
1149 } else if ((ch = dynamic_cast<Hit*>(cne)) != 0) {
1161 add_note (note, visible);
1166 if (!empty_when_starting && (cne = find_canvas_note (note)) != 0) {
1174 /* remove note items that are no longer valid */
1176 if (!empty_when_starting) {
1177 for (Events::iterator i = _events.begin(); i != _events.end(); ) {
1178 if (!(*i)->valid ()) {
1180 for (vector<GhostRegion*>::iterator j = ghosts.begin(); j != ghosts.end(); ++j) {
1181 MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*j);
1183 gr->remove_note (*i);
1188 i = _events.erase (i);
1196 _patch_changes.clear();
1200 display_patch_changes ();
1202 _marked_for_selection.clear ();
1203 _marked_for_velocity.clear ();
1205 /* we may have caused _events to contain things out of order (e.g. if a note
1206 moved earlier or later). we don't generally need them in time order, but
1207 make a note that a sort is required for those cases that require it.
1210 _sort_needed = true;
1214 MidiRegionView::display_patch_changes ()
1216 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1217 uint16_t chn_mask = mtv->midi_track()->get_playback_channel_mask();
1219 for (uint8_t i = 0; i < 16; ++i) {
1220 display_patch_changes_on_channel (i, chn_mask & (1 << i));
1224 /** @param active_channel true to display patch changes fully, false to display
1225 * them `greyed-out' (as on an inactive channel)
1228 MidiRegionView::display_patch_changes_on_channel (uint8_t channel, bool active_channel)
1230 for (MidiModel::PatchChanges::const_iterator i = _model->patch_changes().begin(); i != _model->patch_changes().end(); ++i) {
1232 if ((*i)->channel() != channel) {
1236 const string patch_name = instrument_info().get_patch_name ((*i)->bank(), (*i)->program(), channel);
1237 add_canvas_patch_change (*i, patch_name, active_channel);
1242 MidiRegionView::display_sysexes()
1244 bool have_periodic_system_messages = false;
1245 bool display_periodic_messages = true;
1247 if (!Config->get_never_display_periodic_midi()) {
1249 for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
1250 const boost::shared_ptr<const Evoral::MIDIEvent<Evoral::MusicalTime> > mev =
1251 boost::static_pointer_cast<const Evoral::MIDIEvent<Evoral::MusicalTime> > (*i);
1254 if (mev->is_spp() || mev->is_mtc_quarter() || mev->is_mtc_full()) {
1255 have_periodic_system_messages = true;
1261 if (have_periodic_system_messages) {
1262 double zoom = trackview.editor().get_current_zoom (); // frames per pixel
1264 /* get an approximate value for the number of samples per video frame */
1266 double video_frame = trackview.session()->frame_rate() * (1.0/30);
1268 /* if we are zoomed out beyond than the cutoff (i.e. more
1269 * frames per pixel than frames per 4 video frames), don't
1270 * show periodic sysex messages.
1273 if (zoom > (video_frame*4)) {
1274 display_periodic_messages = false;
1278 display_periodic_messages = false;
1281 for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
1283 const boost::shared_ptr<const Evoral::MIDIEvent<Evoral::MusicalTime> > mev =
1284 boost::static_pointer_cast<const Evoral::MIDIEvent<Evoral::MusicalTime> > (*i);
1286 Evoral::MusicalTime time = (*i)->time();
1289 if (mev->is_spp() || mev->is_mtc_quarter() || mev->is_mtc_full()) {
1290 if (!display_periodic_messages) {
1298 for (uint32_t b = 0; b < (*i)->size(); ++b) {
1299 str << int((*i)->buffer()[b]);
1300 if (b != (*i)->size() -1) {
1304 string text = str.str();
1306 const double x = trackview.editor().sample_to_pixel(source_beats_to_region_frames(time));
1308 double height = midi_stream_view()->contents_height();
1310 // CAIROCANVAS: no longer passing *i (the sysex event) to the
1311 // SysEx canvas object!!!
1313 boost::shared_ptr<SysEx> sysex = boost::shared_ptr<SysEx>(
1314 new SysEx (*this, _note_group, text, height, x, 1.0));
1316 // Show unless message is beyond the region bounds
1317 if (time - _region->start() >= _region->length() || time < _region->start()) {
1323 _sys_exes.push_back(sysex);
1327 MidiRegionView::~MidiRegionView ()
1329 in_destructor = true;
1331 trackview.editor().verbose_cursor()->hide ();
1333 note_delete_connection.disconnect ();
1335 delete _list_editor;
1337 RegionViewGoingAway (this); /* EMIT_SIGNAL */
1339 if (_active_notes) {
1343 _selection_cleared_connection.disconnect ();
1346 clear_events (false);
1349 delete _note_diff_command;
1350 delete _step_edit_cursor;
1351 delete _temporary_note_group;
1355 MidiRegionView::region_resized (const PropertyChange& what_changed)
1357 RegionView::region_resized(what_changed);
1359 if (what_changed.contains (ARDOUR::Properties::position)) {
1360 _region_relative_time_converter.set_origin_b(_region->position());
1361 set_duration(_region->length(), 0);
1362 if (_enable_display) {
1367 if (what_changed.contains (ARDOUR::Properties::start) ||
1368 what_changed.contains (ARDOUR::Properties::position)) {
1369 _source_relative_time_converter.set_origin_b (_region->position() - _region->start());
1374 MidiRegionView::reset_width_dependent_items (double pixel_width)
1376 RegionView::reset_width_dependent_items(pixel_width);
1378 if (_enable_display) {
1382 for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
1383 if ((*x)->canvas_item()->width() >= _pixel_width) {
1390 move_step_edit_cursor (_step_edit_cursor_position);
1391 set_step_edit_cursor_width (_step_edit_cursor_width);
1395 MidiRegionView::set_height (double height)
1397 double old_height = _height;
1398 RegionView::set_height(height);
1400 apply_note_range (midi_stream_view()->lowest_note(),
1401 midi_stream_view()->highest_note(),
1402 height != old_height);
1405 name_text->raise_to_top();
1408 for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
1409 (*x)->set_height (midi_stream_view()->contents_height());
1412 if (_step_edit_cursor) {
1413 _step_edit_cursor->set_y1 (midi_stream_view()->contents_height());
1418 /** Apply the current note range from the stream view
1419 * by repositioning/hiding notes as necessary
1422 MidiRegionView::apply_note_range (uint8_t min, uint8_t max, bool force)
1424 if (!_enable_display) {
1428 if (!force && _current_range_min == min && _current_range_max == max) {
1432 _current_range_min = min;
1433 _current_range_max = max;
1435 for (Events::const_iterator i = _events.begin(); i != _events.end(); ++i) {
1436 NoteBase* event = *i;
1437 boost::shared_ptr<NoteType> note (event->note());
1439 if (note->note() < _current_range_min ||
1440 note->note() > _current_range_max) {
1446 if (Note* cnote = dynamic_cast<Note*>(event)) {
1448 const double y0 = 1. + floor (midi_stream_view()->note_to_y(note->note()));
1449 const double y1 = y0 + std::max(1., floor(midi_stream_view()->note_height()) - 1.);
1454 } else if (Hit* chit = dynamic_cast<Hit*>(event)) {
1461 MidiRegionView::add_ghost (TimeAxisView& tv)
1465 double unit_position = _region->position () / samples_per_pixel;
1466 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(&tv);
1467 MidiGhostRegion* ghost;
1469 if (mtv && mtv->midi_view()) {
1470 /* if ghost is inserted into midi track, use a dedicated midi ghost canvas group
1471 to allow having midi notes on top of note lines and waveforms.
1473 ghost = new MidiGhostRegion (*mtv->midi_view(), trackview, unit_position);
1475 ghost = new MidiGhostRegion (tv, trackview, unit_position);
1478 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1479 if ((note = dynamic_cast<Note*>(*i)) != 0) {
1480 ghost->add_note(note);
1484 ghost->set_height ();
1485 ghost->set_duration (_region->length() / samples_per_pixel);
1486 ghosts.push_back (ghost);
1488 GhostRegion::CatchDeletion.connect (*this, invalidator (*this), boost::bind (&RegionView::remove_ghost, this, _1), gui_context());
1494 /** Begin tracking note state for successive calls to add_event
1497 MidiRegionView::begin_write()
1499 if (_active_notes) {
1500 delete[] _active_notes;
1502 _active_notes = new Note*[128];
1503 for (unsigned i = 0; i < 128; ++i) {
1504 _active_notes[i] = 0;
1509 /** Destroy note state for add_event
1512 MidiRegionView::end_write()
1514 delete[] _active_notes;
1516 _marked_for_selection.clear();
1517 _marked_for_velocity.clear();
1521 /** Resolve an active MIDI note (while recording).
1524 MidiRegionView::resolve_note(uint8_t note, Evoral::MusicalTime end_time)
1526 if (midi_view()->note_mode() != Sustained) {
1530 if (_active_notes && _active_notes[note]) {
1532 /* XXX is end_time really region-centric? I think so, because
1533 this is a new region that we're recording, so source zero is
1534 the same as region zero
1536 const framepos_t end_time_frames = region_beats_to_region_frames(end_time);
1538 _active_notes[note]->set_x1 (trackview.editor().sample_to_pixel(end_time_frames));
1539 _active_notes[note]->set_outline_all ();
1540 _active_notes[note] = 0;
1546 /** Extend active notes to rightmost edge of region (if length is changed)
1549 MidiRegionView::extend_active_notes()
1551 if (!_active_notes) {
1555 for (unsigned i=0; i < 128; ++i) {
1556 if (_active_notes[i]) {
1557 _active_notes[i]->set_x1 (trackview.editor().sample_to_pixel(_region->length()));
1564 MidiRegionView::play_midi_note(boost::shared_ptr<NoteType> note)
1566 if (_no_sound_notes || !Config->get_sound_midi_notes()) {
1570 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1572 if (!route_ui || !route_ui->midi_track()) {
1576 NotePlayer* np = new NotePlayer (route_ui->midi_track ());
1580 /* NotePlayer deletes itself */
1584 MidiRegionView::start_playing_midi_note(boost::shared_ptr<NoteType> note)
1586 if (_no_sound_notes || !Config->get_sound_midi_notes()) {
1590 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1592 if (!route_ui || !route_ui->midi_track()) {
1596 delete _note_player;
1597 _note_player = new NotePlayer (route_ui->midi_track ());
1598 _note_player->add (note);
1599 _note_player->on ();
1603 MidiRegionView::start_playing_midi_chord (vector<boost::shared_ptr<NoteType> > notes)
1605 if (_no_sound_notes || !Config->get_sound_midi_notes()) {
1609 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1611 if (!route_ui || !route_ui->midi_track()) {
1615 delete _note_player;
1616 _note_player = new NotePlayer (route_ui->midi_track());
1618 for (vector<boost::shared_ptr<NoteType> >::iterator n = notes.begin(); n != notes.end(); ++n) {
1619 _note_player->add (*n);
1622 _note_player->on ();
1627 MidiRegionView::note_in_region_range (const boost::shared_ptr<NoteType> note, bool& visible) const
1629 const framepos_t note_start_frames = source_beats_to_region_frames (note->time());
1630 bool outside = (note_start_frames < 0) || (note_start_frames > _region->last_frame());
1632 visible = (note->note() >= midi_stream_view()->lowest_note()) &&
1633 (note->note() <= midi_stream_view()->highest_note());
1638 /** Update a canvas note's size from its model note.
1639 * @param ev Canvas note to update.
1640 * @param update_ghost_regions true to update the note in any ghost regions that we have, otherwise false.
1643 MidiRegionView::update_note (Note* ev, bool update_ghost_regions)
1645 boost::shared_ptr<NoteType> note = ev->note();
1646 const double x = trackview.editor().sample_to_pixel (source_beats_to_region_frames (note->time()));
1647 const double y0 = 1 + floor(midi_stream_view()->note_to_y(note->note()));
1652 /* trim note display to not overlap the end of its region */
1654 if (note->length() > 0) {
1655 const framepos_t note_end_frames = min (source_beats_to_region_frames (note->end_time()), _region->length());
1656 ev->set_x1 (trackview.editor().sample_to_pixel (note_end_frames));
1658 ev->set_x1 (trackview.editor().sample_to_pixel (_region->length()));
1661 ev->set_y1 (y0 + std::max(1., floor(midi_stream_view()->note_height()) - 1));
1663 if (!note->length()) {
1664 if (_active_notes && note->note() < 128) {
1665 // If this note is already active there's a stuck note,
1666 // finish the old note rectangle
1667 if (_active_notes[note->note()]) {
1668 Note* const old_rect = _active_notes[note->note()];
1669 boost::shared_ptr<NoteType> old_note = old_rect->note();
1670 old_rect->set_x1 (x);
1671 old_rect->set_outline_all ();
1673 _active_notes[note->note()] = ev;
1675 /* outline all but right edge */
1676 ev->set_outline_what (ArdourCanvas::Rectangle::What (
1677 ArdourCanvas::Rectangle::TOP|
1678 ArdourCanvas::Rectangle::LEFT|
1679 ArdourCanvas::Rectangle::BOTTOM));
1681 /* outline all edges */
1682 ev->set_outline_all ();
1685 if (update_ghost_regions) {
1686 for (std::vector<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
1687 MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*i);
1689 gr->update_note (ev);
1696 MidiRegionView::update_hit (Hit* ev)
1698 boost::shared_ptr<NoteType> note = ev->note();
1700 const framepos_t note_start_frames = source_beats_to_region_frames(note->time());
1701 const double x = trackview.editor().sample_to_pixel(note_start_frames);
1702 const double diamond_size = std::max(1., floor(midi_stream_view()->note_height()) - 2.);
1703 const double y = 1.5 + floor(midi_stream_view()->note_to_y(note->note())) + diamond_size * .5;
1705 ev->set_position (ArdourCanvas::Duple (x, y));
1706 ev->set_height (diamond_size);
1709 /** Add a MIDI note to the view (with length).
1711 * If in sustained mode, notes with length 0 will be considered active
1712 * notes, and resolve_note should be called when the corresponding note off
1713 * event arrives, to properly display the note.
1716 MidiRegionView::add_note(const boost::shared_ptr<NoteType> note, bool visible)
1718 NoteBase* event = 0;
1720 if (midi_view()->note_mode() == Sustained) {
1722 Note* ev_rect = new Note (*this, _note_group, note);
1724 update_note (ev_rect);
1728 MidiGhostRegion* gr;
1730 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
1731 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
1732 gr->add_note(ev_rect);
1736 } else if (midi_view()->note_mode() == Percussive) {
1738 const double diamond_size = std::max(1., floor(midi_stream_view()->note_height()) - 2.);
1740 Hit* ev_diamond = new Hit (*this, _note_group, diamond_size, note);
1742 update_hit (ev_diamond);
1751 if (_marked_for_selection.find(note) != _marked_for_selection.end()) {
1752 note_selected(event, true);
1755 if (_marked_for_velocity.find(note) != _marked_for_velocity.end()) {
1756 event->show_velocity();
1759 event->on_channel_selection_change (get_selected_channels());
1760 _events.push_back(event);
1769 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1770 MidiStreamView* const view = mtv->midi_view();
1772 view->update_note_range (note->note());
1776 MidiRegionView::step_add_note (uint8_t channel, uint8_t number, uint8_t velocity,
1777 Evoral::MusicalTime pos, Evoral::MusicalTime len)
1779 boost::shared_ptr<NoteType> new_note (new NoteType (channel, pos, len, number, velocity));
1781 /* potentially extend region to hold new note */
1783 framepos_t end_frame = source_beats_to_absolute_frames (new_note->end_time());
1784 framepos_t region_end = _region->last_frame();
1786 if (end_frame > region_end) {
1787 _region->set_length (end_frame - _region->position());
1790 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1791 MidiStreamView* const view = mtv->midi_view();
1793 view->update_note_range(new_note->note());
1795 _marked_for_selection.clear ();
1798 start_note_diff_command (_("step add"));
1799 note_diff_add_note (new_note, true, false);
1802 // last_step_edit_note = new_note;
1806 MidiRegionView::step_sustain (Evoral::MusicalTime beats)
1808 change_note_lengths (false, false, beats, false, true);
1811 /** Add a new patch change flag to the canvas.
1812 * @param patch the patch change to add
1813 * @param the text to display in the flag
1814 * @param active_channel true to display the flag as on an active channel, false to grey it out for an inactive channel.
1817 MidiRegionView::add_canvas_patch_change (MidiModel::PatchChangePtr patch, const string& displaytext, bool /*active_channel*/)
1819 framecnt_t region_frames = source_beats_to_region_frames (patch->time());
1820 const double x = trackview.editor().sample_to_pixel (region_frames);
1822 double const height = midi_stream_view()->contents_height();
1824 // CAIROCANVAS: active_channel info removed from PatcChange constructor
1825 // so we need to do something more sophisticated to keep its color
1826 // appearance (MidiPatchChangeFill/MidiPatchChangeInactiveChannelFill)
1829 boost::shared_ptr<PatchChange> patch_change = boost::shared_ptr<PatchChange>(
1830 new PatchChange(*this, group,
1837 if (patch_change->item().width() < _pixel_width) {
1838 // Show unless patch change is beyond the region bounds
1839 if (region_frames < 0 || region_frames >= _region->length()) {
1840 patch_change->hide();
1842 patch_change->show();
1845 patch_change->hide ();
1848 _patch_changes.push_back (patch_change);
1851 MIDI::Name::PatchPrimaryKey
1852 MidiRegionView::patch_change_to_patch_key (MidiModel::PatchChangePtr p)
1854 return MIDI::Name::PatchPrimaryKey (p->program(), p->bank());
1857 /// Return true iff @p pc applies to the given time on the given channel.
1859 patch_applies (const ARDOUR::MidiModel::constPatchChangePtr pc, double time, uint8_t channel)
1861 return pc->time() <= time && pc->channel() == channel;
1865 MidiRegionView::get_patch_key_at (Evoral::MusicalTime time, uint8_t channel, MIDI::Name::PatchPrimaryKey& key) const
1867 // The earliest event not before time
1868 MidiModel::PatchChanges::iterator i = _model->patch_change_lower_bound (time);
1870 // Go backwards until we find the latest PC for this channel, or the start
1871 while (i != _model->patch_changes().begin() &&
1872 (i == _model->patch_changes().end() ||
1873 !patch_applies(*i, time, channel))) {
1877 if (i != _model->patch_changes().end() && patch_applies(*i, time, channel)) {
1878 key.bank_number = (*i)->bank();
1879 key.program_number = (*i)->program ();
1881 key.bank_number = key.program_number = 0;
1884 if (!key.is_sane()) {
1885 error << string_compose(_("insane MIDI patch key %1:%2"),
1886 key.bank_number, key.program_number) << endmsg;
1891 MidiRegionView::change_patch_change (PatchChange& pc, const MIDI::Name::PatchPrimaryKey& new_patch)
1893 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("alter patch change"));
1895 if (pc.patch()->program() != new_patch.program_number) {
1896 c->change_program (pc.patch (), new_patch.program_number);
1899 int const new_bank = new_patch.bank_number;
1900 if (pc.patch()->bank() != new_bank) {
1901 c->change_bank (pc.patch (), new_bank);
1904 _model->apply_command (*trackview.session(), c);
1906 _patch_changes.clear ();
1907 display_patch_changes ();
1911 MidiRegionView::change_patch_change (MidiModel::PatchChangePtr old_change, const Evoral::PatchChange<Evoral::MusicalTime> & new_change)
1913 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("alter patch change"));
1915 if (old_change->time() != new_change.time()) {
1916 c->change_time (old_change, new_change.time());
1919 if (old_change->channel() != new_change.channel()) {
1920 c->change_channel (old_change, new_change.channel());
1923 if (old_change->program() != new_change.program()) {
1924 c->change_program (old_change, new_change.program());
1927 if (old_change->bank() != new_change.bank()) {
1928 c->change_bank (old_change, new_change.bank());
1931 _model->apply_command (*trackview.session(), c);
1933 _patch_changes.clear ();
1934 display_patch_changes ();
1937 /** Add a patch change to the region.
1938 * @param t Time in frames relative to region position
1939 * @param patch Patch to add; time and channel are ignored (time is converted from t, and channel comes from
1940 * MidiTimeAxisView::get_channel_for_add())
1943 MidiRegionView::add_patch_change (framecnt_t t, Evoral::PatchChange<Evoral::MusicalTime> const & patch)
1945 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1947 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("add patch change"));
1948 c->add (MidiModel::PatchChangePtr (
1949 new Evoral::PatchChange<Evoral::MusicalTime> (
1950 absolute_frames_to_source_beats (_region->position() + t),
1951 mtv->get_channel_for_add(), patch.program(), patch.bank()
1956 _model->apply_command (*trackview.session(), c);
1958 _patch_changes.clear ();
1959 display_patch_changes ();
1963 MidiRegionView::move_patch_change (PatchChange& pc, Evoral::MusicalTime t)
1965 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("move patch change"));
1966 c->change_time (pc.patch (), t);
1967 _model->apply_command (*trackview.session(), c);
1969 _patch_changes.clear ();
1970 display_patch_changes ();
1974 MidiRegionView::delete_patch_change (PatchChange* pc)
1976 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("delete patch change"));
1977 c->remove (pc->patch ());
1978 _model->apply_command (*trackview.session(), c);
1980 _patch_changes.clear ();
1981 display_patch_changes ();
1985 MidiRegionView::previous_patch (PatchChange& patch)
1987 if (patch.patch()->program() < 127) {
1988 MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch());
1989 key.program_number++;
1990 change_patch_change (patch, key);
1995 MidiRegionView::next_patch (PatchChange& patch)
1997 if (patch.patch()->program() > 0) {
1998 MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch());
1999 key.program_number--;
2000 change_patch_change (patch, key);
2005 MidiRegionView::next_bank (PatchChange& patch)
2007 if (patch.patch()->program() < 127) {
2008 MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch());
2009 if (key.bank_number > 0) {
2011 change_patch_change (patch, key);
2017 MidiRegionView::previous_bank (PatchChange& patch)
2019 if (patch.patch()->program() > 0) {
2020 MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch());
2021 if (key.bank_number < 127) {
2023 change_patch_change (patch, key);
2029 MidiRegionView::maybe_remove_deleted_note_from_selection (NoteBase* cne)
2031 if (_selection.empty()) {
2035 _selection.erase (cne);
2039 MidiRegionView::delete_selection()
2041 if (_selection.empty()) {
2045 start_note_diff_command (_("delete selection"));
2047 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2048 if ((*i)->selected()) {
2049 _note_diff_command->remove((*i)->note());
2059 MidiRegionView::delete_note (boost::shared_ptr<NoteType> n)
2061 start_note_diff_command (_("delete note"));
2062 _note_diff_command->remove (n);
2065 trackview.editor().verbose_cursor()->hide ();
2069 MidiRegionView::clear_selection_except (NoteBase* ev, bool signal)
2071 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2073 Selection::iterator tmp = i;
2076 (*i)->set_selected (false);
2077 (*i)->hide_velocity ();
2078 _selection.erase (i);
2086 /* this does not change the status of this regionview w.r.t the editor
2091 SelectionCleared (this); /* EMIT SIGNAL */
2096 MidiRegionView::unique_select(NoteBase* ev)
2098 clear_selection_except (ev);
2100 /* don't bother with checking to see if we should remove this
2101 regionview from the editor selection, since we're about to add
2102 another note, and thus put/keep this regionview in the editor
2106 if (!ev->selected()) {
2107 add_to_selection (ev);
2112 MidiRegionView::select_all_notes ()
2116 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2117 add_to_selection (*i);
2122 MidiRegionView::select_range (framepos_t start, framepos_t end)
2126 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2127 framepos_t t = source_beats_to_absolute_frames((*i)->note()->time());
2128 if (t >= start && t <= end) {
2129 add_to_selection (*i);
2135 MidiRegionView::invert_selection ()
2137 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2138 if ((*i)->selected()) {
2139 remove_from_selection(*i);
2141 add_to_selection (*i);
2147 MidiRegionView::select_matching_notes (uint8_t notenum, uint16_t channel_mask, bool add, bool extend)
2149 bool have_selection = !_selection.empty();
2150 uint8_t low_note = 127;
2151 uint8_t high_note = 0;
2152 MidiModel::Notes& notes (_model->notes());
2153 _optimization_iterator = _events.begin();
2155 if (extend && !have_selection) {
2159 /* scan existing selection to get note range */
2161 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2162 if ((*i)->note()->note() < low_note) {
2163 low_note = (*i)->note()->note();
2165 if ((*i)->note()->note() > high_note) {
2166 high_note = (*i)->note()->note();
2173 if (!extend && (low_note == high_note) && (high_note == notenum)) {
2174 /* only note previously selected is the one we are
2175 * reselecting. treat this as cancelling the selection.
2182 low_note = min (low_note, notenum);
2183 high_note = max (high_note, notenum);
2186 _no_sound_notes = true;
2188 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
2190 boost::shared_ptr<NoteType> note (*n);
2192 bool select = false;
2194 if (((1 << note->channel()) & channel_mask) != 0) {
2196 if ((note->note() >= low_note && note->note() <= high_note)) {
2199 } else if (note->note() == notenum) {
2205 if ((cne = find_canvas_note (note)) != 0) {
2206 // extend is false because we've taken care of it,
2207 // since it extends by time range, not pitch.
2208 note_selected (cne, add, false);
2212 add = true; // we need to add all remaining matching notes, even if the passed in value was false (for "set")
2216 _no_sound_notes = false;
2220 MidiRegionView::toggle_matching_notes (uint8_t notenum, uint16_t channel_mask)
2222 MidiModel::Notes& notes (_model->notes());
2223 _optimization_iterator = _events.begin();
2225 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
2227 boost::shared_ptr<NoteType> note (*n);
2230 if (note->note() == notenum && (((0x0001 << note->channel()) & channel_mask) != 0)) {
2231 if ((cne = find_canvas_note (note)) != 0) {
2232 if (cne->selected()) {
2233 note_deselected (cne);
2235 note_selected (cne, true, false);
2243 MidiRegionView::note_selected (NoteBase* ev, bool add, bool extend)
2246 clear_selection_except (ev);
2247 if (!_selection.empty()) {
2248 PublicEditor& editor (trackview.editor());
2249 editor.get_selection().add (this);
2255 if (!ev->selected()) {
2256 add_to_selection (ev);
2260 /* find end of latest note selected, select all between that and the start of "ev" */
2262 Evoral::MusicalTime earliest = Evoral::MaxMusicalTime;
2263 Evoral::MusicalTime latest = Evoral::MusicalTime();
2265 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2266 if ((*i)->note()->end_time() > latest) {
2267 latest = (*i)->note()->end_time();
2269 if ((*i)->note()->time() < earliest) {
2270 earliest = (*i)->note()->time();
2274 if (ev->note()->end_time() > latest) {
2275 latest = ev->note()->end_time();
2278 if (ev->note()->time() < earliest) {
2279 earliest = ev->note()->time();
2282 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2284 /* find notes entirely within OR spanning the earliest..latest range */
2286 if (((*i)->note()->time() >= earliest && (*i)->note()->end_time() <= latest) ||
2287 ((*i)->note()->time() <= earliest && (*i)->note()->end_time() >= latest)) {
2288 add_to_selection (*i);
2296 MidiRegionView::note_deselected(NoteBase* ev)
2298 remove_from_selection (ev);
2302 MidiRegionView::update_drag_selection(framepos_t start, framepos_t end, double gy0, double gy1, bool extend)
2304 PublicEditor& editor = trackview.editor();
2306 // Convert to local coordinates
2307 const framepos_t p = _region->position();
2308 const double y = midi_view()->y_position();
2309 const double x0 = editor.sample_to_pixel(max((framepos_t)0, start - p));
2310 const double x1 = editor.sample_to_pixel(max((framepos_t)0, end - p));
2311 const double y0 = max(0.0, gy0 - y);
2312 const double y1 = max(0.0, gy1 - y);
2314 // TODO: Make this faster by storing the last updated selection rect, and only
2315 // adjusting things that are in the area that appears/disappeared.
2316 // We probably need a tree to be able to find events in O(log(n)) time.
2318 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2319 if ((*i)->x0() < x1 && (*i)->x1() > x0 && (*i)->y0() < y1 && (*i)->y1() > y0) {
2320 // Rectangles intersect
2321 if (!(*i)->selected()) {
2322 add_to_selection (*i);
2324 } else if ((*i)->selected() && !extend) {
2325 // Rectangles do not intersect
2326 remove_from_selection (*i);
2330 typedef RouteTimeAxisView::AutomationTracks ATracks;
2331 typedef std::list<Selectable*> Selectables;
2333 /* Add control points to selection. */
2334 const ATracks& atracks = midi_view()->automation_tracks();
2335 Selectables selectables;
2336 editor.get_selection().clear_points();
2337 for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) {
2338 a->second->get_selectables(start, end, gy0, gy1, selectables);
2339 for (Selectables::const_iterator s = selectables.begin(); s != selectables.end(); ++s) {
2340 ControlPoint* cp = dynamic_cast<ControlPoint*>(*s);
2342 editor.get_selection().add(cp);
2345 a->second->set_selected_points(editor.get_selection().points);
2350 MidiRegionView::update_vertical_drag_selection (double y1, double y2, bool extend)
2356 // TODO: Make this faster by storing the last updated selection rect, and only
2357 // adjusting things that are in the area that appears/disappeared.
2358 // We probably need a tree to be able to find events in O(log(n)) time.
2360 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2361 if (((*i)->y1() >= y1 && (*i)->y1() <= y2)) {
2362 // within y- (note-) range
2363 if (!(*i)->selected()) {
2364 add_to_selection (*i);
2366 } else if ((*i)->selected() && !extend) {
2367 remove_from_selection (*i);
2373 MidiRegionView::remove_from_selection (NoteBase* ev)
2375 Selection::iterator i = _selection.find (ev);
2377 if (i != _selection.end()) {
2378 _selection.erase (i);
2381 ev->set_selected (false);
2382 ev->hide_velocity ();
2384 if (_selection.empty()) {
2385 PublicEditor& editor (trackview.editor());
2386 editor.get_selection().remove (this);
2391 MidiRegionView::add_to_selection (NoteBase* ev)
2393 bool add_mrv_selection = false;
2395 if (_selection.empty()) {
2396 add_mrv_selection = true;
2399 if (_selection.insert (ev).second) {
2400 ev->set_selected (true);
2401 start_playing_midi_note ((ev)->note());
2404 if (add_mrv_selection) {
2405 PublicEditor& editor (trackview.editor());
2406 editor.get_selection().add (this);
2411 MidiRegionView::move_selection(double dx, double dy, double cumulative_dy)
2413 typedef vector<boost::shared_ptr<NoteType> > PossibleChord;
2414 PossibleChord to_play;
2415 Evoral::MusicalTime earliest = Evoral::MaxMusicalTime;
2417 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2418 if ((*i)->note()->time() < earliest) {
2419 earliest = (*i)->note()->time();
2423 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2424 if ((*i)->note()->time() == earliest) {
2425 to_play.push_back ((*i)->note());
2427 (*i)->move_event(dx, dy);
2430 if (dy && !_selection.empty() && !_no_sound_notes && Config->get_sound_midi_notes()) {
2432 if (to_play.size() > 1) {
2434 PossibleChord shifted;
2436 for (PossibleChord::iterator n = to_play.begin(); n != to_play.end(); ++n) {
2437 boost::shared_ptr<NoteType> moved_note (new NoteType (**n));
2438 moved_note->set_note (moved_note->note() + cumulative_dy);
2439 shifted.push_back (moved_note);
2442 start_playing_midi_chord (shifted);
2444 } else if (!to_play.empty()) {
2446 boost::shared_ptr<NoteType> moved_note (new NoteType (*to_play.front()));
2447 moved_note->set_note (moved_note->note() + cumulative_dy);
2448 start_playing_midi_note (moved_note);
2454 MidiRegionView::note_dropped(NoteBase *, frameoffset_t dt, int8_t dnote)
2456 uint8_t lowest_note_in_selection = 127;
2457 uint8_t highest_note_in_selection = 0;
2458 uint8_t highest_note_difference = 0;
2460 // find highest and lowest notes first
2462 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2463 uint8_t pitch = (*i)->note()->note();
2464 lowest_note_in_selection = std::min(lowest_note_in_selection, pitch);
2465 highest_note_in_selection = std::max(highest_note_in_selection, pitch);
2469 cerr << "dnote: " << (int) dnote << endl;
2470 cerr << "lowest note (streamview): " << int(midi_stream_view()->lowest_note())
2471 << " highest note (streamview): " << int(midi_stream_view()->highest_note()) << endl;
2472 cerr << "lowest note (selection): " << int(lowest_note_in_selection) << " highest note(selection): "
2473 << int(highest_note_in_selection) << endl;
2474 cerr << "selection size: " << _selection.size() << endl;
2475 cerr << "Highest note in selection: " << (int) highest_note_in_selection << endl;
2478 // Make sure the note pitch does not exceed the MIDI standard range
2479 if (highest_note_in_selection + dnote > 127) {
2480 highest_note_difference = highest_note_in_selection - 127;
2483 start_note_diff_command (_("move notes"));
2485 for (Selection::iterator i = _selection.begin(); i != _selection.end() ; ++i) {
2487 framepos_t new_frames = source_beats_to_absolute_frames ((*i)->note()->time()) + dt;
2488 Evoral::MusicalTime new_time = absolute_frames_to_source_beats (new_frames);
2494 note_diff_add_change (*i, MidiModel::NoteDiffCommand::StartTime, new_time);
2496 uint8_t original_pitch = (*i)->note()->note();
2497 uint8_t new_pitch = original_pitch + dnote - highest_note_difference;
2499 // keep notes in standard midi range
2500 clamp_to_0_127(new_pitch);
2502 lowest_note_in_selection = std::min(lowest_note_in_selection, new_pitch);
2503 highest_note_in_selection = std::max(highest_note_in_selection, new_pitch);
2505 note_diff_add_change (*i, MidiModel::NoteDiffCommand::NoteNumber, new_pitch);
2510 // care about notes being moved beyond the upper/lower bounds on the canvas
2511 if (lowest_note_in_selection < midi_stream_view()->lowest_note() ||
2512 highest_note_in_selection > midi_stream_view()->highest_note()) {
2513 midi_stream_view()->set_note_range(MidiStreamView::ContentsRange);
2517 /** @param x Pixel relative to the region position.
2518 * @return Snapped frame relative to the region position.
2521 MidiRegionView::snap_pixel_to_sample(double x)
2523 PublicEditor& editor (trackview.editor());
2524 return snap_frame_to_frame (editor.pixel_to_sample (x));
2527 /** @param x Pixel relative to the region position.
2528 * @return Snapped pixel relative to the region position.
2531 MidiRegionView::snap_to_pixel(double x)
2533 return (double) trackview.editor().sample_to_pixel(snap_pixel_to_sample(x));
2537 MidiRegionView::get_position_pixels()
2539 framepos_t region_frame = get_position();
2540 return trackview.editor().sample_to_pixel(region_frame);
2544 MidiRegionView::get_end_position_pixels()
2546 framepos_t frame = get_position() + get_duration ();
2547 return trackview.editor().sample_to_pixel(frame);
2551 MidiRegionView::source_beats_to_absolute_frames(Evoral::MusicalTime beats) const
2553 /* the time converter will return the frame corresponding to `beats'
2554 relative to the start of the source. The start of the source
2555 is an implied position given by region->position - region->start
2557 const framepos_t source_start = _region->position() - _region->start();
2558 return source_start + _source_relative_time_converter.to (beats);
2562 MidiRegionView::absolute_frames_to_source_beats(framepos_t frames) const
2564 /* the `frames' argument needs to be converted into a frame count
2565 relative to the start of the source before being passed in to the
2568 const framepos_t source_start = _region->position() - _region->start();
2569 return _source_relative_time_converter.from (frames - source_start);
2573 MidiRegionView::region_beats_to_region_frames(Evoral::MusicalTime beats) const
2575 return _region_relative_time_converter.to(beats);
2579 MidiRegionView::region_frames_to_region_beats(framepos_t frames) const
2581 return _region_relative_time_converter.from(frames);
2585 MidiRegionView::begin_resizing (bool /*at_front*/)
2587 _resize_data.clear();
2589 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2590 Note *note = dynamic_cast<Note*> (*i);
2592 // only insert CanvasNotes into the map
2594 NoteResizeData *resize_data = new NoteResizeData();
2595 resize_data->note = note;
2597 // create a new SimpleRect from the note which will be the resize preview
2598 ArdourCanvas::Rectangle *resize_rect = new ArdourCanvas::Rectangle (_note_group,
2599 ArdourCanvas::Rect (note->x0(), note->y0(), note->x0(), note->y1()));
2601 // calculate the colors: get the color settings
2602 uint32_t fill_color = UINT_RGBA_CHANGE_A(
2603 ARDOUR_UI::config()->get_MidiNoteSelected(),
2606 // make the resize preview notes more transparent and bright
2607 fill_color = UINT_INTERPOLATE(fill_color, 0xFFFFFF40, 0.5);
2609 // calculate color based on note velocity
2610 resize_rect->set_fill_color (UINT_INTERPOLATE(
2611 NoteBase::meter_style_fill_color(note->note()->velocity(), note->selected()),
2615 resize_rect->set_outline_color (NoteBase::calculate_outline (
2616 ARDOUR_UI::config()->get_MidiNoteSelected()));
2618 resize_data->resize_rect = resize_rect;
2619 _resize_data.push_back(resize_data);
2624 /** Update resizing notes while user drags.
2625 * @param primary `primary' note for the drag; ie the one that is used as the reference in non-relative mode.
2626 * @param at_front which end of the note (true == note on, false == note off)
2627 * @param delta_x change in mouse position since the start of the drag
2628 * @param relative true if relative resizing is taking place, false if absolute resizing. This only makes
2629 * a difference when multiple notes are being resized; in relative mode, each note's length is changed by the
2630 * amount of the drag. In non-relative mode, all selected notes are set to have the same start or end point
2631 * as the \a primary note.
2634 MidiRegionView::update_resizing (NoteBase* primary, bool at_front, double delta_x, bool relative)
2636 bool cursor_set = false;
2638 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2639 ArdourCanvas::Rectangle* resize_rect = (*i)->resize_rect;
2640 Note* canvas_note = (*i)->note;
2645 current_x = canvas_note->x0() + delta_x;
2647 current_x = primary->x0() + delta_x;
2651 current_x = canvas_note->x1() + delta_x;
2653 current_x = primary->x1() + delta_x;
2657 if (current_x < 0) {
2658 // This works even with snapping because RegionView::snap_frame_to_frame()
2659 // snaps forward if the snapped sample is before the beginning of the region
2662 if (current_x > trackview.editor().sample_to_pixel(_region->length())) {
2663 current_x = trackview.editor().sample_to_pixel(_region->length());
2667 resize_rect->set_x0 (snap_to_pixel(current_x));
2668 resize_rect->set_x1 (canvas_note->x1());
2670 resize_rect->set_x1 (snap_to_pixel(current_x));
2671 resize_rect->set_x0 (canvas_note->x0());
2677 beats = snap_pixel_to_sample (current_x);
2678 beats = region_frames_to_region_beats (beats);
2683 if (beats < canvas_note->note()->end_time()) {
2684 len = canvas_note->note()->time() - beats;
2685 len += canvas_note->note()->length();
2690 if (beats >= canvas_note->note()->time()) {
2691 len = beats - canvas_note->note()->time();
2698 snprintf (buf, sizeof (buf), "%.3g beats", len);
2699 show_verbose_cursor (buf, 0, 0);
2708 /** Finish resizing notes when the user releases the mouse button.
2709 * Parameters the same as for \a update_resizing().
2712 MidiRegionView::commit_resizing (NoteBase* primary, bool at_front, double delta_x, bool relative)
2714 start_note_diff_command (_("resize notes"));
2716 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2717 Note* canvas_note = (*i)->note;
2718 ArdourCanvas::Rectangle* resize_rect = (*i)->resize_rect;
2720 /* Get the new x position for this resize, which is in pixels relative
2721 * to the region position.
2728 current_x = canvas_note->x0() + delta_x;
2730 current_x = primary->x0() + delta_x;
2734 current_x = canvas_note->x1() + delta_x;
2736 current_x = primary->x1() + delta_x;
2740 if (current_x < 0) {
2743 if (current_x > trackview.editor().sample_to_pixel(_region->length())) {
2744 current_x = trackview.editor().sample_to_pixel(_region->length());
2747 /* Convert that to a frame within the source */
2748 current_x = snap_pixel_to_sample (current_x) + _region->start ();
2750 /* and then to beats */
2751 current_x = region_frames_to_region_beats (current_x);
2753 if (at_front && current_x < canvas_note->note()->end_time()) {
2754 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::StartTime, current_x);
2756 double len = canvas_note->note()->time() - current_x;
2757 len += canvas_note->note()->length();
2760 /* XXX convert to beats */
2761 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
2766 double len = current_x - canvas_note->note()->time();
2769 /* XXX convert to beats */
2770 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
2778 _resize_data.clear();
2783 MidiRegionView::abort_resizing ()
2785 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2786 delete (*i)->resize_rect;
2790 _resize_data.clear ();
2794 MidiRegionView::change_note_velocity(NoteBase* event, int8_t velocity, bool relative)
2796 uint8_t new_velocity;
2799 new_velocity = event->note()->velocity() + velocity;
2800 clamp_to_0_127(new_velocity);
2802 new_velocity = velocity;
2805 event->set_selected (event->selected()); // change color
2807 note_diff_add_change (event, MidiModel::NoteDiffCommand::Velocity, new_velocity);
2811 MidiRegionView::change_note_note (NoteBase* event, int8_t note, bool relative)
2816 new_note = event->note()->note() + note;
2821 clamp_to_0_127 (new_note);
2822 note_diff_add_change (event, MidiModel::NoteDiffCommand::NoteNumber, new_note);
2826 MidiRegionView::trim_note (NoteBase* event, Evoral::MusicalTime front_delta, Evoral::MusicalTime end_delta)
2828 bool change_start = false;
2829 bool change_length = false;
2830 Evoral::MusicalTime new_start;
2831 Evoral::MusicalTime new_length;
2833 /* NOTE: the semantics of the two delta arguments are slightly subtle:
2835 front_delta: if positive - move the start of the note later in time (shortening it)
2836 if negative - move the start of the note earlier in time (lengthening it)
2838 end_delta: if positive - move the end of the note later in time (lengthening it)
2839 if negative - move the end of the note earlier in time (shortening it)
2843 if (front_delta < 0) {
2845 if (event->note()->time() < -front_delta) {
2846 new_start = Evoral::MusicalTime();
2848 new_start = event->note()->time() + front_delta; // moves earlier
2851 /* start moved toward zero, so move the end point out to where it used to be.
2852 Note that front_delta is negative, so this increases the length.
2855 new_length = event->note()->length() - front_delta;
2856 change_start = true;
2857 change_length = true;
2861 Evoral::MusicalTime new_pos = event->note()->time() + front_delta;
2863 if (new_pos < event->note()->end_time()) {
2864 new_start = event->note()->time() + front_delta;
2865 /* start moved toward the end, so move the end point back to where it used to be */
2866 new_length = event->note()->length() - front_delta;
2867 change_start = true;
2868 change_length = true;
2875 bool can_change = true;
2876 if (end_delta < 0) {
2877 if (event->note()->length() < -end_delta) {
2883 new_length = event->note()->length() + end_delta;
2884 change_length = true;
2889 note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_start);
2892 if (change_length) {
2893 note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, new_length);
2898 MidiRegionView::change_note_channel (NoteBase* event, int8_t chn, bool relative)
2900 uint8_t new_channel;
2904 if (event->note()->channel() < -chn) {
2907 new_channel = event->note()->channel() + chn;
2910 new_channel = event->note()->channel() + chn;
2913 new_channel = (uint8_t) chn;
2916 note_diff_add_change (event, MidiModel::NoteDiffCommand::Channel, new_channel);
2920 MidiRegionView::change_note_time (NoteBase* event, Evoral::MusicalTime delta, bool relative)
2922 Evoral::MusicalTime new_time;
2926 if (event->note()->time() < -delta) {
2927 new_time = Evoral::MusicalTime();
2929 new_time = event->note()->time() + delta;
2932 new_time = event->note()->time() + delta;
2938 note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_time);
2942 MidiRegionView::change_note_length (NoteBase* event, Evoral::MusicalTime t)
2944 note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, t);
2948 MidiRegionView::change_velocities (bool up, bool fine, bool allow_smush, bool all_together)
2953 if (_selection.empty()) {
2968 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2969 if ((*i)->note()->velocity() < -delta || (*i)->note()->velocity() + delta > 127) {
2975 start_note_diff_command (_("change velocities"));
2977 for (Selection::iterator i = _selection.begin(); i != _selection.end();) {
2978 Selection::iterator next = i;
2982 if (i == _selection.begin()) {
2983 change_note_velocity (*i, delta, true);
2984 value = (*i)->note()->velocity() + delta;
2986 change_note_velocity (*i, value, false);
2990 change_note_velocity (*i, delta, true);
2999 if (!_selection.empty()) {
3001 snprintf (buf, sizeof (buf), "Vel %d",
3002 (int) (*_selection.begin())->note()->velocity());
3003 show_verbose_cursor (buf, 10, 10);
3009 MidiRegionView::transpose (bool up, bool fine, bool allow_smush)
3011 if (_selection.empty()) {
3028 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3030 if ((int8_t) (*i)->note()->note() + delta <= 0) {
3034 if ((int8_t) (*i)->note()->note() + delta > 127) {
3041 start_note_diff_command (_("transpose"));
3043 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3044 Selection::iterator next = i;
3046 change_note_note (*i, delta, true);
3054 MidiRegionView::change_note_lengths (bool fine, bool shorter, Evoral::MusicalTime delta, bool start, bool end)
3058 delta = Evoral::MusicalTime(1.0/128.0);
3060 /* grab the current grid distance */
3061 delta = get_grid_beats(_region->position());
3069 start_note_diff_command (_("change note lengths"));
3071 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3072 Selection::iterator next = i;
3075 /* note the negation of the delta for start */
3078 (start ? -delta : Evoral::MusicalTime()),
3079 (end ? delta : Evoral::MusicalTime()));
3088 MidiRegionView::nudge_notes (bool forward)
3090 if (_selection.empty()) {
3094 /* pick a note as the point along the timeline to get the nudge distance.
3095 its not necessarily the earliest note, so we may want to pull the notes out
3096 into a vector and sort before using the first one.
3099 framepos_t ref_point = source_beats_to_absolute_frames ((*(_selection.begin()))->note()->time());
3101 framecnt_t distance;
3103 if (trackview.editor().snap_mode() == Editing::SnapOff) {
3105 /* grid is off - use nudge distance */
3107 distance = trackview.editor().get_nudge_distance (ref_point, unused);
3113 framepos_t next_pos = ref_point;
3116 if (max_framepos - 1 < next_pos) {
3120 if (next_pos == 0) {
3126 trackview.editor().snap_to (next_pos, (forward ? RoundUpAlways : RoundDownAlways), false);
3127 distance = ref_point - next_pos;
3130 if (distance == 0) {
3134 Evoral::MusicalTime delta = region_frames_to_region_beats (fabs ((double)distance));
3140 start_note_diff_command (_("nudge"));
3142 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3143 Selection::iterator next = i;
3145 change_note_time (*i, delta, true);
3153 MidiRegionView::change_channel(uint8_t channel)
3155 start_note_diff_command(_("change channel"));
3156 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3157 note_diff_add_change (*i, MidiModel::NoteDiffCommand::Channel, channel);
3165 MidiRegionView::note_entered(NoteBase* ev)
3167 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3169 pre_note_enter_cursor = editor->get_canvas_cursor ();
3171 if (_mouse_state == SelectTouchDragging) {
3172 note_selected (ev, true);
3175 show_verbose_cursor (ev->note ());
3179 MidiRegionView::note_left (NoteBase*)
3181 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3183 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3184 (*i)->hide_velocity ();
3187 editor->verbose_cursor()->hide ();
3189 if (pre_note_enter_cursor) {
3190 editor->set_canvas_cursor (pre_note_enter_cursor);
3191 pre_note_enter_cursor = 0;
3196 MidiRegionView::patch_entered (PatchChange* p)
3199 /* XXX should get patch name if we can */
3200 s << _("Bank ") << (p->patch()->bank() + MIDI_BP_ZERO) << '\n'
3201 << _("Program ") << ((int) p->patch()->program()) + MIDI_BP_ZERO << '\n'
3202 << _("Channel ") << ((int) p->patch()->channel() + 1);
3203 show_verbose_cursor (s.str(), 10, 20);
3204 p->item().grab_focus();
3208 MidiRegionView::patch_left (PatchChange *)
3210 trackview.editor().verbose_cursor()->hide ();
3211 /* focus will transfer back via the enter-notify event sent to this
3217 MidiRegionView::sysex_entered (SysEx* p)
3221 // need a way to extract text from p->_flag->_text
3223 // show_verbose_cursor (s.str(), 10, 20);
3224 p->item().grab_focus();
3228 MidiRegionView::sysex_left (SysEx *)
3230 trackview.editor().verbose_cursor()->hide ();
3231 /* focus will transfer back via the enter-notify event sent to this
3237 MidiRegionView::note_mouse_position (float x_fraction, float /*y_fraction*/, bool can_set_cursor)
3239 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3240 Editing::MouseMode mm = editor->current_mouse_mode();
3241 bool trimmable = (mm == MouseObject || mm == MouseTimeFX || mm == MouseDraw);
3243 if (can_set_cursor) {
3244 if (trimmable && x_fraction > 0.0 && x_fraction < 0.2) {
3245 editor->set_canvas_cursor (editor->cursors()->left_side_trim);
3246 } else if (trimmable && x_fraction >= 0.8 && x_fraction < 1.0) {
3247 editor->set_canvas_cursor (editor->cursors()->right_side_trim);
3248 } else if (pre_note_enter_cursor) {
3249 editor->set_canvas_cursor (pre_note_enter_cursor);
3255 MidiRegionView::set_frame_color()
3259 TimeAxisViewItem::set_frame_color ();
3266 f = ARDOUR_UI::config()->get_SelectedFrameBase();
3267 } else if (high_enough_for_name) {
3268 f= ARDOUR_UI::config()->get_MidiFrameBase();
3273 if (!rect_visible) {
3274 f = UINT_RGBA_CHANGE_A (f, 80);
3277 frame->set_fill_color (f);
3281 MidiRegionView::midi_channel_mode_changed ()
3283 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3284 uint16_t mask = mtv->midi_track()->get_playback_channel_mask();
3285 ChannelMode mode = mtv->midi_track()->get_playback_channel_mode ();
3287 if (mode == ForceChannel) {
3288 mask = 0xFFFF; // Show all notes as active (below)
3291 // Update notes for selection
3292 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3293 (*i)->on_channel_selection_change (mask);
3296 _patch_changes.clear ();
3297 display_patch_changes ();
3301 MidiRegionView::instrument_settings_changed ()
3307 MidiRegionView::cut_copy_clear (Editing::CutCopyOp op)
3309 if (_selection.empty()) {
3313 PublicEditor& editor (trackview.editor());
3317 /* XXX what to do ? */
3321 editor.get_cut_buffer().add (selection_as_cut_buffer());
3329 start_note_diff_command();
3331 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3338 note_diff_remove_note (*i);
3348 MidiRegionView::selection_as_cut_buffer () const
3352 for (Selection::const_iterator i = _selection.begin(); i != _selection.end(); ++i) {
3353 NoteType* n = (*i)->note().get();
3354 notes.insert (boost::shared_ptr<NoteType> (new NoteType (*n)));
3357 MidiCutBuffer* cb = new MidiCutBuffer (trackview.session());
3363 /** This method handles undo */
3365 MidiRegionView::paste (framepos_t pos, unsigned paste_count, float times, const ::Selection& selection, ItemCounts& counts)
3367 // Get our set of notes from the selection
3368 MidiNoteSelection::const_iterator m = selection.midi_notes.get_nth(counts.n_notes());
3369 if (m == selection.midi_notes.end()) {
3372 counts.increase_n_notes();
3374 trackview.session()->begin_reversible_command (Operations::paste);
3377 paste_internal(pos, paste_count, times, **m);
3379 // Paste control points to automation children
3380 typedef RouteTimeAxisView::AutomationTracks ATracks;
3381 const ATracks& atracks = midi_view()->automation_tracks();
3382 for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) {
3383 a->second->paste(pos, paste_count, times, selection, counts);
3386 trackview.session()->commit_reversible_command ();
3391 /** This method handles undo */
3393 MidiRegionView::paste_internal (framepos_t pos, unsigned paste_count, float times, const MidiCutBuffer& mcb)
3399 PublicEditor& editor = trackview.editor ();
3401 start_note_diff_command (_("paste"));
3403 const Evoral::MusicalTime snap_beats = get_grid_beats(pos);
3404 const Evoral::MusicalTime first_time = (*mcb.notes().begin())->time();
3405 const Evoral::MusicalTime last_time = (*mcb.notes().rbegin())->end_time();
3406 const Evoral::MusicalTime duration = last_time - first_time;
3407 const Evoral::MusicalTime snap_duration = duration.snap_to(snap_beats);
3408 const Evoral::MusicalTime paste_offset = snap_duration * paste_count;
3409 const Evoral::MusicalTime pos_beats = absolute_frames_to_source_beats(pos) + paste_offset;
3410 Evoral::MusicalTime end_point = Evoral::MusicalTime();
3412 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste data spans from %1 to %2 (%3) ; paste pos beats = %4 (based on %5 - %6)\n",
3415 duration, pos, _region->position(),
3420 for (int n = 0; n < (int) times; ++n) {
3422 for (Notes::const_iterator i = mcb.notes().begin(); i != mcb.notes().end(); ++i) {
3424 boost::shared_ptr<NoteType> copied_note (new NoteType (*((*i).get())));
3425 copied_note->set_time (pos_beats + copied_note->time() - first_time);
3427 /* make all newly added notes selected */
3429 note_diff_add_note (copied_note, true);
3430 end_point = copied_note->end_time();
3434 /* if we pasted past the current end of the region, extend the region */
3436 framepos_t end_frame = source_beats_to_absolute_frames (end_point);
3437 framepos_t region_end = _region->position() + _region->length() - 1;
3439 if (end_frame > region_end) {
3441 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste extended region from %1 to %2\n", region_end, end_frame));
3443 _region->clear_changes ();
3444 _region->set_length (end_frame - _region->position());
3445 trackview.session()->add_command (new StatefulDiffCommand (_region));
3451 struct EventNoteTimeEarlyFirstComparator {
3452 bool operator() (NoteBase* a, NoteBase* b) {
3453 return a->note()->time() < b->note()->time();
3458 MidiRegionView::time_sort_events ()
3460 if (!_sort_needed) {
3464 EventNoteTimeEarlyFirstComparator cmp;
3467 _sort_needed = false;
3471 MidiRegionView::goto_next_note (bool add_to_selection)
3473 bool use_next = false;
3475 if (_events.back()->selected()) {
3479 time_sort_events ();
3481 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3482 uint16_t const channel_mask = mtv->midi_track()->get_playback_channel_mask();
3484 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3485 if ((*i)->selected()) {
3488 } else if (use_next) {
3489 if (channel_mask & (1 << (*i)->note()->channel())) {
3490 if (!add_to_selection) {
3493 note_selected (*i, true, false);
3500 /* use the first one */
3502 if (!_events.empty() && (channel_mask & (1 << _events.front()->note()->channel ()))) {
3503 unique_select (_events.front());
3508 MidiRegionView::goto_previous_note (bool add_to_selection)
3510 bool use_next = false;
3512 if (_events.front()->selected()) {
3516 time_sort_events ();
3518 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3519 uint16_t const channel_mask = mtv->midi_track()->get_playback_channel_mask ();
3521 for (Events::reverse_iterator i = _events.rbegin(); i != _events.rend(); ++i) {
3522 if ((*i)->selected()) {
3525 } else if (use_next) {
3526 if (channel_mask & (1 << (*i)->note()->channel())) {
3527 if (!add_to_selection) {
3530 note_selected (*i, true, false);
3537 /* use the last one */
3539 if (!_events.empty() && (channel_mask & (1 << (*_events.rbegin())->note()->channel ()))) {
3540 unique_select (*(_events.rbegin()));
3545 MidiRegionView::selection_as_notelist (Notes& selected, bool allow_all_if_none_selected)
3547 bool had_selected = false;
3549 time_sort_events ();
3551 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3552 if ((*i)->selected()) {
3553 selected.insert ((*i)->note());
3554 had_selected = true;
3558 if (allow_all_if_none_selected && !had_selected) {
3559 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3560 selected.insert ((*i)->note());
3566 MidiRegionView::update_ghost_note (double x, double y)
3568 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3573 _note_group->canvas_to_item (x, y);
3575 PublicEditor& editor = trackview.editor ();
3577 framepos_t const unsnapped_frame = editor.pixel_to_sample (x);
3578 framecnt_t grid_frames;
3579 framepos_t const f = snap_frame_to_grid_underneath (unsnapped_frame, grid_frames);
3581 /* use region_frames... because we are converting a delta within the region
3584 const Evoral::MusicalTime length = get_grid_beats(unsnapped_frame);
3586 /* note that this sets the time of the ghost note in beats relative to
3587 the start of the source; that is how all note times are stored.
3589 _ghost_note->note()->set_time (
3590 std::max(Evoral::MusicalTime(),
3591 absolute_frames_to_source_beats (f + _region->position ())));
3592 _ghost_note->note()->set_length (length);
3593 _ghost_note->note()->set_note (midi_stream_view()->y_to_note (y));
3594 _ghost_note->note()->set_channel (mtv->get_channel_for_add ());
3596 /* the ghost note does not appear in ghost regions, so pass false in here */
3597 update_note (_ghost_note, false);
3599 show_verbose_cursor (_ghost_note->note ());
3603 MidiRegionView::create_ghost_note (double x, double y)
3605 remove_ghost_note ();
3607 boost::shared_ptr<NoteType> g (new NoteType);
3608 _ghost_note = new Note (*this, _note_group, g);
3609 _ghost_note->set_ignore_events (true);
3610 _ghost_note->set_outline_color (0x000000aa);
3611 if (x < 0) { x = 0; }
3612 update_ghost_note (x, y);
3613 _ghost_note->show ();
3618 show_verbose_cursor (_ghost_note->note ());
3622 MidiRegionView::remove_ghost_note ()
3629 MidiRegionView::snap_changed ()
3635 create_ghost_note (_last_ghost_x, _last_ghost_y);
3639 MidiRegionView::drop_down_keys ()
3641 _mouse_state = None;
3645 MidiRegionView::maybe_select_by_position (GdkEventButton* ev, double /*x*/, double y)
3647 double note = midi_stream_view()->y_to_note(y);
3649 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3651 uint16_t chn_mask = mtv->midi_track()->get_playback_channel_mask();
3653 if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
3654 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchGreaterThanOrEqual, (uint8_t) floor (note), chn_mask);
3655 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
3656 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchLessThanOrEqual, (uint8_t) floor (note), chn_mask);
3661 bool add_mrv_selection = false;
3663 if (_selection.empty()) {
3664 add_mrv_selection = true;
3667 for (Events::iterator i = e.begin(); i != e.end(); ++i) {
3668 if (_selection.insert (*i).second) {
3669 (*i)->set_selected (true);
3673 if (add_mrv_selection) {
3674 PublicEditor& editor (trackview.editor());
3675 editor.get_selection().add (this);
3680 MidiRegionView::color_handler ()
3682 RegionView::color_handler ();
3684 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3685 (*i)->set_selected ((*i)->selected()); // will change color
3688 /* XXX probably more to do here */
3692 MidiRegionView::enable_display (bool yn)
3694 RegionView::enable_display (yn);
3701 MidiRegionView::show_step_edit_cursor (Evoral::MusicalTime pos)
3703 if (_step_edit_cursor == 0) {
3704 ArdourCanvas::Item* const group = get_canvas_group();
3706 _step_edit_cursor = new ArdourCanvas::Rectangle (group);
3707 _step_edit_cursor->set_y0 (0);
3708 _step_edit_cursor->set_y1 (midi_stream_view()->contents_height());
3709 _step_edit_cursor->set_fill_color (RGBA_TO_UINT (45,0,0,90));
3710 _step_edit_cursor->set_outline_color (RGBA_TO_UINT (85,0,0,90));
3713 move_step_edit_cursor (pos);
3714 _step_edit_cursor->show ();
3718 MidiRegionView::move_step_edit_cursor (Evoral::MusicalTime pos)
3720 _step_edit_cursor_position = pos;
3722 if (_step_edit_cursor) {
3723 double pixel = trackview.editor().sample_to_pixel (region_beats_to_region_frames (pos));
3724 _step_edit_cursor->set_x0 (pixel);
3725 set_step_edit_cursor_width (_step_edit_cursor_width);
3730 MidiRegionView::hide_step_edit_cursor ()
3732 if (_step_edit_cursor) {
3733 _step_edit_cursor->hide ();
3738 MidiRegionView::set_step_edit_cursor_width (Evoral::MusicalTime beats)
3740 _step_edit_cursor_width = beats;
3742 if (_step_edit_cursor) {
3743 _step_edit_cursor->set_x1 (_step_edit_cursor->x0() + trackview.editor().sample_to_pixel (region_beats_to_region_frames (beats)));
3747 /** Called when a diskstream on our track has received some data. Update the view, if applicable.
3748 * @param w Source that the data will end up in.
3751 MidiRegionView::data_recorded (boost::weak_ptr<MidiSource> w)
3753 if (!_active_notes) {
3754 /* we aren't actively being recorded to */
3758 boost::shared_ptr<MidiSource> src = w.lock ();
3759 if (!src || src != midi_region()->midi_source()) {
3760 /* recorded data was not destined for our source */
3764 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (&trackview);
3766 boost::shared_ptr<MidiBuffer> buf = mtv->midi_track()->get_gui_feed_buffer ();
3768 BeatsFramesConverter converter (trackview.session()->tempo_map(), mtv->midi_track()->get_capture_start_frame (0));
3770 framepos_t back = max_framepos;
3772 for (MidiBuffer::iterator i = buf->begin(); i != buf->end(); ++i) {
3773 Evoral::MIDIEvent<MidiBuffer::TimeType> const ev (*i, false);
3775 if (ev.is_channel_event()) {
3776 if (get_channel_mode() == FilterChannels) {
3777 if (((uint16_t(1) << ev.channel()) & get_selected_channels()) == 0) {
3783 /* ev.time() is in session frames, so (ev.time() - converter.origin_b()) is
3784 frames from the start of the source, and so time_beats is in terms of the
3788 Evoral::MusicalTime const time_beats = converter.from (ev.time () - converter.origin_b ());
3790 if (ev.type() == MIDI_CMD_NOTE_ON) {
3791 boost::shared_ptr<NoteType> note (
3792 new NoteType (ev.channel(), time_beats, Evoral::MusicalTime(), ev.note(), ev.velocity()));
3794 add_note (note, true);
3796 /* fix up our note range */
3797 if (ev.note() < _current_range_min) {
3798 midi_stream_view()->apply_note_range (ev.note(), _current_range_max, true);
3799 } else if (ev.note() > _current_range_max) {
3800 midi_stream_view()->apply_note_range (_current_range_min, ev.note(), true);
3803 } else if (ev.type() == MIDI_CMD_NOTE_OFF) {
3804 resolve_note (ev.note (), time_beats);
3810 midi_stream_view()->check_record_layers (region(), back);
3814 MidiRegionView::trim_front_starting ()
3816 /* Reparent the note group to the region view's parent, so that it doesn't change
3817 when the region view is trimmed.
3819 _temporary_note_group = new ArdourCanvas::Container (group->parent ());
3820 _temporary_note_group->move (group->position ());
3821 _note_group->reparent (_temporary_note_group);
3825 MidiRegionView::trim_front_ending ()
3827 _note_group->reparent (group);
3828 delete _temporary_note_group;
3829 _temporary_note_group = 0;
3831 if (_region->start() < 0) {
3832 /* Trim drag made start time -ve; fix this */
3833 midi_region()->fix_negative_start ();
3838 MidiRegionView::edit_patch_change (PatchChange* pc)
3840 PatchChangeDialog d (&_source_relative_time_converter, trackview.session(), *pc->patch (), instrument_info(), Gtk::Stock::APPLY, true);
3842 int response = d.run();
3845 case Gtk::RESPONSE_ACCEPT:
3847 case Gtk::RESPONSE_REJECT:
3848 delete_patch_change (pc);
3854 change_patch_change (pc->patch(), d.patch ());
3858 MidiRegionView::delete_sysex (SysEx* /*sysex*/)
3861 // sysyex object doesn't have a pointer to a sysex event
3862 // MidiModel::SysExDiffCommand* c = _model->new_sysex_diff_command (_("delete sysex"));
3863 // c->remove (sysex->sysex());
3864 // _model->apply_command (*trackview.session(), c);
3866 //_sys_exes.clear ();
3867 // display_sysexes();
3871 MidiRegionView::show_verbose_cursor (boost::shared_ptr<NoteType> n) const
3873 using namespace MIDI::Name;
3877 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3879 boost::shared_ptr<MasterDeviceNames> device_names(mtv->get_device_names());
3881 MIDI::Name::PatchPrimaryKey patch_key;
3882 get_patch_key_at(n->time(), n->channel(), patch_key);
3883 name = device_names->note_name(mtv->gui_property(X_("midnam-custom-device-mode")),
3885 patch_key.bank_number,
3886 patch_key.program_number,
3892 snprintf (buf, sizeof (buf), "%d %s\nCh %d Vel %d",
3894 name.empty() ? Evoral::midi_note_name (n->note()).c_str() : name.c_str(),
3895 (int) n->channel() + 1,
3896 (int) n->velocity());
3898 show_verbose_cursor(buf, 10, 20);
3902 MidiRegionView::show_verbose_cursor (string const & text, double xoffset, double yoffset) const
3904 trackview.editor().verbose_cursor()->set (text);
3905 trackview.editor().verbose_cursor()->show ();
3906 trackview.editor().verbose_cursor()->set_offset (ArdourCanvas::Duple (xoffset, yoffset));
3909 /** @param p A session framepos.
3910 * @param grid_frames Filled in with the number of frames that a grid interval is at p.
3911 * @return p snapped to the grid subdivision underneath it.
3914 MidiRegionView::snap_frame_to_grid_underneath (framepos_t p, framecnt_t& grid_frames) const
3916 PublicEditor& editor = trackview.editor ();
3918 const Evoral::MusicalTime grid_beats = get_grid_beats(p);
3920 grid_frames = region_beats_to_region_frames (grid_beats);
3922 /* Hack so that we always snap to the note that we are over, instead of snapping
3923 to the next one if we're more than halfway through the one we're over.
3925 if (editor.snap_mode() == SnapNormal && p >= grid_frames / 2) {
3926 p -= grid_frames / 2;
3929 return snap_frame_to_frame (p);
3932 /** Called when the selection has been cleared in any MidiRegionView.
3933 * @param rv MidiRegionView that the selection was cleared in.
3936 MidiRegionView::selection_cleared (MidiRegionView* rv)
3942 /* Clear our selection in sympathy; but don't signal the fact */
3943 clear_selection (false);
3947 MidiRegionView::note_button_release ()
3949 delete _note_player;
3954 MidiRegionView::get_channel_mode () const
3956 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (&trackview);
3957 return rtav->midi_track()->get_playback_channel_mode();
3961 MidiRegionView::get_selected_channels () const
3963 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (&trackview);
3964 return rtav->midi_track()->get_playback_channel_mask();
3969 MidiRegionView::get_grid_beats(framepos_t pos) const
3971 PublicEditor& editor = trackview.editor();
3972 bool success = false;
3973 Evoral::MusicalTime beats = editor.get_grid_type_as_beats(success, pos);
3975 beats = Evoral::MusicalTime(1);