2 Copyright (C) 2001-2011 Paul Davis
3 Author: David Robillard
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 #include "gtkmm2ext/gtk_ui.h"
28 #include <sigc++/signal.h>
30 #include "midi++/midnam_patch.h"
32 #include "pbd/memento_command.h"
33 #include "pbd/stateful_diff_command.h"
35 #include "ardour/midi_model.h"
36 #include "ardour/midi_region.h"
37 #include "ardour/midi_source.h"
38 #include "ardour/midi_track.h"
39 #include "ardour/operations.h"
40 #include "ardour/session.h"
42 #include "evoral/Parameter.hpp"
43 #include "evoral/MIDIEvent.hpp"
44 #include "evoral/Control.hpp"
45 #include "evoral/midi_util.h"
47 #include "canvas/debug.h"
48 #include "canvas/text.h"
50 #include "automation_region_view.h"
51 #include "automation_time_axis.h"
52 #include "control_point.h"
55 #include "editor_drag.h"
56 #include "ghostregion.h"
57 #include "gui_thread.h"
58 #include "item_counts.h"
60 #include "midi_channel_dialog.h"
61 #include "midi_cut_buffer.h"
62 #include "midi_list_editor.h"
63 #include "midi_region_view.h"
64 #include "midi_streamview.h"
65 #include "midi_time_axis.h"
66 #include "midi_util.h"
67 #include "midi_velocity_dialog.h"
68 #include "mouse_cursors.h"
69 #include "note_player.h"
70 #include "paste_context.h"
71 #include "public_editor.h"
72 #include "route_time_axis.h"
73 #include "rgb_macros.h"
74 #include "selection.h"
75 #include "streamview.h"
76 #include "patch_change_dialog.h"
77 #include "verbose_cursor.h"
78 #include "ardour_ui.h"
81 #include "patch_change.h"
86 using namespace ARDOUR;
88 using namespace Editing;
90 using Gtkmm2ext::Keyboard;
92 PBD::Signal1<void, MidiRegionView *> MidiRegionView::SelectionCleared;
94 #define MIDI_BP_ZERO ((Config->get_first_midi_bank_is_zero())?0:1)
96 MidiRegionView::MidiRegionView (ArdourCanvas::Container* parent,
97 RouteTimeAxisView& tv,
98 boost::shared_ptr<MidiRegion> r,
100 uint32_t basic_color)
101 : RegionView (parent, tv, r, spu, basic_color)
102 , _current_range_min(0)
103 , _current_range_max(0)
104 , _region_relative_time_converter(r->session().tempo_map(), r->position())
105 , _source_relative_time_converter(r->session().tempo_map(), r->position() - r->start())
107 , _note_group (new ArdourCanvas::Container (group))
108 , _note_diff_command (0)
110 , _step_edit_cursor (0)
111 , _step_edit_cursor_width (1.0)
112 , _step_edit_cursor_position (0.0)
113 , _channel_selection_scoped_note (0)
114 , _temporary_note_group (0)
117 , _sort_needed (true)
118 , _optimization_iterator (_events.end())
120 , _no_sound_notes (false)
123 , _grabbed_keyboard (false)
125 , pre_enter_cursor (0)
126 , pre_press_cursor (0)
127 , pre_note_enter_cursor (0)
130 CANVAS_DEBUG_NAME (_note_group, string_compose ("note group for %1", get_item_name()));
131 _note_group->raise_to_top();
132 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
134 Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&MidiRegionView::parameter_changed, this, _1), gui_context());
135 connect_to_diskstream ();
137 SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), boost::bind (&MidiRegionView::selection_cleared, this, _1), gui_context ());
140 MidiRegionView::MidiRegionView (ArdourCanvas::Container* parent,
141 RouteTimeAxisView& tv,
142 boost::shared_ptr<MidiRegion> r,
144 uint32_t basic_color,
146 TimeAxisViewItem::Visibility visibility)
147 : RegionView (parent, tv, r, spu, basic_color, recording, visibility)
148 , _current_range_min(0)
149 , _current_range_max(0)
150 , _region_relative_time_converter(r->session().tempo_map(), r->position())
151 , _source_relative_time_converter(r->session().tempo_map(), r->position() - r->start())
153 , _note_group (new ArdourCanvas::Container (parent))
154 , _note_diff_command (0)
156 , _step_edit_cursor (0)
157 , _step_edit_cursor_width (1.0)
158 , _step_edit_cursor_position (0.0)
159 , _channel_selection_scoped_note (0)
160 , _temporary_note_group (0)
163 , _sort_needed (true)
164 , _optimization_iterator (_events.end())
166 , _no_sound_notes (false)
169 , _grabbed_keyboard (false)
171 , pre_enter_cursor (0)
172 , pre_press_cursor (0)
173 , pre_note_enter_cursor (0)
176 CANVAS_DEBUG_NAME (_note_group, string_compose ("note group for %1", get_item_name()));
177 _note_group->raise_to_top();
179 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
181 connect_to_diskstream ();
183 SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), boost::bind (&MidiRegionView::selection_cleared, this, _1), gui_context ());
187 MidiRegionView::parameter_changed (std::string const & p)
189 if (p == "display-first-midi-bank-as-zero") {
190 if (_enable_display) {
196 MidiRegionView::MidiRegionView (const MidiRegionView& other)
197 : sigc::trackable(other)
199 , _current_range_min(0)
200 , _current_range_max(0)
201 , _region_relative_time_converter(other.region_relative_time_converter())
202 , _source_relative_time_converter(other.source_relative_time_converter())
204 , _note_group (new ArdourCanvas::Container (get_canvas_group()))
205 , _note_diff_command (0)
207 , _step_edit_cursor (0)
208 , _step_edit_cursor_width (1.0)
209 , _step_edit_cursor_position (0.0)
210 , _channel_selection_scoped_note (0)
211 , _temporary_note_group (0)
214 , _sort_needed (true)
215 , _optimization_iterator (_events.end())
217 , _no_sound_notes (false)
220 , _grabbed_keyboard (false)
222 , pre_enter_cursor (0)
223 , pre_press_cursor (0)
224 , pre_note_enter_cursor (0)
230 MidiRegionView::MidiRegionView (const MidiRegionView& other, boost::shared_ptr<MidiRegion> region)
231 : RegionView (other, boost::shared_ptr<Region> (region))
232 , _current_range_min(0)
233 , _current_range_max(0)
234 , _region_relative_time_converter(other.region_relative_time_converter())
235 , _source_relative_time_converter(other.source_relative_time_converter())
237 , _note_group (new ArdourCanvas::Container (get_canvas_group()))
238 , _note_diff_command (0)
240 , _step_edit_cursor (0)
241 , _step_edit_cursor_width (1.0)
242 , _step_edit_cursor_position (0.0)
243 , _channel_selection_scoped_note (0)
244 , _temporary_note_group (0)
247 , _sort_needed (true)
248 , _optimization_iterator (_events.end())
250 , _no_sound_notes (false)
253 , _grabbed_keyboard (false)
255 , pre_enter_cursor (0)
256 , pre_press_cursor (0)
257 , pre_note_enter_cursor (0)
264 MidiRegionView::init (bool wfd)
266 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
268 NoteBase::NoteBaseDeleted.connect (note_delete_connection, MISSING_INVALIDATOR,
269 boost::bind (&MidiRegionView::maybe_remove_deleted_note_from_selection, this, _1),
273 Glib::Threads::Mutex::Lock lm(midi_region()->midi_source(0)->mutex());
274 midi_region()->midi_source(0)->load_model(lm);
277 _model = midi_region()->midi_source(0)->model();
278 _enable_display = false;
279 _fill_color_name = "midi frame base";
281 RegionView::init (false);
283 set_height (trackview.current_height());
286 region_sync_changed ();
287 region_resized (ARDOUR::bounds_change);
292 _enable_display = true;
295 display_model (_model);
299 reset_width_dependent_items (_pixel_width);
301 group->raise_to_top();
303 midi_view()->midi_track()->PlaybackChannelModeChanged.connect (_channel_mode_changed_connection, invalidator (*this),
304 boost::bind (&MidiRegionView::midi_channel_mode_changed, this),
307 instrument_info().Changed.connect (_instrument_changed_connection, invalidator (*this),
308 boost::bind (&MidiRegionView::instrument_settings_changed, this), gui_context());
310 trackview.editor().SnapChanged.connect(snap_changed_connection, invalidator(*this),
311 boost::bind (&MidiRegionView::snap_changed, this),
314 Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&MidiRegionView::parameter_changed, this, _1), gui_context());
315 connect_to_diskstream ();
317 SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), boost::bind (&MidiRegionView::selection_cleared, this, _1), gui_context ());
321 MidiRegionView::instrument_info () const
323 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
324 return route_ui->route()->instrument_info();
327 const boost::shared_ptr<ARDOUR::MidiRegion>
328 MidiRegionView::midi_region() const
330 return boost::dynamic_pointer_cast<ARDOUR::MidiRegion>(_region);
334 MidiRegionView::connect_to_diskstream ()
336 midi_view()->midi_track()->DataRecorded.connect(
337 *this, invalidator(*this),
338 boost::bind (&MidiRegionView::data_recorded, this, _1),
343 MidiRegionView::canvas_group_event(GdkEvent* ev)
345 if (in_destructor || _recregion) {
349 if (!trackview.editor().internal_editing()) {
350 // not in internal edit mode, so just act like a normal region
351 return RegionView::canvas_group_event (ev);
357 case GDK_ENTER_NOTIFY:
358 _last_event_x = ev->crossing.x;
359 _last_event_y = ev->crossing.y;
360 enter_notify(&ev->crossing);
361 // set entered_regionview (among other things)
362 return RegionView::canvas_group_event (ev);
364 case GDK_LEAVE_NOTIFY:
365 _last_event_x = ev->crossing.x;
366 _last_event_y = ev->crossing.y;
367 leave_notify(&ev->crossing);
368 // reset entered_regionview (among other things)
369 return RegionView::canvas_group_event (ev);
372 if (scroll (&ev->scroll)) {
378 return key_press (&ev->key);
380 case GDK_KEY_RELEASE:
381 return key_release (&ev->key);
383 case GDK_BUTTON_PRESS:
384 return button_press (&ev->button);
386 case GDK_BUTTON_RELEASE:
387 r = button_release (&ev->button);
392 case GDK_MOTION_NOTIFY:
393 _last_event_x = ev->motion.x;
394 _last_event_y = ev->motion.y;
395 return motion (&ev->motion);
401 return RegionView::canvas_group_event (ev);
405 MidiRegionView::enter_notify (GdkEventCrossing* ev)
407 trackview.editor().MouseModeChanged.connect (
408 _mouse_mode_connection, invalidator (*this), boost::bind (&MidiRegionView::mouse_mode_changed, this), gui_context ()
418 MidiRegionView::leave_notify (GdkEventCrossing*)
420 _mouse_mode_connection.disconnect ();
429 MidiRegionView::mouse_mode_changed ()
431 if (trackview.editor().internal_editing()) {
432 // Switched in to internal editing mode while entered
435 // Switched out of internal editing mode while entered
441 MidiRegionView::enter_internal()
443 if (trackview.editor().current_mouse_mode() == MouseDraw && _mouse_state != AddDragging) {
444 // Show ghost note under pencil
445 create_ghost_note(_last_event_x, _last_event_y);
448 if (!_selection.empty()) {
449 // Grab keyboard for moving selected notes with arrow keys
450 Keyboard::magic_widget_grab_focus();
451 _grabbed_keyboard = true;
456 MidiRegionView::leave_internal()
458 trackview.editor().verbose_cursor()->hide ();
459 remove_ghost_note ();
461 if (_grabbed_keyboard) {
462 Keyboard::magic_widget_drop_focus();
463 _grabbed_keyboard = false;
468 MidiRegionView::button_press (GdkEventButton* ev)
470 if (ev->button != 1) {
474 Editor* editor = dynamic_cast<Editor *> (&trackview.editor());
475 MouseMode m = editor->current_mouse_mode();
477 if (m == MouseContent && Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())) {
478 pre_press_cursor = editor->get_canvas_cursor ();
479 editor->set_canvas_cursor (editor->cursors()->midi_pencil);
482 if (_mouse_state != SelectTouchDragging) {
484 _pressed_button = ev->button;
485 _mouse_state = Pressed;
490 _pressed_button = ev->button;
496 MidiRegionView::button_release (GdkEventButton* ev)
498 double event_x, event_y;
500 if (ev->button != 1) {
507 group->canvas_to_item (event_x, event_y);
510 PublicEditor& editor = trackview.editor ();
512 if (pre_press_cursor) {
513 dynamic_cast<Editor*>(&editor)->set_canvas_cursor (pre_press_cursor, false);
514 pre_press_cursor = 0;
517 switch (_mouse_state) {
518 case Pressed: // Clicked
520 switch (editor.current_mouse_mode()) {
522 /* no motion occured - simple click */
531 if (Keyboard::is_insert_note_event(ev)) {
533 double event_x, event_y;
537 group->canvas_to_item (event_x, event_y);
539 Evoral::MusicalTime beats = get_grid_beats(editor.pixel_to_sample(event_x));
541 /* Shorten the length by 1 tick so that we can add a new note at the next
542 grid snap without it overlapping this one.
544 beats -= Evoral::MusicalTime::tick();
546 create_note_at (editor.pixel_to_sample (event_x), event_y, beats, true);
553 Evoral::MusicalTime beats = get_grid_beats(editor.pixel_to_sample(event_x));
555 /* Shorten the length by 1 tick so that we can add a new note at the next
556 grid snap without it overlapping this one.
558 beats -= Evoral::MusicalTime::tick();
560 create_note_at (editor.pixel_to_sample (event_x), event_y, beats, true);
571 case SelectRectDragging:
573 editor.drags()->end_grab ((GdkEvent *) ev);
575 create_ghost_note (ev->x, ev->y);
587 MidiRegionView::motion (GdkEventMotion* ev)
589 PublicEditor& editor = trackview.editor ();
591 if (!_ghost_note && editor.current_mouse_mode() == MouseContent &&
592 Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier()) &&
593 _mouse_state != AddDragging) {
595 create_ghost_note (ev->x, ev->y);
597 } else if (_ghost_note && editor.current_mouse_mode() == MouseContent &&
598 Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())) {
600 update_ghost_note (ev->x, ev->y);
602 } else if (_ghost_note && editor.current_mouse_mode() == MouseContent) {
604 remove_ghost_note ();
605 editor.verbose_cursor()->hide ();
607 } else if (_ghost_note && editor.current_mouse_mode() == MouseDraw) {
609 update_ghost_note (ev->x, ev->y);
612 /* any motion immediately hides velocity text that may have been visible */
614 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
615 (*i)->hide_velocity ();
618 switch (_mouse_state) {
621 if (_pressed_button == 1) {
623 MouseMode m = editor.current_mouse_mode();
625 if (m == MouseDraw || (m == MouseContent && Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier()))) {
626 editor.drags()->set (new NoteCreateDrag (dynamic_cast<Editor *> (&editor), group, this), (GdkEvent *) ev);
627 _mouse_state = AddDragging;
628 remove_ghost_note ();
629 editor.verbose_cursor()->hide ();
631 } else if (m == MouseContent) {
632 editor.drags()->set (new MidiRubberbandSelectDrag (dynamic_cast<Editor *> (&editor), this), (GdkEvent *) ev);
633 if (!Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
636 _mouse_state = SelectRectDragging;
638 } else if (m == MouseRange) {
639 editor.drags()->set (new MidiVerticalSelectDrag (dynamic_cast<Editor *> (&editor), this), (GdkEvent *) ev);
640 _mouse_state = SelectVerticalDragging;
647 case SelectRectDragging:
648 case SelectVerticalDragging:
650 editor.drags()->motion_handler ((GdkEvent *) ev, false);
653 case SelectTouchDragging:
661 /* we may be dragging some non-note object (eg. patch-change, sysex)
664 return editor.drags()->motion_handler ((GdkEvent *) ev, false);
669 MidiRegionView::scroll (GdkEventScroll* ev)
671 if (_selection.empty()) {
675 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
676 /* XXX: bit of a hack; allow PrimaryModifier scroll through so that
677 it still works for zoom.
682 trackview.editor().verbose_cursor()->hide ();
684 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
685 bool together = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
687 if (ev->direction == GDK_SCROLL_UP) {
688 change_velocities (true, fine, false, together);
689 } else if (ev->direction == GDK_SCROLL_DOWN) {
690 change_velocities (false, fine, false, together);
692 /* left, right: we don't use them */
700 MidiRegionView::key_press (GdkEventKey* ev)
702 /* since GTK bindings are generally activated on press, and since
703 detectable auto-repeat is the name of the game and only sends
704 repeated presses, carry out key actions at key press, not release.
707 bool unmodified = Keyboard::no_modifier_keys_pressed (ev);
709 if (unmodified && (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R)) {
710 _mouse_state = SelectTouchDragging;
713 } else if (ev->keyval == GDK_Escape && unmodified) {
717 } else if (unmodified && (ev->keyval == GDK_comma || ev->keyval == GDK_period)) {
719 bool start = (ev->keyval == GDK_comma);
720 bool end = (ev->keyval == GDK_period);
721 bool shorter = Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier);
722 bool fine = Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
724 change_note_lengths (fine, shorter, Evoral::MusicalTime(), start, end);
728 } else if ((ev->keyval == GDK_BackSpace || ev->keyval == GDK_Delete) && unmodified) {
730 if (_selection.empty()) {
737 } else if (ev->keyval == GDK_Tab) {
739 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
740 goto_previous_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
742 goto_next_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
746 } else if (ev->keyval == GDK_ISO_Left_Tab) {
748 /* Shift-TAB generates ISO Left Tab, for some reason */
750 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
751 goto_previous_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
753 goto_next_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
759 } else if (ev->keyval == GDK_Up) {
761 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
762 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
763 bool together = Keyboard::modifier_state_contains (ev->state, Keyboard::Level4Modifier);
765 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
766 change_velocities (true, fine, allow_smush, together);
768 transpose (true, fine, allow_smush);
772 } else if (ev->keyval == GDK_Down) {
774 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
775 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
776 bool together = Keyboard::modifier_state_contains (ev->state, Keyboard::Level4Modifier);
778 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
779 change_velocities (false, fine, allow_smush, together);
781 transpose (false, fine, allow_smush);
785 } else if (ev->keyval == GDK_Left) {
787 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
788 nudge_notes (false, fine);
791 } else if (ev->keyval == GDK_Right) {
793 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
794 nudge_notes (true, fine);
797 } else if (ev->keyval == GDK_c && unmodified) {
801 } else if (ev->keyval == GDK_v && unmodified) {
810 MidiRegionView::key_release (GdkEventKey* ev)
812 if ((_mouse_state == SelectTouchDragging) && (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R)) {
820 MidiRegionView::channel_edit ()
822 if (_selection.empty()) {
826 /* pick a note somewhat at random (since Selection is a set<>) to
827 * provide the "current" channel for the dialog.
830 uint8_t current_channel = (*_selection.begin())->note()->channel ();
831 MidiChannelDialog channel_dialog (current_channel);
832 int ret = channel_dialog.run ();
835 case Gtk::RESPONSE_OK:
841 uint8_t new_channel = channel_dialog.active_channel ();
843 start_note_diff_command (_("channel edit"));
845 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
846 Selection::iterator next = i;
848 change_note_channel (*i, new_channel);
856 MidiRegionView::velocity_edit ()
858 if (_selection.empty()) {
862 /* pick a note somewhat at random (since Selection is a set<>) to
863 * provide the "current" velocity for the dialog.
866 uint8_t current_velocity = (*_selection.begin())->note()->velocity ();
867 MidiVelocityDialog velocity_dialog (current_velocity);
868 int ret = velocity_dialog.run ();
871 case Gtk::RESPONSE_OK:
877 uint8_t new_velocity = velocity_dialog.velocity ();
879 start_note_diff_command (_("velocity edit"));
881 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
882 Selection::iterator next = i;
884 change_note_velocity (*i, new_velocity, false);
892 MidiRegionView::show_list_editor ()
895 _list_editor = new MidiListEditor (trackview.session(), midi_region(), midi_view()->midi_track());
897 _list_editor->present ();
900 /** Add a note to the model, and the view, at a canvas (click) coordinate.
901 * \param t time in frames relative to the position of the region
902 * \param y vertical position in pixels
903 * \param length duration of the note in beats
904 * \param snap_t true to snap t to the grid, otherwise false.
907 MidiRegionView::create_note_at (framepos_t t, double y, Evoral::MusicalTime length, bool snap_t)
909 if (length < 2 * DBL_EPSILON) {
913 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
914 MidiStreamView* const view = mtv->midi_view();
916 const double note = view->y_to_note(y);
918 // Start of note in frames relative to region start
920 framecnt_t grid_frames;
921 t = snap_frame_to_grid_underneath (t, grid_frames);
924 const boost::shared_ptr<NoteType> new_note (
925 new NoteType (mtv->get_channel_for_add (),
926 region_frames_to_region_beats(t + _region->start()),
928 (uint8_t)note, 0x40));
930 if (_model->contains (new_note)) {
934 view->update_note_range(new_note->note());
936 MidiModel::NoteDiffCommand* cmd = _model->new_note_diff_command(_("add note"));
938 _model->apply_command(*trackview.session(), cmd);
940 play_midi_note (new_note);
944 MidiRegionView::clear_events (bool with_selection_signal)
946 clear_selection (with_selection_signal);
949 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
950 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
955 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
960 _patch_changes.clear();
962 _optimization_iterator = _events.end();
966 MidiRegionView::display_model(boost::shared_ptr<MidiModel> model)
970 content_connection.disconnect ();
971 _model->ContentsChanged.connect (content_connection, invalidator (*this), boost::bind (&MidiRegionView::redisplay_model, this), gui_context());
975 if (_enable_display) {
981 MidiRegionView::start_note_diff_command (string name)
983 if (!_note_diff_command) {
984 _note_diff_command = _model->new_note_diff_command (name);
989 MidiRegionView::note_diff_add_note (const boost::shared_ptr<NoteType> note, bool selected, bool show_velocity)
991 if (_note_diff_command) {
992 _note_diff_command->add (note);
995 _marked_for_selection.insert(note);
998 _marked_for_velocity.insert(note);
1003 MidiRegionView::note_diff_remove_note (NoteBase* ev)
1005 if (_note_diff_command && ev->note()) {
1006 _note_diff_command->remove(ev->note());
1011 MidiRegionView::note_diff_add_change (NoteBase* ev,
1012 MidiModel::NoteDiffCommand::Property property,
1015 if (_note_diff_command) {
1016 _note_diff_command->change (ev->note(), property, val);
1021 MidiRegionView::note_diff_add_change (NoteBase* ev,
1022 MidiModel::NoteDiffCommand::Property property,
1023 Evoral::MusicalTime val)
1025 if (_note_diff_command) {
1026 _note_diff_command->change (ev->note(), property, val);
1031 MidiRegionView::apply_diff (bool as_subcommand)
1035 if (!_note_diff_command) {
1039 if ((add_or_remove = _note_diff_command->adds_or_removes())) {
1040 // Mark all selected notes for selection when model reloads
1041 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1042 _marked_for_selection.insert((*i)->note());
1046 if (as_subcommand) {
1047 _model->apply_command_as_subcommand (*trackview.session(), _note_diff_command);
1049 _model->apply_command (*trackview.session(), _note_diff_command);
1052 _note_diff_command = 0;
1053 midi_view()->midi_track()->playlist_modified();
1055 if (add_or_remove) {
1056 _marked_for_selection.clear();
1059 _marked_for_velocity.clear();
1063 MidiRegionView::abort_command()
1065 delete _note_diff_command;
1066 _note_diff_command = 0;
1071 MidiRegionView::find_canvas_note (boost::shared_ptr<NoteType> note)
1073 if (_optimization_iterator != _events.end()) {
1074 ++_optimization_iterator;
1077 if (_optimization_iterator != _events.end() && (*_optimization_iterator)->note() == note) {
1078 return *_optimization_iterator;
1081 for (_optimization_iterator = _events.begin(); _optimization_iterator != _events.end(); ++_optimization_iterator) {
1082 if ((*_optimization_iterator)->note() == note) {
1083 return *_optimization_iterator;
1091 MidiRegionView::get_events (Events& e, Evoral::Sequence<Evoral::MusicalTime>::NoteOperator op, uint8_t val, int chan_mask)
1093 MidiModel::Notes notes;
1094 _model->get_notes (notes, op, val, chan_mask);
1096 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1097 NoteBase* cne = find_canvas_note (*n);
1105 MidiRegionView::redisplay_model()
1107 // Don't redisplay the model if we're currently recording and displaying that
1108 if (_active_notes) {
1116 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1117 (*i)->invalidate ();
1120 MidiModel::ReadLock lock(_model->read_lock());
1122 MidiModel::Notes& notes (_model->notes());
1123 _optimization_iterator = _events.begin();
1125 bool empty_when_starting = _events.empty();
1127 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1129 boost::shared_ptr<NoteType> note (*n);
1133 if (note_in_region_range (note, visible)) {
1135 if (!empty_when_starting && (cne = find_canvas_note (note)) != 0) {
1142 if ((cn = dynamic_cast<Note*>(cne)) != 0) {
1144 } else if ((ch = dynamic_cast<Hit*>(cne)) != 0) {
1156 add_note (note, visible);
1161 if (!empty_when_starting && (cne = find_canvas_note (note)) != 0) {
1169 /* remove note items that are no longer valid */
1171 if (!empty_when_starting) {
1172 for (Events::iterator i = _events.begin(); i != _events.end(); ) {
1173 if (!(*i)->valid ()) {
1175 for (vector<GhostRegion*>::iterator j = ghosts.begin(); j != ghosts.end(); ++j) {
1176 MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*j);
1178 gr->remove_note (*i);
1183 i = _events.erase (i);
1191 _patch_changes.clear();
1195 display_patch_changes ();
1197 _marked_for_selection.clear ();
1198 _marked_for_velocity.clear ();
1200 /* we may have caused _events to contain things out of order (e.g. if a note
1201 moved earlier or later). we don't generally need them in time order, but
1202 make a note that a sort is required for those cases that require it.
1205 _sort_needed = true;
1209 MidiRegionView::display_patch_changes ()
1211 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1212 uint16_t chn_mask = mtv->midi_track()->get_playback_channel_mask();
1214 for (uint8_t i = 0; i < 16; ++i) {
1215 display_patch_changes_on_channel (i, chn_mask & (1 << i));
1219 /** @param active_channel true to display patch changes fully, false to display
1220 * them `greyed-out' (as on an inactive channel)
1223 MidiRegionView::display_patch_changes_on_channel (uint8_t channel, bool active_channel)
1225 for (MidiModel::PatchChanges::const_iterator i = _model->patch_changes().begin(); i != _model->patch_changes().end(); ++i) {
1227 if ((*i)->channel() != channel) {
1231 const string patch_name = instrument_info().get_patch_name ((*i)->bank(), (*i)->program(), channel);
1232 add_canvas_patch_change (*i, patch_name, active_channel);
1237 MidiRegionView::display_sysexes()
1239 bool have_periodic_system_messages = false;
1240 bool display_periodic_messages = true;
1242 if (!Config->get_never_display_periodic_midi()) {
1244 for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
1245 const boost::shared_ptr<const Evoral::MIDIEvent<Evoral::MusicalTime> > mev =
1246 boost::static_pointer_cast<const Evoral::MIDIEvent<Evoral::MusicalTime> > (*i);
1249 if (mev->is_spp() || mev->is_mtc_quarter() || mev->is_mtc_full()) {
1250 have_periodic_system_messages = true;
1256 if (have_periodic_system_messages) {
1257 double zoom = trackview.editor().get_current_zoom (); // frames per pixel
1259 /* get an approximate value for the number of samples per video frame */
1261 double video_frame = trackview.session()->frame_rate() * (1.0/30);
1263 /* if we are zoomed out beyond than the cutoff (i.e. more
1264 * frames per pixel than frames per 4 video frames), don't
1265 * show periodic sysex messages.
1268 if (zoom > (video_frame*4)) {
1269 display_periodic_messages = false;
1273 display_periodic_messages = false;
1276 for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
1278 const boost::shared_ptr<const Evoral::MIDIEvent<Evoral::MusicalTime> > mev =
1279 boost::static_pointer_cast<const Evoral::MIDIEvent<Evoral::MusicalTime> > (*i);
1281 Evoral::MusicalTime time = (*i)->time();
1284 if (mev->is_spp() || mev->is_mtc_quarter() || mev->is_mtc_full()) {
1285 if (!display_periodic_messages) {
1293 for (uint32_t b = 0; b < (*i)->size(); ++b) {
1294 str << int((*i)->buffer()[b]);
1295 if (b != (*i)->size() -1) {
1299 string text = str.str();
1301 const double x = trackview.editor().sample_to_pixel(source_beats_to_region_frames(time));
1303 double height = midi_stream_view()->contents_height();
1305 // CAIROCANVAS: no longer passing *i (the sysex event) to the
1306 // SysEx canvas object!!!
1308 boost::shared_ptr<SysEx> sysex = boost::shared_ptr<SysEx>(
1309 new SysEx (*this, _note_group, text, height, x, 1.0));
1311 // Show unless message is beyond the region bounds
1312 if (time - _region->start() >= _region->length() || time < _region->start()) {
1318 _sys_exes.push_back(sysex);
1322 MidiRegionView::~MidiRegionView ()
1324 in_destructor = true;
1326 trackview.editor().verbose_cursor()->hide ();
1328 note_delete_connection.disconnect ();
1330 delete _list_editor;
1332 RegionViewGoingAway (this); /* EMIT_SIGNAL */
1334 if (_active_notes) {
1338 _selection_cleared_connection.disconnect ();
1341 clear_events (false);
1344 delete _note_diff_command;
1345 delete _step_edit_cursor;
1346 delete _temporary_note_group;
1350 MidiRegionView::region_resized (const PropertyChange& what_changed)
1352 RegionView::region_resized(what_changed);
1354 if (what_changed.contains (ARDOUR::Properties::position)) {
1355 _region_relative_time_converter.set_origin_b(_region->position());
1356 set_duration(_region->length(), 0);
1357 if (_enable_display) {
1362 if (what_changed.contains (ARDOUR::Properties::start) ||
1363 what_changed.contains (ARDOUR::Properties::position)) {
1364 _source_relative_time_converter.set_origin_b (_region->position() - _region->start());
1369 MidiRegionView::reset_width_dependent_items (double pixel_width)
1371 RegionView::reset_width_dependent_items(pixel_width);
1373 if (_enable_display) {
1377 for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
1378 if ((*x)->canvas_item()->width() >= _pixel_width) {
1385 move_step_edit_cursor (_step_edit_cursor_position);
1386 set_step_edit_cursor_width (_step_edit_cursor_width);
1390 MidiRegionView::set_height (double height)
1392 double old_height = _height;
1393 RegionView::set_height(height);
1395 apply_note_range (midi_stream_view()->lowest_note(),
1396 midi_stream_view()->highest_note(),
1397 height != old_height);
1400 name_text->raise_to_top();
1403 for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
1404 (*x)->set_height (midi_stream_view()->contents_height());
1407 if (_step_edit_cursor) {
1408 _step_edit_cursor->set_y1 (midi_stream_view()->contents_height());
1413 /** Apply the current note range from the stream view
1414 * by repositioning/hiding notes as necessary
1417 MidiRegionView::apply_note_range (uint8_t min, uint8_t max, bool force)
1419 if (!_enable_display) {
1423 if (!force && _current_range_min == min && _current_range_max == max) {
1427 _current_range_min = min;
1428 _current_range_max = max;
1430 for (Events::const_iterator i = _events.begin(); i != _events.end(); ++i) {
1431 NoteBase* event = *i;
1432 boost::shared_ptr<NoteType> note (event->note());
1434 if (note->note() < _current_range_min ||
1435 note->note() > _current_range_max) {
1441 if (Note* cnote = dynamic_cast<Note*>(event)) {
1443 const double y0 = 1. + floor (midi_stream_view()->note_to_y(note->note()));
1444 const double y1 = y0 + std::max(1., floor(midi_stream_view()->note_height()) - 1.);
1449 } else if (Hit* chit = dynamic_cast<Hit*>(event)) {
1456 MidiRegionView::add_ghost (TimeAxisView& tv)
1460 double unit_position = _region->position () / samples_per_pixel;
1461 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(&tv);
1462 MidiGhostRegion* ghost;
1464 if (mtv && mtv->midi_view()) {
1465 /* if ghost is inserted into midi track, use a dedicated midi ghost canvas group
1466 to allow having midi notes on top of note lines and waveforms.
1468 ghost = new MidiGhostRegion (*mtv->midi_view(), trackview, unit_position);
1470 ghost = new MidiGhostRegion (tv, trackview, unit_position);
1473 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1474 if ((note = dynamic_cast<Note*>(*i)) != 0) {
1475 ghost->add_note(note);
1479 ghost->set_height ();
1480 ghost->set_duration (_region->length() / samples_per_pixel);
1481 ghosts.push_back (ghost);
1483 GhostRegion::CatchDeletion.connect (*this, invalidator (*this), boost::bind (&RegionView::remove_ghost, this, _1), gui_context());
1489 /** Begin tracking note state for successive calls to add_event
1492 MidiRegionView::begin_write()
1494 if (_active_notes) {
1495 delete[] _active_notes;
1497 _active_notes = new Note*[128];
1498 for (unsigned i = 0; i < 128; ++i) {
1499 _active_notes[i] = 0;
1504 /** Destroy note state for add_event
1507 MidiRegionView::end_write()
1509 delete[] _active_notes;
1511 _marked_for_selection.clear();
1512 _marked_for_velocity.clear();
1516 /** Resolve an active MIDI note (while recording).
1519 MidiRegionView::resolve_note(uint8_t note, Evoral::MusicalTime end_time)
1521 if (midi_view()->note_mode() != Sustained) {
1525 if (_active_notes && _active_notes[note]) {
1527 /* XXX is end_time really region-centric? I think so, because
1528 this is a new region that we're recording, so source zero is
1529 the same as region zero
1531 const framepos_t end_time_frames = region_beats_to_region_frames(end_time);
1533 _active_notes[note]->set_x1 (trackview.editor().sample_to_pixel(end_time_frames));
1534 _active_notes[note]->set_outline_all ();
1535 _active_notes[note] = 0;
1541 /** Extend active notes to rightmost edge of region (if length is changed)
1544 MidiRegionView::extend_active_notes()
1546 if (!_active_notes) {
1550 for (unsigned i=0; i < 128; ++i) {
1551 if (_active_notes[i]) {
1552 _active_notes[i]->set_x1(
1553 trackview.editor().sample_to_pixel(_region->position() + _region->length()));
1560 MidiRegionView::play_midi_note(boost::shared_ptr<NoteType> note)
1562 if (_no_sound_notes || !Config->get_sound_midi_notes()) {
1566 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1568 if (!route_ui || !route_ui->midi_track()) {
1572 NotePlayer* np = new NotePlayer (route_ui->midi_track ());
1576 /* NotePlayer deletes itself */
1580 MidiRegionView::start_playing_midi_note(boost::shared_ptr<NoteType> note)
1582 if (_no_sound_notes || !Config->get_sound_midi_notes()) {
1586 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1588 if (!route_ui || !route_ui->midi_track()) {
1592 delete _note_player;
1593 _note_player = new NotePlayer (route_ui->midi_track ());
1594 _note_player->add (note);
1595 _note_player->on ();
1599 MidiRegionView::start_playing_midi_chord (vector<boost::shared_ptr<NoteType> > notes)
1601 if (_no_sound_notes || !Config->get_sound_midi_notes()) {
1605 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1607 if (!route_ui || !route_ui->midi_track()) {
1611 delete _note_player;
1612 _note_player = new NotePlayer (route_ui->midi_track());
1614 for (vector<boost::shared_ptr<NoteType> >::iterator n = notes.begin(); n != notes.end(); ++n) {
1615 _note_player->add (*n);
1618 _note_player->on ();
1623 MidiRegionView::note_in_region_range (const boost::shared_ptr<NoteType> note, bool& visible) const
1625 const framepos_t note_start_frames = source_beats_to_region_frames (note->time());
1626 bool outside = (note_start_frames < 0) || (note_start_frames > _region->last_frame());
1628 visible = (note->note() >= midi_stream_view()->lowest_note()) &&
1629 (note->note() <= midi_stream_view()->highest_note());
1634 /** Update a canvas note's size from its model note.
1635 * @param ev Canvas note to update.
1636 * @param update_ghost_regions true to update the note in any ghost regions that we have, otherwise false.
1639 MidiRegionView::update_note (Note* ev, bool update_ghost_regions)
1641 boost::shared_ptr<NoteType> note = ev->note();
1642 const double x = trackview.editor().sample_to_pixel (source_beats_to_region_frames (note->time()));
1643 const double y0 = 1 + floor(midi_stream_view()->note_to_y(note->note()));
1648 /* trim note display to not overlap the end of its region */
1650 if (note->length() > 0) {
1651 const framepos_t note_end_frames = min (source_beats_to_region_frames (note->end_time()), _region->length());
1652 ev->set_x1 (trackview.editor().sample_to_pixel (note_end_frames));
1654 ev->set_x1 (trackview.editor().sample_to_pixel (_region->length()));
1657 ev->set_y1 (y0 + std::max(1., floor(midi_stream_view()->note_height()) - 1));
1659 if (!note->length()) {
1660 if (_active_notes && note->note() < 128) {
1661 // If this note is already active there's a stuck note,
1662 // finish the old note rectangle
1663 if (_active_notes[note->note()]) {
1664 Note* const old_rect = _active_notes[note->note()];
1665 boost::shared_ptr<NoteType> old_note = old_rect->note();
1666 old_rect->set_x1 (x);
1667 old_rect->set_outline_all ();
1669 _active_notes[note->note()] = ev;
1671 /* outline all but right edge */
1672 ev->set_outline_what (ArdourCanvas::Rectangle::What (
1673 ArdourCanvas::Rectangle::TOP|
1674 ArdourCanvas::Rectangle::LEFT|
1675 ArdourCanvas::Rectangle::BOTTOM));
1677 /* outline all edges */
1678 ev->set_outline_all ();
1681 if (update_ghost_regions) {
1682 for (std::vector<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
1683 MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*i);
1685 gr->update_note (ev);
1692 MidiRegionView::update_hit (Hit* ev)
1694 boost::shared_ptr<NoteType> note = ev->note();
1696 const framepos_t note_start_frames = source_beats_to_region_frames(note->time());
1697 const double x = trackview.editor().sample_to_pixel(note_start_frames);
1698 const double diamond_size = std::max(1., floor(midi_stream_view()->note_height()) - 2.);
1699 const double y = 1.5 + floor(midi_stream_view()->note_to_y(note->note())) + diamond_size * .5;
1701 ev->set_position (ArdourCanvas::Duple (x, y));
1702 ev->set_height (diamond_size);
1705 /** Add a MIDI note to the view (with length).
1707 * If in sustained mode, notes with length 0 will be considered active
1708 * notes, and resolve_note should be called when the corresponding note off
1709 * event arrives, to properly display the note.
1712 MidiRegionView::add_note(const boost::shared_ptr<NoteType> note, bool visible)
1714 NoteBase* event = 0;
1716 if (midi_view()->note_mode() == Sustained) {
1718 Note* ev_rect = new Note (*this, _note_group, note);
1720 update_note (ev_rect);
1724 MidiGhostRegion* gr;
1726 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
1727 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
1728 gr->add_note(ev_rect);
1732 } else if (midi_view()->note_mode() == Percussive) {
1734 const double diamond_size = std::max(1., floor(midi_stream_view()->note_height()) - 2.);
1736 Hit* ev_diamond = new Hit (*this, _note_group, diamond_size, note);
1738 update_hit (ev_diamond);
1747 if (_marked_for_selection.find(note) != _marked_for_selection.end()) {
1748 note_selected(event, true);
1751 if (_marked_for_velocity.find(note) != _marked_for_velocity.end()) {
1752 event->show_velocity();
1755 event->on_channel_selection_change (get_selected_channels());
1756 _events.push_back(event);
1765 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1766 MidiStreamView* const view = mtv->midi_view();
1768 view->update_note_range (note->note());
1772 MidiRegionView::step_add_note (uint8_t channel, uint8_t number, uint8_t velocity,
1773 Evoral::MusicalTime pos, Evoral::MusicalTime len)
1775 boost::shared_ptr<NoteType> new_note (new NoteType (channel, pos, len, number, velocity));
1777 /* potentially extend region to hold new note */
1779 framepos_t end_frame = source_beats_to_absolute_frames (new_note->end_time());
1780 framepos_t region_end = _region->last_frame();
1782 if (end_frame > region_end) {
1783 _region->set_length (end_frame - _region->position());
1786 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1787 MidiStreamView* const view = mtv->midi_view();
1789 view->update_note_range(new_note->note());
1791 _marked_for_selection.clear ();
1794 start_note_diff_command (_("step add"));
1795 note_diff_add_note (new_note, true, false);
1798 // last_step_edit_note = new_note;
1802 MidiRegionView::step_sustain (Evoral::MusicalTime beats)
1804 change_note_lengths (false, false, beats, false, true);
1807 /** Add a new patch change flag to the canvas.
1808 * @param patch the patch change to add
1809 * @param the text to display in the flag
1810 * @param active_channel true to display the flag as on an active channel, false to grey it out for an inactive channel.
1813 MidiRegionView::add_canvas_patch_change (MidiModel::PatchChangePtr patch, const string& displaytext, bool /*active_channel*/)
1815 framecnt_t region_frames = source_beats_to_region_frames (patch->time());
1816 const double x = trackview.editor().sample_to_pixel (region_frames);
1818 double const height = midi_stream_view()->contents_height();
1820 // CAIROCANVAS: active_channel info removed from PatcChange constructor
1821 // so we need to do something more sophisticated to keep its color
1822 // appearance (MidiPatchChangeFill/MidiPatchChangeInactiveChannelFill)
1825 boost::shared_ptr<PatchChange> patch_change = boost::shared_ptr<PatchChange>(
1826 new PatchChange(*this, group,
1833 if (patch_change->item().width() < _pixel_width) {
1834 // Show unless patch change is beyond the region bounds
1835 if (region_frames < 0 || region_frames >= _region->length()) {
1836 patch_change->hide();
1838 patch_change->show();
1841 patch_change->hide ();
1844 _patch_changes.push_back (patch_change);
1847 MIDI::Name::PatchPrimaryKey
1848 MidiRegionView::patch_change_to_patch_key (MidiModel::PatchChangePtr p)
1850 return MIDI::Name::PatchPrimaryKey (p->program(), p->bank());
1853 /// Return true iff @p pc applies to the given time on the given channel.
1855 patch_applies (const ARDOUR::MidiModel::constPatchChangePtr pc, Evoral::MusicalTime time, uint8_t channel)
1857 return pc->time() <= time && pc->channel() == channel;
1861 MidiRegionView::get_patch_key_at (Evoral::MusicalTime time, uint8_t channel, MIDI::Name::PatchPrimaryKey& key) const
1863 // The earliest event not before time
1864 MidiModel::PatchChanges::iterator i = _model->patch_change_lower_bound (time);
1866 // Go backwards until we find the latest PC for this channel, or the start
1867 while (i != _model->patch_changes().begin() &&
1868 (i == _model->patch_changes().end() ||
1869 !patch_applies(*i, time, channel))) {
1873 if (i != _model->patch_changes().end() && patch_applies(*i, time, channel)) {
1874 key.set_bank((*i)->bank());
1875 key.set_program((*i)->program ());
1883 MidiRegionView::change_patch_change (PatchChange& pc, const MIDI::Name::PatchPrimaryKey& new_patch)
1885 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("alter patch change"));
1887 if (pc.patch()->program() != new_patch.program()) {
1888 c->change_program (pc.patch (), new_patch.program());
1891 int const new_bank = new_patch.bank();
1892 if (pc.patch()->bank() != new_bank) {
1893 c->change_bank (pc.patch (), new_bank);
1896 _model->apply_command (*trackview.session(), c);
1898 _patch_changes.clear ();
1899 display_patch_changes ();
1903 MidiRegionView::change_patch_change (MidiModel::PatchChangePtr old_change, const Evoral::PatchChange<Evoral::MusicalTime> & new_change)
1905 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("alter patch change"));
1907 if (old_change->time() != new_change.time()) {
1908 c->change_time (old_change, new_change.time());
1911 if (old_change->channel() != new_change.channel()) {
1912 c->change_channel (old_change, new_change.channel());
1915 if (old_change->program() != new_change.program()) {
1916 c->change_program (old_change, new_change.program());
1919 if (old_change->bank() != new_change.bank()) {
1920 c->change_bank (old_change, new_change.bank());
1923 _model->apply_command (*trackview.session(), c);
1925 _patch_changes.clear ();
1926 display_patch_changes ();
1929 /** Add a patch change to the region.
1930 * @param t Time in frames relative to region position
1931 * @param patch Patch to add; time and channel are ignored (time is converted from t, and channel comes from
1932 * MidiTimeAxisView::get_channel_for_add())
1935 MidiRegionView::add_patch_change (framecnt_t t, Evoral::PatchChange<Evoral::MusicalTime> const & patch)
1937 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1939 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("add patch change"));
1940 c->add (MidiModel::PatchChangePtr (
1941 new Evoral::PatchChange<Evoral::MusicalTime> (
1942 absolute_frames_to_source_beats (_region->position() + t),
1943 mtv->get_channel_for_add(), patch.program(), patch.bank()
1948 _model->apply_command (*trackview.session(), c);
1950 _patch_changes.clear ();
1951 display_patch_changes ();
1955 MidiRegionView::move_patch_change (PatchChange& pc, Evoral::MusicalTime t)
1957 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("move patch change"));
1958 c->change_time (pc.patch (), t);
1959 _model->apply_command (*trackview.session(), c);
1961 _patch_changes.clear ();
1962 display_patch_changes ();
1966 MidiRegionView::delete_patch_change (PatchChange* pc)
1968 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("delete patch change"));
1969 c->remove (pc->patch ());
1970 _model->apply_command (*trackview.session(), c);
1972 _patch_changes.clear ();
1973 display_patch_changes ();
1977 MidiRegionView::step_patch (PatchChange& patch, bool bank, int delta)
1979 MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key(patch.patch());
1981 key.set_bank(key.bank() + delta);
1983 key.set_program(key.program() + delta);
1985 change_patch_change(patch, key);
1989 MidiRegionView::maybe_remove_deleted_note_from_selection (NoteBase* cne)
1991 if (_selection.empty()) {
1995 _selection.erase (cne);
1999 MidiRegionView::delete_selection()
2001 if (_selection.empty()) {
2005 start_note_diff_command (_("delete selection"));
2007 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2008 if ((*i)->selected()) {
2009 _note_diff_command->remove((*i)->note());
2019 MidiRegionView::delete_note (boost::shared_ptr<NoteType> n)
2021 start_note_diff_command (_("delete note"));
2022 _note_diff_command->remove (n);
2025 trackview.editor().verbose_cursor()->hide ();
2029 MidiRegionView::clear_selection_except (NoteBase* ev, bool signal)
2031 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2033 Selection::iterator tmp = i;
2036 (*i)->set_selected (false);
2037 (*i)->hide_velocity ();
2038 _selection.erase (i);
2046 if (!ev && _entered) {
2047 // Clearing selection entirely, ungrab keyboard
2048 Keyboard::magic_widget_drop_focus();
2049 _grabbed_keyboard = false;
2052 /* this does not change the status of this regionview w.r.t the editor
2057 SelectionCleared (this); /* EMIT SIGNAL */
2062 MidiRegionView::unique_select(NoteBase* ev)
2064 const bool selection_was_empty = _selection.empty();
2066 clear_selection_except (ev);
2068 /* don't bother with checking to see if we should remove this
2069 regionview from the editor selection, since we're about to add
2070 another note, and thus put/keep this regionview in the editor
2074 if (!ev->selected()) {
2075 add_to_selection (ev);
2076 if (selection_was_empty && _entered) {
2077 // Grab keyboard for moving notes with arrow keys
2078 Keyboard::magic_widget_grab_focus();
2079 _grabbed_keyboard = true;
2085 MidiRegionView::select_all_notes ()
2089 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2090 add_to_selection (*i);
2095 MidiRegionView::select_range (framepos_t start, framepos_t end)
2099 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2100 framepos_t t = source_beats_to_absolute_frames((*i)->note()->time());
2101 if (t >= start && t <= end) {
2102 add_to_selection (*i);
2108 MidiRegionView::invert_selection ()
2110 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2111 if ((*i)->selected()) {
2112 remove_from_selection(*i);
2114 add_to_selection (*i);
2120 MidiRegionView::select_matching_notes (uint8_t notenum, uint16_t channel_mask, bool add, bool extend)
2122 bool have_selection = !_selection.empty();
2123 uint8_t low_note = 127;
2124 uint8_t high_note = 0;
2125 MidiModel::Notes& notes (_model->notes());
2126 _optimization_iterator = _events.begin();
2128 if (extend && !have_selection) {
2132 /* scan existing selection to get note range */
2134 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2135 if ((*i)->note()->note() < low_note) {
2136 low_note = (*i)->note()->note();
2138 if ((*i)->note()->note() > high_note) {
2139 high_note = (*i)->note()->note();
2146 if (!extend && (low_note == high_note) && (high_note == notenum)) {
2147 /* only note previously selected is the one we are
2148 * reselecting. treat this as cancelling the selection.
2155 low_note = min (low_note, notenum);
2156 high_note = max (high_note, notenum);
2159 _no_sound_notes = true;
2161 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
2163 boost::shared_ptr<NoteType> note (*n);
2165 bool select = false;
2167 if (((1 << note->channel()) & channel_mask) != 0) {
2169 if ((note->note() >= low_note && note->note() <= high_note)) {
2172 } else if (note->note() == notenum) {
2178 if ((cne = find_canvas_note (note)) != 0) {
2179 // extend is false because we've taken care of it,
2180 // since it extends by time range, not pitch.
2181 note_selected (cne, add, false);
2185 add = true; // we need to add all remaining matching notes, even if the passed in value was false (for "set")
2189 _no_sound_notes = false;
2193 MidiRegionView::toggle_matching_notes (uint8_t notenum, uint16_t channel_mask)
2195 MidiModel::Notes& notes (_model->notes());
2196 _optimization_iterator = _events.begin();
2198 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
2200 boost::shared_ptr<NoteType> note (*n);
2203 if (note->note() == notenum && (((0x0001 << note->channel()) & channel_mask) != 0)) {
2204 if ((cne = find_canvas_note (note)) != 0) {
2205 if (cne->selected()) {
2206 note_deselected (cne);
2208 note_selected (cne, true, false);
2216 MidiRegionView::note_selected (NoteBase* ev, bool add, bool extend)
2219 clear_selection_except (ev);
2220 if (!_selection.empty()) {
2221 PublicEditor& editor (trackview.editor());
2222 editor.get_selection().add (this);
2228 if (!ev->selected()) {
2229 add_to_selection (ev);
2233 /* find end of latest note selected, select all between that and the start of "ev" */
2235 Evoral::MusicalTime earliest = Evoral::MaxMusicalTime;
2236 Evoral::MusicalTime latest = Evoral::MusicalTime();
2238 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2239 if ((*i)->note()->end_time() > latest) {
2240 latest = (*i)->note()->end_time();
2242 if ((*i)->note()->time() < earliest) {
2243 earliest = (*i)->note()->time();
2247 if (ev->note()->end_time() > latest) {
2248 latest = ev->note()->end_time();
2251 if (ev->note()->time() < earliest) {
2252 earliest = ev->note()->time();
2255 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2257 /* find notes entirely within OR spanning the earliest..latest range */
2259 if (((*i)->note()->time() >= earliest && (*i)->note()->end_time() <= latest) ||
2260 ((*i)->note()->time() <= earliest && (*i)->note()->end_time() >= latest)) {
2261 add_to_selection (*i);
2269 MidiRegionView::note_deselected(NoteBase* ev)
2271 remove_from_selection (ev);
2275 MidiRegionView::update_drag_selection(framepos_t start, framepos_t end, double gy0, double gy1, bool extend)
2277 PublicEditor& editor = trackview.editor();
2279 // Convert to local coordinates
2280 const framepos_t p = _region->position();
2281 const double y = midi_view()->y_position();
2282 const double x0 = editor.sample_to_pixel(max((framepos_t)0, start - p));
2283 const double x1 = editor.sample_to_pixel(max((framepos_t)0, end - p));
2284 const double y0 = max(0.0, gy0 - y);
2285 const double y1 = max(0.0, gy1 - y);
2287 // TODO: Make this faster by storing the last updated selection rect, and only
2288 // adjusting things that are in the area that appears/disappeared.
2289 // We probably need a tree to be able to find events in O(log(n)) time.
2291 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2292 if ((*i)->x0() < x1 && (*i)->x1() > x0 && (*i)->y0() < y1 && (*i)->y1() > y0) {
2293 // Rectangles intersect
2294 if (!(*i)->selected()) {
2295 add_to_selection (*i);
2297 } else if ((*i)->selected() && !extend) {
2298 // Rectangles do not intersect
2299 remove_from_selection (*i);
2303 typedef RouteTimeAxisView::AutomationTracks ATracks;
2304 typedef std::list<Selectable*> Selectables;
2306 /* Add control points to selection. */
2307 const ATracks& atracks = midi_view()->automation_tracks();
2308 Selectables selectables;
2309 editor.get_selection().clear_points();
2310 for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) {
2311 a->second->get_selectables(start, end, gy0, gy1, selectables);
2312 for (Selectables::const_iterator s = selectables.begin(); s != selectables.end(); ++s) {
2313 ControlPoint* cp = dynamic_cast<ControlPoint*>(*s);
2315 editor.get_selection().add(cp);
2318 a->second->set_selected_points(editor.get_selection().points);
2323 MidiRegionView::update_vertical_drag_selection (double y1, double y2, bool extend)
2329 // TODO: Make this faster by storing the last updated selection rect, and only
2330 // adjusting things that are in the area that appears/disappeared.
2331 // We probably need a tree to be able to find events in O(log(n)) time.
2333 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2334 if (((*i)->y1() >= y1 && (*i)->y1() <= y2)) {
2335 // within y- (note-) range
2336 if (!(*i)->selected()) {
2337 add_to_selection (*i);
2339 } else if ((*i)->selected() && !extend) {
2340 remove_from_selection (*i);
2346 MidiRegionView::remove_from_selection (NoteBase* ev)
2348 Selection::iterator i = _selection.find (ev);
2350 if (i != _selection.end()) {
2351 _selection.erase (i);
2352 if (_selection.empty() && _grabbed_keyboard) {
2354 Keyboard::magic_widget_drop_focus();
2355 _grabbed_keyboard = false;
2359 ev->set_selected (false);
2360 ev->hide_velocity ();
2362 if (_selection.empty()) {
2363 PublicEditor& editor (trackview.editor());
2364 editor.get_selection().remove (this);
2369 MidiRegionView::add_to_selection (NoteBase* ev)
2371 const bool selection_was_empty = _selection.empty();
2373 if (_selection.insert (ev).second) {
2374 ev->set_selected (true);
2375 start_playing_midi_note ((ev)->note());
2376 if (selection_was_empty && _entered) {
2377 // Grab keyboard for moving notes with arrow keys
2378 Keyboard::magic_widget_grab_focus();
2379 _grabbed_keyboard = true;
2383 if (selection_was_empty) {
2384 PublicEditor& editor (trackview.editor());
2385 editor.get_selection().add (this);
2390 MidiRegionView::move_selection(double dx, double dy, double cumulative_dy)
2392 typedef vector<boost::shared_ptr<NoteType> > PossibleChord;
2393 PossibleChord to_play;
2394 Evoral::MusicalTime earliest = Evoral::MaxMusicalTime;
2396 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2397 if ((*i)->note()->time() < earliest) {
2398 earliest = (*i)->note()->time();
2402 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2403 if ((*i)->note()->time() == earliest) {
2404 to_play.push_back ((*i)->note());
2406 (*i)->move_event(dx, dy);
2409 if (dy && !_selection.empty() && !_no_sound_notes && Config->get_sound_midi_notes()) {
2411 if (to_play.size() > 1) {
2413 PossibleChord shifted;
2415 for (PossibleChord::iterator n = to_play.begin(); n != to_play.end(); ++n) {
2416 boost::shared_ptr<NoteType> moved_note (new NoteType (**n));
2417 moved_note->set_note (moved_note->note() + cumulative_dy);
2418 shifted.push_back (moved_note);
2421 start_playing_midi_chord (shifted);
2423 } else if (!to_play.empty()) {
2425 boost::shared_ptr<NoteType> moved_note (new NoteType (*to_play.front()));
2426 moved_note->set_note (moved_note->note() + cumulative_dy);
2427 start_playing_midi_note (moved_note);
2433 MidiRegionView::note_dropped(NoteBase *, frameoffset_t dt, int8_t dnote)
2435 uint8_t lowest_note_in_selection = 127;
2436 uint8_t highest_note_in_selection = 0;
2437 uint8_t highest_note_difference = 0;
2439 // find highest and lowest notes first
2441 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2442 uint8_t pitch = (*i)->note()->note();
2443 lowest_note_in_selection = std::min(lowest_note_in_selection, pitch);
2444 highest_note_in_selection = std::max(highest_note_in_selection, pitch);
2448 cerr << "dnote: " << (int) dnote << endl;
2449 cerr << "lowest note (streamview): " << int(midi_stream_view()->lowest_note())
2450 << " highest note (streamview): " << int(midi_stream_view()->highest_note()) << endl;
2451 cerr << "lowest note (selection): " << int(lowest_note_in_selection) << " highest note(selection): "
2452 << int(highest_note_in_selection) << endl;
2453 cerr << "selection size: " << _selection.size() << endl;
2454 cerr << "Highest note in selection: " << (int) highest_note_in_selection << endl;
2457 // Make sure the note pitch does not exceed the MIDI standard range
2458 if (highest_note_in_selection + dnote > 127) {
2459 highest_note_difference = highest_note_in_selection - 127;
2462 start_note_diff_command (_("move notes"));
2464 for (Selection::iterator i = _selection.begin(); i != _selection.end() ; ++i) {
2466 framepos_t new_frames = source_beats_to_absolute_frames ((*i)->note()->time()) + dt;
2467 Evoral::MusicalTime new_time = absolute_frames_to_source_beats (new_frames);
2473 note_diff_add_change (*i, MidiModel::NoteDiffCommand::StartTime, new_time);
2475 uint8_t original_pitch = (*i)->note()->note();
2476 uint8_t new_pitch = original_pitch + dnote - highest_note_difference;
2478 // keep notes in standard midi range
2479 clamp_to_0_127(new_pitch);
2481 lowest_note_in_selection = std::min(lowest_note_in_selection, new_pitch);
2482 highest_note_in_selection = std::max(highest_note_in_selection, new_pitch);
2484 note_diff_add_change (*i, MidiModel::NoteDiffCommand::NoteNumber, new_pitch);
2489 // care about notes being moved beyond the upper/lower bounds on the canvas
2490 if (lowest_note_in_selection < midi_stream_view()->lowest_note() ||
2491 highest_note_in_selection > midi_stream_view()->highest_note()) {
2492 midi_stream_view()->set_note_range(MidiStreamView::ContentsRange);
2496 /** @param x Pixel relative to the region position.
2497 * @return Snapped frame relative to the region position.
2500 MidiRegionView::snap_pixel_to_sample(double x)
2502 PublicEditor& editor (trackview.editor());
2503 return snap_frame_to_frame (editor.pixel_to_sample (x));
2506 /** @param x Pixel relative to the region position.
2507 * @return Snapped pixel relative to the region position.
2510 MidiRegionView::snap_to_pixel(double x)
2512 return (double) trackview.editor().sample_to_pixel(snap_pixel_to_sample(x));
2516 MidiRegionView::get_position_pixels()
2518 framepos_t region_frame = get_position();
2519 return trackview.editor().sample_to_pixel(region_frame);
2523 MidiRegionView::get_end_position_pixels()
2525 framepos_t frame = get_position() + get_duration ();
2526 return trackview.editor().sample_to_pixel(frame);
2530 MidiRegionView::source_beats_to_absolute_frames(Evoral::MusicalTime beats) const
2532 /* the time converter will return the frame corresponding to `beats'
2533 relative to the start of the source. The start of the source
2534 is an implied position given by region->position - region->start
2536 const framepos_t source_start = _region->position() - _region->start();
2537 return source_start + _source_relative_time_converter.to (beats);
2541 MidiRegionView::absolute_frames_to_source_beats(framepos_t frames) const
2543 /* the `frames' argument needs to be converted into a frame count
2544 relative to the start of the source before being passed in to the
2547 const framepos_t source_start = _region->position() - _region->start();
2548 return _source_relative_time_converter.from (frames - source_start);
2552 MidiRegionView::region_beats_to_region_frames(Evoral::MusicalTime beats) const
2554 return _region_relative_time_converter.to(beats);
2558 MidiRegionView::region_frames_to_region_beats(framepos_t frames) const
2560 return _region_relative_time_converter.from(frames);
2564 MidiRegionView::begin_resizing (bool /*at_front*/)
2566 _resize_data.clear();
2568 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2569 Note *note = dynamic_cast<Note*> (*i);
2571 // only insert CanvasNotes into the map
2573 NoteResizeData *resize_data = new NoteResizeData();
2574 resize_data->note = note;
2576 // create a new SimpleRect from the note which will be the resize preview
2577 ArdourCanvas::Rectangle *resize_rect = new ArdourCanvas::Rectangle (_note_group,
2578 ArdourCanvas::Rect (note->x0(), note->y0(), note->x0(), note->y1()));
2580 // calculate the colors: get the color settings
2581 uint32_t fill_color = UINT_RGBA_CHANGE_A(
2582 ARDOUR_UI::config()->color ("midi note selected"),
2585 // make the resize preview notes more transparent and bright
2586 fill_color = UINT_INTERPOLATE(fill_color, 0xFFFFFF40, 0.5);
2588 // calculate color based on note velocity
2589 resize_rect->set_fill_color (UINT_INTERPOLATE(
2590 NoteBase::meter_style_fill_color(note->note()->velocity(), note->selected()),
2594 resize_rect->set_outline_color (NoteBase::calculate_outline (
2595 ARDOUR_UI::config()->color ("midi note selected")));
2597 resize_data->resize_rect = resize_rect;
2598 _resize_data.push_back(resize_data);
2603 /** Update resizing notes while user drags.
2604 * @param primary `primary' note for the drag; ie the one that is used as the reference in non-relative mode.
2605 * @param at_front which end of the note (true == note on, false == note off)
2606 * @param delta_x change in mouse position since the start of the drag
2607 * @param relative true if relative resizing is taking place, false if absolute resizing. This only makes
2608 * a difference when multiple notes are being resized; in relative mode, each note's length is changed by the
2609 * amount of the drag. In non-relative mode, all selected notes are set to have the same start or end point
2610 * as the \a primary note.
2613 MidiRegionView::update_resizing (NoteBase* primary, bool at_front, double delta_x, bool relative)
2615 bool cursor_set = false;
2617 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2618 ArdourCanvas::Rectangle* resize_rect = (*i)->resize_rect;
2619 Note* canvas_note = (*i)->note;
2624 current_x = canvas_note->x0() + delta_x;
2626 current_x = primary->x0() + delta_x;
2630 current_x = canvas_note->x1() + delta_x;
2632 current_x = primary->x1() + delta_x;
2636 if (current_x < 0) {
2637 // This works even with snapping because RegionView::snap_frame_to_frame()
2638 // snaps forward if the snapped sample is before the beginning of the region
2641 if (current_x > trackview.editor().sample_to_pixel(_region->length())) {
2642 current_x = trackview.editor().sample_to_pixel(_region->length());
2646 resize_rect->set_x0 (snap_to_pixel(current_x));
2647 resize_rect->set_x1 (canvas_note->x1());
2649 resize_rect->set_x1 (snap_to_pixel(current_x));
2650 resize_rect->set_x0 (canvas_note->x0());
2654 const double snapped_x = snap_pixel_to_sample (current_x);
2655 Evoral::MusicalTime beats = region_frames_to_region_beats (snapped_x);
2656 Evoral::MusicalTime len = Evoral::MusicalTime();
2659 if (beats < canvas_note->note()->end_time()) {
2660 len = canvas_note->note()->time() - beats;
2661 len += canvas_note->note()->length();
2664 if (beats >= canvas_note->note()->time()) {
2665 len = beats - canvas_note->note()->time();
2670 snprintf (buf, sizeof (buf), "%.3g beats", len.to_double());
2671 show_verbose_cursor (buf, 0, 0);
2680 /** Finish resizing notes when the user releases the mouse button.
2681 * Parameters the same as for \a update_resizing().
2684 MidiRegionView::commit_resizing (NoteBase* primary, bool at_front, double delta_x, bool relative)
2686 start_note_diff_command (_("resize notes"));
2688 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2689 Note* canvas_note = (*i)->note;
2690 ArdourCanvas::Rectangle* resize_rect = (*i)->resize_rect;
2692 /* Get the new x position for this resize, which is in pixels relative
2693 * to the region position.
2700 current_x = canvas_note->x0() + delta_x;
2702 current_x = primary->x0() + delta_x;
2706 current_x = canvas_note->x1() + delta_x;
2708 current_x = primary->x1() + delta_x;
2712 if (current_x < 0) {
2715 if (current_x > trackview.editor().sample_to_pixel(_region->length())) {
2716 current_x = trackview.editor().sample_to_pixel(_region->length());
2719 /* Convert that to a frame within the source */
2720 current_x = snap_pixel_to_sample (current_x) + _region->start ();
2722 /* and then to beats */
2723 const Evoral::MusicalTime x_beats = region_frames_to_region_beats (current_x);
2725 if (at_front && x_beats < canvas_note->note()->end_time()) {
2726 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::StartTime, x_beats);
2728 Evoral::MusicalTime len = canvas_note->note()->time() - x_beats;
2729 len += canvas_note->note()->length();
2732 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
2737 const Evoral::MusicalTime len = x_beats - canvas_note->note()->time();
2740 /* XXX convert to beats */
2741 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
2749 _resize_data.clear();
2754 MidiRegionView::abort_resizing ()
2756 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2757 delete (*i)->resize_rect;
2761 _resize_data.clear ();
2765 MidiRegionView::change_note_velocity(NoteBase* event, int8_t velocity, bool relative)
2767 uint8_t new_velocity;
2770 new_velocity = event->note()->velocity() + velocity;
2771 clamp_to_0_127(new_velocity);
2773 new_velocity = velocity;
2776 event->set_selected (event->selected()); // change color
2778 note_diff_add_change (event, MidiModel::NoteDiffCommand::Velocity, new_velocity);
2782 MidiRegionView::change_note_note (NoteBase* event, int8_t note, bool relative)
2787 new_note = event->note()->note() + note;
2792 clamp_to_0_127 (new_note);
2793 note_diff_add_change (event, MidiModel::NoteDiffCommand::NoteNumber, new_note);
2797 MidiRegionView::trim_note (NoteBase* event, Evoral::MusicalTime front_delta, Evoral::MusicalTime end_delta)
2799 bool change_start = false;
2800 bool change_length = false;
2801 Evoral::MusicalTime new_start;
2802 Evoral::MusicalTime new_length;
2804 /* NOTE: the semantics of the two delta arguments are slightly subtle:
2806 front_delta: if positive - move the start of the note later in time (shortening it)
2807 if negative - move the start of the note earlier in time (lengthening it)
2809 end_delta: if positive - move the end of the note later in time (lengthening it)
2810 if negative - move the end of the note earlier in time (shortening it)
2813 if (!!front_delta) {
2814 if (front_delta < 0) {
2816 if (event->note()->time() < -front_delta) {
2817 new_start = Evoral::MusicalTime();
2819 new_start = event->note()->time() + front_delta; // moves earlier
2822 /* start moved toward zero, so move the end point out to where it used to be.
2823 Note that front_delta is negative, so this increases the length.
2826 new_length = event->note()->length() - front_delta;
2827 change_start = true;
2828 change_length = true;
2832 Evoral::MusicalTime new_pos = event->note()->time() + front_delta;
2834 if (new_pos < event->note()->end_time()) {
2835 new_start = event->note()->time() + front_delta;
2836 /* start moved toward the end, so move the end point back to where it used to be */
2837 new_length = event->note()->length() - front_delta;
2838 change_start = true;
2839 change_length = true;
2846 bool can_change = true;
2847 if (end_delta < 0) {
2848 if (event->note()->length() < -end_delta) {
2854 new_length = event->note()->length() + end_delta;
2855 change_length = true;
2860 note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_start);
2863 if (change_length) {
2864 note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, new_length);
2869 MidiRegionView::change_note_channel (NoteBase* event, int8_t chn, bool relative)
2871 uint8_t new_channel;
2875 if (event->note()->channel() < -chn) {
2878 new_channel = event->note()->channel() + chn;
2881 new_channel = event->note()->channel() + chn;
2884 new_channel = (uint8_t) chn;
2887 note_diff_add_change (event, MidiModel::NoteDiffCommand::Channel, new_channel);
2891 MidiRegionView::change_note_time (NoteBase* event, Evoral::MusicalTime delta, bool relative)
2893 Evoral::MusicalTime new_time;
2897 if (event->note()->time() < -delta) {
2898 new_time = Evoral::MusicalTime();
2900 new_time = event->note()->time() + delta;
2903 new_time = event->note()->time() + delta;
2909 note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_time);
2913 MidiRegionView::change_note_length (NoteBase* event, Evoral::MusicalTime t)
2915 note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, t);
2919 MidiRegionView::change_velocities (bool up, bool fine, bool allow_smush, bool all_together)
2924 if (_selection.empty()) {
2939 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2940 if ((*i)->note()->velocity() < -delta || (*i)->note()->velocity() + delta > 127) {
2946 start_note_diff_command (_("change velocities"));
2948 for (Selection::iterator i = _selection.begin(); i != _selection.end();) {
2949 Selection::iterator next = i;
2953 if (i == _selection.begin()) {
2954 change_note_velocity (*i, delta, true);
2955 value = (*i)->note()->velocity() + delta;
2957 change_note_velocity (*i, value, false);
2961 change_note_velocity (*i, delta, true);
2970 if (!_selection.empty()) {
2972 snprintf (buf, sizeof (buf), "Vel %d",
2973 (int) (*_selection.begin())->note()->velocity());
2974 show_verbose_cursor (buf, 10, 10);
2980 MidiRegionView::transpose (bool up, bool fine, bool allow_smush)
2982 if (_selection.empty()) {
2999 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3001 if ((int8_t) (*i)->note()->note() + delta <= 0) {
3005 if ((int8_t) (*i)->note()->note() + delta > 127) {
3012 start_note_diff_command (_("transpose"));
3014 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3015 Selection::iterator next = i;
3017 change_note_note (*i, delta, true);
3025 MidiRegionView::change_note_lengths (bool fine, bool shorter, Evoral::MusicalTime delta, bool start, bool end)
3029 delta = Evoral::MusicalTime(1.0/128.0);
3031 /* grab the current grid distance */
3032 delta = get_grid_beats(_region->position());
3040 start_note_diff_command (_("change note lengths"));
3042 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3043 Selection::iterator next = i;
3046 /* note the negation of the delta for start */
3049 (start ? -delta : Evoral::MusicalTime()),
3050 (end ? delta : Evoral::MusicalTime()));
3059 MidiRegionView::nudge_notes (bool forward, bool fine)
3061 if (_selection.empty()) {
3065 /* pick a note as the point along the timeline to get the nudge distance.
3066 its not necessarily the earliest note, so we may want to pull the notes out
3067 into a vector and sort before using the first one.
3070 const framepos_t ref_point = source_beats_to_absolute_frames ((*(_selection.begin()))->note()->time());
3071 Evoral::MusicalTime delta;
3075 /* non-fine, move by 1 bar regardless of snap */
3076 delta = Evoral::MusicalTime(trackview.session()->tempo_map().meter_at(ref_point).divisions_per_bar());
3078 } else if (trackview.editor().snap_mode() == Editing::SnapOff) {
3080 /* grid is off - use nudge distance */
3083 const framecnt_t distance = trackview.editor().get_nudge_distance (ref_point, unused);
3084 delta = region_frames_to_region_beats (fabs ((double)distance));
3090 framepos_t next_pos = ref_point;
3093 if (max_framepos - 1 < next_pos) {
3097 if (next_pos == 0) {
3103 trackview.editor().snap_to (next_pos, (forward ? RoundUpAlways : RoundDownAlways), false);
3104 const framecnt_t distance = ref_point - next_pos;
3105 delta = region_frames_to_region_beats (fabs ((double)distance));
3116 start_note_diff_command (_("nudge"));
3118 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3119 Selection::iterator next = i;
3121 change_note_time (*i, delta, true);
3129 MidiRegionView::change_channel(uint8_t channel)
3131 start_note_diff_command(_("change channel"));
3132 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3133 note_diff_add_change (*i, MidiModel::NoteDiffCommand::Channel, channel);
3141 MidiRegionView::note_entered(NoteBase* ev)
3143 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3145 pre_note_enter_cursor = editor->get_canvas_cursor ();
3147 if (_mouse_state == SelectTouchDragging) {
3148 note_selected (ev, true);
3151 show_verbose_cursor (ev->note ());
3155 MidiRegionView::note_left (NoteBase*)
3157 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3159 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3160 (*i)->hide_velocity ();
3163 editor->verbose_cursor()->hide ();
3165 if (pre_note_enter_cursor) {
3166 editor->set_canvas_cursor (pre_note_enter_cursor);
3167 pre_note_enter_cursor = 0;
3172 MidiRegionView::patch_entered (PatchChange* p)
3175 /* XXX should get patch name if we can */
3176 s << _("Bank ") << (p->patch()->bank() + MIDI_BP_ZERO) << '\n'
3177 << _("Program ") << ((int) p->patch()->program()) + MIDI_BP_ZERO << '\n'
3178 << _("Channel ") << ((int) p->patch()->channel() + 1);
3179 show_verbose_cursor (s.str(), 10, 20);
3180 p->item().grab_focus();
3184 MidiRegionView::patch_left (PatchChange *)
3186 trackview.editor().verbose_cursor()->hide ();
3187 /* focus will transfer back via the enter-notify event sent to this
3193 MidiRegionView::sysex_entered (SysEx* p)
3197 // need a way to extract text from p->_flag->_text
3199 // show_verbose_cursor (s.str(), 10, 20);
3200 p->item().grab_focus();
3204 MidiRegionView::sysex_left (SysEx *)
3206 trackview.editor().verbose_cursor()->hide ();
3207 /* focus will transfer back via the enter-notify event sent to this
3213 MidiRegionView::note_mouse_position (float x_fraction, float /*y_fraction*/, bool can_set_cursor)
3215 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3216 Editing::MouseMode mm = editor->current_mouse_mode();
3217 bool trimmable = (mm == MouseContent || mm == MouseTimeFX || mm == MouseDraw);
3219 if (can_set_cursor) {
3220 if (trimmable && x_fraction > 0.0 && x_fraction < 0.2) {
3221 editor->set_canvas_cursor (editor->cursors()->left_side_trim);
3222 } else if (trimmable && x_fraction >= 0.8 && x_fraction < 1.0) {
3223 editor->set_canvas_cursor (editor->cursors()->right_side_trim);
3224 } else if (pre_note_enter_cursor) {
3225 editor->set_canvas_cursor (pre_note_enter_cursor);
3231 MidiRegionView::set_frame_color()
3235 TimeAxisViewItem::set_frame_color ();
3242 f = ARDOUR_UI::config()->color ("selected region base");
3243 } else if (high_enough_for_name || !ARDOUR_UI::config()->get_color_regions_using_track_color()) {
3244 f = ARDOUR_UI::config()->color_mod ("midi frame base", "midi frame base");
3249 if (!rect_visible) {
3250 f = UINT_RGBA_CHANGE_A (f, 80);
3253 frame->set_fill_color (f);
3257 MidiRegionView::midi_channel_mode_changed ()
3259 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3260 uint16_t mask = mtv->midi_track()->get_playback_channel_mask();
3261 ChannelMode mode = mtv->midi_track()->get_playback_channel_mode ();
3263 if (mode == ForceChannel) {
3264 mask = 0xFFFF; // Show all notes as active (below)
3267 // Update notes for selection
3268 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3269 (*i)->on_channel_selection_change (mask);
3272 _patch_changes.clear ();
3273 display_patch_changes ();
3277 MidiRegionView::instrument_settings_changed ()
3283 MidiRegionView::cut_copy_clear (Editing::CutCopyOp op)
3285 if (_selection.empty()) {
3289 PublicEditor& editor (trackview.editor());
3293 /* XXX what to do ? */
3297 editor.get_cut_buffer().add (selection_as_cut_buffer());
3305 start_note_diff_command();
3307 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3314 note_diff_remove_note (*i);
3324 MidiRegionView::selection_as_cut_buffer () const
3328 for (Selection::const_iterator i = _selection.begin(); i != _selection.end(); ++i) {
3329 NoteType* n = (*i)->note().get();
3330 notes.insert (boost::shared_ptr<NoteType> (new NoteType (*n)));
3333 MidiCutBuffer* cb = new MidiCutBuffer (trackview.session());
3339 /** This method handles undo */
3341 MidiRegionView::paste (framepos_t pos, const ::Selection& selection, PasteContext& ctx)
3343 trackview.editor().begin_reversible_command (Operations::paste);
3345 // Paste notes, if available
3346 MidiNoteSelection::const_iterator m = selection.midi_notes.get_nth(ctx.counts.n_notes());
3347 if (m != selection.midi_notes.end()) {
3348 ctx.counts.increase_n_notes();
3349 paste_internal(pos, ctx.count, ctx.times, **m);
3352 // Paste control points to automation children, if available
3353 typedef RouteTimeAxisView::AutomationTracks ATracks;
3354 const ATracks& atracks = midi_view()->automation_tracks();
3355 for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) {
3356 a->second->paste(pos, selection, ctx);
3359 trackview.editor().commit_reversible_command ();
3364 /** This method handles undo */
3366 MidiRegionView::paste_internal (framepos_t pos, unsigned paste_count, float times, const MidiCutBuffer& mcb)
3372 start_note_diff_command (_("paste"));
3374 const Evoral::MusicalTime snap_beats = get_grid_beats(pos);
3375 const Evoral::MusicalTime first_time = (*mcb.notes().begin())->time();
3376 const Evoral::MusicalTime last_time = (*mcb.notes().rbegin())->end_time();
3377 const Evoral::MusicalTime duration = last_time - first_time;
3378 const Evoral::MusicalTime snap_duration = duration.snap_to(snap_beats);
3379 const Evoral::MusicalTime paste_offset = snap_duration * paste_count;
3380 const Evoral::MusicalTime pos_beats = absolute_frames_to_source_beats(pos) + paste_offset;
3381 Evoral::MusicalTime end_point = Evoral::MusicalTime();
3383 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste data spans from %1 to %2 (%3) ; paste pos beats = %4 (based on %5 - %6)\n",
3386 duration, pos, _region->position(),
3391 for (int n = 0; n < (int) times; ++n) {
3393 for (Notes::const_iterator i = mcb.notes().begin(); i != mcb.notes().end(); ++i) {
3395 boost::shared_ptr<NoteType> copied_note (new NoteType (*((*i).get())));
3396 copied_note->set_time (pos_beats + copied_note->time() - first_time);
3398 /* make all newly added notes selected */
3400 note_diff_add_note (copied_note, true);
3401 end_point = copied_note->end_time();
3405 /* if we pasted past the current end of the region, extend the region */
3407 framepos_t end_frame = source_beats_to_absolute_frames (end_point);
3408 framepos_t region_end = _region->position() + _region->length() - 1;
3410 if (end_frame > region_end) {
3412 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste extended region from %1 to %2\n", region_end, end_frame));
3414 _region->clear_changes ();
3415 _region->set_length (end_frame - _region->position());
3416 trackview.session()->add_command (new StatefulDiffCommand (_region));
3422 struct EventNoteTimeEarlyFirstComparator {
3423 bool operator() (NoteBase* a, NoteBase* b) {
3424 return a->note()->time() < b->note()->time();
3429 MidiRegionView::time_sort_events ()
3431 if (!_sort_needed) {
3435 EventNoteTimeEarlyFirstComparator cmp;
3438 _sort_needed = false;
3442 MidiRegionView::goto_next_note (bool add_to_selection)
3444 bool use_next = false;
3446 if (_events.back()->selected()) {
3450 time_sort_events ();
3452 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3453 uint16_t const channel_mask = mtv->midi_track()->get_playback_channel_mask();
3455 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3456 if ((*i)->selected()) {
3459 } else if (use_next) {
3460 if (channel_mask & (1 << (*i)->note()->channel())) {
3461 if (!add_to_selection) {
3464 note_selected (*i, true, false);
3471 /* use the first one */
3473 if (!_events.empty() && (channel_mask & (1 << _events.front()->note()->channel ()))) {
3474 unique_select (_events.front());
3479 MidiRegionView::goto_previous_note (bool add_to_selection)
3481 bool use_next = false;
3483 if (_events.front()->selected()) {
3487 time_sort_events ();
3489 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3490 uint16_t const channel_mask = mtv->midi_track()->get_playback_channel_mask ();
3492 for (Events::reverse_iterator i = _events.rbegin(); i != _events.rend(); ++i) {
3493 if ((*i)->selected()) {
3496 } else if (use_next) {
3497 if (channel_mask & (1 << (*i)->note()->channel())) {
3498 if (!add_to_selection) {
3501 note_selected (*i, true, false);
3508 /* use the last one */
3510 if (!_events.empty() && (channel_mask & (1 << (*_events.rbegin())->note()->channel ()))) {
3511 unique_select (*(_events.rbegin()));
3516 MidiRegionView::selection_as_notelist (Notes& selected, bool allow_all_if_none_selected)
3518 bool had_selected = false;
3520 time_sort_events ();
3522 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3523 if ((*i)->selected()) {
3524 selected.insert ((*i)->note());
3525 had_selected = true;
3529 if (allow_all_if_none_selected && !had_selected) {
3530 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3531 selected.insert ((*i)->note());
3537 MidiRegionView::update_ghost_note (double x, double y)
3539 x = std::max(0.0, x);
3541 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3546 _note_group->canvas_to_item (x, y);
3548 PublicEditor& editor = trackview.editor ();
3550 framepos_t const unsnapped_frame = editor.pixel_to_sample (x);
3551 framecnt_t grid_frames;
3552 framepos_t const f = snap_frame_to_grid_underneath (unsnapped_frame, grid_frames);
3554 /* use region_frames... because we are converting a delta within the region
3557 const Evoral::MusicalTime length = get_grid_beats(unsnapped_frame);
3559 /* note that this sets the time of the ghost note in beats relative to
3560 the start of the source; that is how all note times are stored.
3562 _ghost_note->note()->set_time (
3563 std::max(Evoral::MusicalTime(),
3564 absolute_frames_to_source_beats (f + _region->position ())));
3565 _ghost_note->note()->set_length (length);
3566 _ghost_note->note()->set_note (midi_stream_view()->y_to_note (y));
3567 _ghost_note->note()->set_channel (mtv->get_channel_for_add ());
3569 /* the ghost note does not appear in ghost regions, so pass false in here */
3570 update_note (_ghost_note, false);
3572 show_verbose_cursor (_ghost_note->note ());
3576 MidiRegionView::create_ghost_note (double x, double y)
3578 remove_ghost_note ();
3580 boost::shared_ptr<NoteType> g (new NoteType);
3581 _ghost_note = new Note (*this, _note_group, g);
3582 _ghost_note->set_ignore_events (true);
3583 _ghost_note->set_outline_color (0x000000aa);
3584 update_ghost_note (x, y);
3585 _ghost_note->show ();
3587 show_verbose_cursor (_ghost_note->note ());
3591 MidiRegionView::remove_ghost_note ()
3598 MidiRegionView::snap_changed ()
3604 create_ghost_note (_last_ghost_x, _last_ghost_y);
3608 MidiRegionView::drop_down_keys ()
3610 _mouse_state = None;
3614 MidiRegionView::maybe_select_by_position (GdkEventButton* ev, double /*x*/, double y)
3616 /* XXX: This is dead code. What was it for? */
3618 double note = midi_stream_view()->y_to_note(y);
3620 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3622 uint16_t chn_mask = mtv->midi_track()->get_playback_channel_mask();
3624 if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
3625 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchGreaterThanOrEqual, (uint8_t) floor (note), chn_mask);
3626 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
3627 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchLessThanOrEqual, (uint8_t) floor (note), chn_mask);
3632 bool add_mrv_selection = false;
3634 if (_selection.empty()) {
3635 add_mrv_selection = true;
3638 for (Events::iterator i = e.begin(); i != e.end(); ++i) {
3639 if (_selection.insert (*i).second) {
3640 (*i)->set_selected (true);
3644 if (add_mrv_selection) {
3645 PublicEditor& editor (trackview.editor());
3646 editor.get_selection().add (this);
3651 MidiRegionView::color_handler ()
3653 RegionView::color_handler ();
3655 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3656 (*i)->set_selected ((*i)->selected()); // will change color
3659 /* XXX probably more to do here */
3663 MidiRegionView::enable_display (bool yn)
3665 RegionView::enable_display (yn);
3672 MidiRegionView::show_step_edit_cursor (Evoral::MusicalTime pos)
3674 if (_step_edit_cursor == 0) {
3675 ArdourCanvas::Item* const group = get_canvas_group();
3677 _step_edit_cursor = new ArdourCanvas::Rectangle (group);
3678 _step_edit_cursor->set_y0 (0);
3679 _step_edit_cursor->set_y1 (midi_stream_view()->contents_height());
3680 _step_edit_cursor->set_fill_color (RGBA_TO_UINT (45,0,0,90));
3681 _step_edit_cursor->set_outline_color (RGBA_TO_UINT (85,0,0,90));
3684 move_step_edit_cursor (pos);
3685 _step_edit_cursor->show ();
3689 MidiRegionView::move_step_edit_cursor (Evoral::MusicalTime pos)
3691 _step_edit_cursor_position = pos;
3693 if (_step_edit_cursor) {
3694 double pixel = trackview.editor().sample_to_pixel (region_beats_to_region_frames (pos));
3695 _step_edit_cursor->set_x0 (pixel);
3696 set_step_edit_cursor_width (_step_edit_cursor_width);
3701 MidiRegionView::hide_step_edit_cursor ()
3703 if (_step_edit_cursor) {
3704 _step_edit_cursor->hide ();
3709 MidiRegionView::set_step_edit_cursor_width (Evoral::MusicalTime beats)
3711 _step_edit_cursor_width = beats;
3713 if (_step_edit_cursor) {
3714 _step_edit_cursor->set_x1 (_step_edit_cursor->x0() + trackview.editor().sample_to_pixel (region_beats_to_region_frames (beats)));
3718 /** Called when a diskstream on our track has received some data. Update the view, if applicable.
3719 * @param w Source that the data will end up in.
3722 MidiRegionView::data_recorded (boost::weak_ptr<MidiSource> w)
3724 if (!_active_notes) {
3725 /* we aren't actively being recorded to */
3729 boost::shared_ptr<MidiSource> src = w.lock ();
3730 if (!src || src != midi_region()->midi_source()) {
3731 /* recorded data was not destined for our source */
3735 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (&trackview);
3737 boost::shared_ptr<MidiBuffer> buf = mtv->midi_track()->get_gui_feed_buffer ();
3739 framepos_t back = max_framepos;
3741 for (MidiBuffer::iterator i = buf->begin(); i != buf->end(); ++i) {
3742 Evoral::MIDIEvent<MidiBuffer::TimeType> const ev (*i, false);
3744 if (ev.is_channel_event()) {
3745 if (get_channel_mode() == FilterChannels) {
3746 if (((uint16_t(1) << ev.channel()) & get_selected_channels()) == 0) {
3752 /* convert from session frames to source beats */
3753 Evoral::MusicalTime const time_beats = _source_relative_time_converter.from(ev.time());
3755 if (ev.type() == MIDI_CMD_NOTE_ON) {
3756 boost::shared_ptr<NoteType> note (
3757 new NoteType (ev.channel(), time_beats, Evoral::MusicalTime(), ev.note(), ev.velocity()));
3759 add_note (note, true);
3761 /* fix up our note range */
3762 if (ev.note() < _current_range_min) {
3763 midi_stream_view()->apply_note_range (ev.note(), _current_range_max, true);
3764 } else if (ev.note() > _current_range_max) {
3765 midi_stream_view()->apply_note_range (_current_range_min, ev.note(), true);
3768 } else if (ev.type() == MIDI_CMD_NOTE_OFF) {
3769 resolve_note (ev.note (), time_beats);
3775 midi_stream_view()->check_record_layers (region(), back);
3779 MidiRegionView::trim_front_starting ()
3781 /* Reparent the note group to the region view's parent, so that it doesn't change
3782 when the region view is trimmed.
3784 _temporary_note_group = new ArdourCanvas::Container (group->parent ());
3785 _temporary_note_group->move (group->position ());
3786 _note_group->reparent (_temporary_note_group);
3790 MidiRegionView::trim_front_ending ()
3792 _note_group->reparent (group);
3793 delete _temporary_note_group;
3794 _temporary_note_group = 0;
3796 if (_region->start() < 0) {
3797 /* Trim drag made start time -ve; fix this */
3798 midi_region()->fix_negative_start ();
3803 MidiRegionView::edit_patch_change (PatchChange* pc)
3805 PatchChangeDialog d (&_source_relative_time_converter, trackview.session(), *pc->patch (), instrument_info(), Gtk::Stock::APPLY, true);
3807 int response = d.run();
3810 case Gtk::RESPONSE_ACCEPT:
3812 case Gtk::RESPONSE_REJECT:
3813 delete_patch_change (pc);
3819 change_patch_change (pc->patch(), d.patch ());
3823 MidiRegionView::delete_sysex (SysEx* /*sysex*/)
3826 // sysyex object doesn't have a pointer to a sysex event
3827 // MidiModel::SysExDiffCommand* c = _model->new_sysex_diff_command (_("delete sysex"));
3828 // c->remove (sysex->sysex());
3829 // _model->apply_command (*trackview.session(), c);
3831 //_sys_exes.clear ();
3832 // display_sysexes();
3836 MidiRegionView::show_verbose_cursor (boost::shared_ptr<NoteType> n) const
3838 using namespace MIDI::Name;
3842 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3844 boost::shared_ptr<MasterDeviceNames> device_names(mtv->get_device_names());
3846 MIDI::Name::PatchPrimaryKey patch_key;
3847 get_patch_key_at(n->time(), n->channel(), patch_key);
3848 name = device_names->note_name(mtv->gui_property(X_("midnam-custom-device-mode")),
3851 patch_key.program(),
3857 snprintf (buf, sizeof (buf), "%d %s\nCh %d Vel %d",
3859 name.empty() ? Evoral::midi_note_name (n->note()).c_str() : name.c_str(),
3860 (int) n->channel() + 1,
3861 (int) n->velocity());
3863 show_verbose_cursor(buf, 10, 20);
3867 MidiRegionView::show_verbose_cursor (string const & text, double xoffset, double yoffset) const
3869 trackview.editor().verbose_cursor()->set (text);
3870 trackview.editor().verbose_cursor()->show ();
3871 trackview.editor().verbose_cursor()->set_offset (ArdourCanvas::Duple (xoffset, yoffset));
3874 /** @param p A session framepos.
3875 * @param grid_frames Filled in with the number of frames that a grid interval is at p.
3876 * @return p snapped to the grid subdivision underneath it.
3879 MidiRegionView::snap_frame_to_grid_underneath (framepos_t p, framecnt_t& grid_frames) const
3881 PublicEditor& editor = trackview.editor ();
3883 const Evoral::MusicalTime grid_beats = get_grid_beats(p);
3885 grid_frames = region_beats_to_region_frames (grid_beats);
3887 /* Hack so that we always snap to the note that we are over, instead of snapping
3888 to the next one if we're more than halfway through the one we're over.
3890 if (editor.snap_mode() == SnapNormal && p >= grid_frames / 2) {
3891 p -= grid_frames / 2;
3894 return snap_frame_to_frame (p);
3897 /** Called when the selection has been cleared in any MidiRegionView.
3898 * @param rv MidiRegionView that the selection was cleared in.
3901 MidiRegionView::selection_cleared (MidiRegionView* rv)
3907 /* Clear our selection in sympathy; but don't signal the fact */
3908 clear_selection (false);
3912 MidiRegionView::note_button_release ()
3914 delete _note_player;
3919 MidiRegionView::get_channel_mode () const
3921 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (&trackview);
3922 return rtav->midi_track()->get_playback_channel_mode();
3926 MidiRegionView::get_selected_channels () const
3928 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (&trackview);
3929 return rtav->midi_track()->get_playback_channel_mask();
3934 MidiRegionView::get_grid_beats(framepos_t pos) const
3936 PublicEditor& editor = trackview.editor();
3937 bool success = false;
3938 Evoral::MusicalTime beats = editor.get_grid_type_as_beats(success, pos);
3940 beats = Evoral::MusicalTime(1);