2 Copyright (C) 2001-2011 Paul Davis
3 Author: David Robillard
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 #include "gtkmm2ext/gtk_ui.h"
28 #include <sigc++/signal.h>
30 #include "pbd/memento_command.h"
31 #include "pbd/stateful_diff_command.h"
33 #include "ardour/midi_model.h"
34 #include "ardour/midi_patch_manager.h"
35 #include "ardour/midi_region.h"
36 #include "ardour/midi_source.h"
37 #include "ardour/midi_track.h"
38 #include "ardour/session.h"
40 #include "evoral/Parameter.hpp"
41 #include "evoral/MIDIParameters.hpp"
42 #include "evoral/MIDIEvent.hpp"
43 #include "evoral/Control.hpp"
44 #include "evoral/midi_util.h"
46 #include "canvas/debug.h"
48 #include "automation_region_view.h"
49 #include "automation_time_axis.h"
52 #include "editor_drag.h"
53 #include "ghostregion.h"
54 #include "gui_thread.h"
56 #include "midi_channel_dialog.h"
57 #include "midi_cut_buffer.h"
58 #include "midi_list_editor.h"
59 #include "midi_region_view.h"
60 #include "midi_streamview.h"
61 #include "midi_time_axis.h"
62 #include "midi_util.h"
63 #include "midi_velocity_dialog.h"
64 #include "mouse_cursors.h"
65 #include "note_player.h"
66 #include "public_editor.h"
67 #include "route_time_axis.h"
68 #include "rgb_macros.h"
69 #include "selection.h"
70 #include "streamview.h"
72 #include "patch_change_dialog.h"
73 #include "verbose_cursor.h"
74 #include "ardour_ui.h"
77 #include "patch_change.h"
82 using namespace ARDOUR;
84 using namespace Editing;
85 using Gtkmm2ext::Keyboard;
87 PBD::Signal1<void, MidiRegionView *> MidiRegionView::SelectionCleared;
89 #define MIDI_BP_ZERO ((Config->get_first_midi_bank_is_zero())?0:1)
91 MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &tv,
92 boost::shared_ptr<MidiRegion> r, double spu, Gdk::Color const & basic_color)
93 : RegionView (parent, tv, r, spu, basic_color)
94 , _current_range_min(0)
95 , _current_range_max(0)
97 , _note_group (new ArdourCanvas::Group (group))
98 , _note_diff_command (0)
100 , _step_edit_cursor (0)
101 , _step_edit_cursor_width (1.0)
102 , _step_edit_cursor_position (0.0)
103 , _channel_selection_scoped_note (0)
104 , _temporary_note_group (0)
107 , _sort_needed (true)
108 , _optimization_iterator (_events.end())
110 , _no_sound_notes (false)
113 , pre_enter_cursor (0)
114 , pre_press_cursor (0)
117 CANVAS_DEBUG_NAME (_note_group, string_compose ("note group for %1", get_item_name()));
118 _note_group->raise_to_top();
119 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
121 Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&MidiRegionView::parameter_changed, this, _1), gui_context());
122 connect_to_diskstream ();
124 SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), boost::bind (&MidiRegionView::selection_cleared, this, _1), gui_context ());
127 MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &tv,
128 boost::shared_ptr<MidiRegion> r, double spu, Gdk::Color& basic_color,
129 TimeAxisViewItem::Visibility visibility)
130 : RegionView (parent, tv, r, spu, basic_color, false, visibility)
131 , _current_range_min(0)
132 , _current_range_max(0)
134 , _note_group (new ArdourCanvas::Group (parent))
135 , _note_diff_command (0)
137 , _step_edit_cursor (0)
138 , _step_edit_cursor_width (1.0)
139 , _step_edit_cursor_position (0.0)
140 , _channel_selection_scoped_note (0)
141 , _temporary_note_group (0)
144 , _sort_needed (true)
145 , _optimization_iterator (_events.end())
147 , _no_sound_notes (false)
150 , pre_enter_cursor (0)
151 , pre_press_cursor (0)
154 CANVAS_DEBUG_NAME (_note_group, string_compose ("note group for %1", get_item_name()));
155 _note_group->raise_to_top();
157 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
159 connect_to_diskstream ();
161 SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), boost::bind (&MidiRegionView::selection_cleared, this, _1), gui_context ());
165 MidiRegionView::parameter_changed (std::string const & p)
167 if (p == "diplay-first-midi-bank-as-zero") {
168 if (_enable_display) {
174 MidiRegionView::MidiRegionView (const MidiRegionView& other)
175 : sigc::trackable(other)
177 , _current_range_min(0)
178 , _current_range_max(0)
180 , _note_group (new ArdourCanvas::Group (get_canvas_group()))
181 , _note_diff_command (0)
183 , _step_edit_cursor (0)
184 , _step_edit_cursor_width (1.0)
185 , _step_edit_cursor_position (0.0)
186 , _channel_selection_scoped_note (0)
187 , _temporary_note_group (0)
190 , _sort_needed (true)
191 , _optimization_iterator (_events.end())
193 , _no_sound_notes (false)
196 , pre_enter_cursor (0)
197 , pre_press_cursor (0)
203 UINT_TO_RGBA (other.fill_color, &r, &g, &b, &a);
204 c.set_rgb_p (r/255.0, g/255.0, b/255.0);
209 MidiRegionView::MidiRegionView (const MidiRegionView& other, boost::shared_ptr<MidiRegion> region)
210 : RegionView (other, boost::shared_ptr<Region> (region))
211 , _current_range_min(0)
212 , _current_range_max(0)
214 , _note_group (new ArdourCanvas::Group (get_canvas_group()))
215 , _note_diff_command (0)
217 , _step_edit_cursor (0)
218 , _step_edit_cursor_width (1.0)
219 , _step_edit_cursor_position (0.0)
220 , _channel_selection_scoped_note (0)
221 , _temporary_note_group (0)
224 , _sort_needed (true)
225 , _optimization_iterator (_events.end())
227 , _no_sound_notes (false)
230 , pre_enter_cursor (0)
231 , pre_press_cursor (0)
237 UINT_TO_RGBA (other.fill_color, &r, &g, &b, &a);
238 c.set_rgb_p (r/255.0, g/255.0, b/255.0);
244 MidiRegionView::init (Gdk::Color const & basic_color, bool wfd)
246 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
248 NoteBase::NoteBaseDeleted.connect (note_delete_connection, MISSING_INVALIDATOR,
249 boost::bind (&MidiRegionView::maybe_remove_deleted_note_from_selection, this, _1),
253 midi_region()->midi_source(0)->load_model();
256 _model = midi_region()->midi_source(0)->model();
257 _enable_display = false;
259 RegionView::init (basic_color, false);
261 compute_colors (basic_color);
263 set_height (trackview.current_height());
266 region_sync_changed ();
267 region_resized (ARDOUR::bounds_change);
272 _enable_display = true;
275 display_model (_model);
279 reset_width_dependent_items (_pixel_width);
281 group->raise_to_top();
283 midi_view()->midi_track()->PlaybackChannelModeChanged.connect (_channel_mode_changed_connection, invalidator (*this),
284 boost::bind (&MidiRegionView::midi_channel_mode_changed, this),
287 instrument_info().Changed.connect (_instrument_changed_connection, invalidator (*this),
288 boost::bind (&MidiRegionView::instrument_settings_changed, this), gui_context());
290 trackview.editor().SnapChanged.connect(snap_changed_connection, invalidator(*this),
291 boost::bind (&MidiRegionView::snap_changed, this),
294 Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&MidiRegionView::parameter_changed, this, _1), gui_context());
295 connect_to_diskstream ();
297 SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), boost::bind (&MidiRegionView::selection_cleared, this, _1), gui_context ());
301 MidiRegionView::instrument_info () const
303 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
304 return route_ui->route()->instrument_info();
307 const boost::shared_ptr<ARDOUR::MidiRegion>
308 MidiRegionView::midi_region() const
310 return boost::dynamic_pointer_cast<ARDOUR::MidiRegion>(_region);
314 MidiRegionView::connect_to_diskstream ()
316 midi_view()->midi_track()->DataRecorded.connect(
317 *this, invalidator(*this),
318 boost::bind (&MidiRegionView::data_recorded, this, _1),
323 MidiRegionView::canvas_group_event(GdkEvent* ev)
328 case GDK_ENTER_NOTIFY:
329 case GDK_LEAVE_NOTIFY:
330 _last_event_x = ev->crossing.x;
331 _last_event_y = ev->crossing.y;
333 case GDK_MOTION_NOTIFY:
334 _last_event_x = ev->motion.x;
335 _last_event_y = ev->motion.y;
341 if (ev->type == GDK_2BUTTON_PRESS) {
342 // cannot use double-click to exit internal mode if single-click is being used
343 MouseMode m = trackview.editor().current_mouse_mode();
345 if ((m != MouseObject || !Keyboard::modifier_state_contains (ev->button.state, Keyboard::insert_note_modifier())) && (m != MouseDraw)) {
346 return trackview.editor().toggle_internal_editing_from_double_click (ev);
350 if ((!trackview.editor().internal_editing() && trackview.editor().current_mouse_mode() != MouseGain) ||
351 (trackview.editor().current_mouse_mode() == MouseTimeFX) ||
352 (trackview.editor().current_mouse_mode() == MouseZoom)) {
353 // handle non-internal-edit/non-draw modes elsewhere
354 return RegionView::canvas_group_event (ev);
359 return scroll (&ev->scroll);
362 return key_press (&ev->key);
364 case GDK_KEY_RELEASE:
365 return key_release (&ev->key);
367 case GDK_BUTTON_PRESS:
368 return button_press (&ev->button);
370 case GDK_BUTTON_RELEASE:
371 r = button_release (&ev->button);
376 case GDK_ENTER_NOTIFY:
377 return enter_notify (&ev->crossing);
379 case GDK_LEAVE_NOTIFY:
380 return leave_notify (&ev->crossing);
382 case GDK_MOTION_NOTIFY:
383 return motion (&ev->motion);
389 return trackview.editor().canvas_region_view_event (ev, group, this);
393 MidiRegionView::enter_notify (GdkEventCrossing* ev)
395 trackview.editor().MouseModeChanged.connect (
396 _mouse_mode_connection, invalidator (*this), boost::bind (&MidiRegionView::mouse_mode_changed, this), gui_context ()
399 if (trackview.editor().current_mouse_mode() == MouseDraw && _mouse_state != AddDragging) {
400 create_ghost_note (ev->x, ev->y);
403 if (!trackview.editor().internal_editing()) {
404 Keyboard::magic_widget_drop_focus();
406 Keyboard::magic_widget_grab_focus();
410 // if current operation is non-operational in a midi region, change the cursor to so indicate
411 if (trackview.editor().current_mouse_mode() == MouseGain) {
412 Editor* editor = dynamic_cast<Editor *> (&trackview.editor());
413 pre_enter_cursor = editor->get_canvas_cursor();
414 editor->set_canvas_cursor(editor->cursors()->timebar);
421 MidiRegionView::leave_notify (GdkEventCrossing*)
423 _mouse_mode_connection.disconnect ();
425 trackview.editor().verbose_cursor()->hide ();
426 remove_ghost_note ();
428 if (pre_enter_cursor) {
429 Editor* editor = dynamic_cast<Editor *> (&trackview.editor());
430 editor->set_canvas_cursor(pre_enter_cursor);
437 MidiRegionView::mouse_mode_changed ()
439 if (trackview.editor().current_mouse_mode() == MouseDraw && trackview.editor().internal_editing()) {
440 create_ghost_note (_last_event_x, _last_event_y);
442 remove_ghost_note ();
443 trackview.editor().verbose_cursor()->hide ();
446 if (!trackview.editor().internal_editing()) {
447 Keyboard::magic_widget_drop_focus();
449 Keyboard::magic_widget_grab_focus();
455 MidiRegionView::button_press (GdkEventButton* ev)
457 if (ev->button != 1) {
461 Editor* editor = dynamic_cast<Editor *> (&trackview.editor());
462 MouseMode m = editor->current_mouse_mode();
464 if (m == MouseObject && Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())) {
465 pre_press_cursor = editor->get_canvas_cursor ();
466 editor->set_canvas_cursor (editor->cursors()->midi_pencil);
469 if (_mouse_state != SelectTouchDragging) {
471 _pressed_button = ev->button;
472 _mouse_state = Pressed;
477 _pressed_button = ev->button;
483 MidiRegionView::button_release (GdkEventButton* ev)
485 double event_x, event_y;
487 if (ev->button != 1) {
494 group->canvas_to_item (event_x, event_y);
497 PublicEditor& editor = trackview.editor ();
499 if (pre_press_cursor) {
500 dynamic_cast<Editor*>(&editor)->set_canvas_cursor (pre_press_cursor, false);
501 pre_press_cursor = 0;
504 switch (_mouse_state) {
505 case Pressed: // Clicked
507 switch (editor.current_mouse_mode()) {
509 /* no motion occured - simple click */
518 if (Keyboard::is_insert_note_event(ev)) {
520 double event_x, event_y;
524 group->canvas_to_item (event_x, event_y);
527 Evoral::MusicalTime beats = editor.get_grid_type_as_beats (success, editor.pixel_to_sample (event_x));
533 /* Shorten the length by 1 tick so that we can add a new note at the next
534 grid snap without it overlapping this one.
536 beats -= 1.0 / Timecode::BBT_Time::ticks_per_beat;
538 create_note_at (editor.pixel_to_sample (event_x), event_y, beats, true);
546 Evoral::MusicalTime beats = editor.get_grid_type_as_beats (success, editor.pixel_to_sample (event_x));
552 /* Shorten the length by 1 tick so that we can add a new note at the next
553 grid snap without it overlapping this one.
555 beats -= 1.0 / Timecode::BBT_Time::ticks_per_beat;
557 create_note_at (editor.pixel_to_sample (event_x), event_y, beats, true);
568 case SelectRectDragging:
570 editor.drags()->end_grab ((GdkEvent *) ev);
572 create_ghost_note (ev->x, ev->y);
584 MidiRegionView::motion (GdkEventMotion* ev)
586 PublicEditor& editor = trackview.editor ();
588 if (!_ghost_note && editor.current_mouse_mode() == MouseObject &&
589 Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier()) &&
590 _mouse_state != AddDragging) {
592 create_ghost_note (ev->x, ev->y);
594 } else if (_ghost_note && editor.current_mouse_mode() == MouseObject &&
595 Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())) {
597 update_ghost_note (ev->x, ev->y);
599 } else if (_ghost_note && editor.current_mouse_mode() == MouseObject) {
601 remove_ghost_note ();
602 editor.verbose_cursor()->hide ();
604 } else if (_ghost_note && editor.current_mouse_mode() == MouseDraw) {
606 update_ghost_note (ev->x, ev->y);
609 /* any motion immediately hides velocity text that may have been visible */
611 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
612 (*i)->hide_velocity ();
615 switch (_mouse_state) {
618 if (_pressed_button == 1) {
620 MouseMode m = editor.current_mouse_mode();
622 if (m == MouseDraw || (m == MouseObject && Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier()))) {
623 editor.drags()->set (new NoteCreateDrag (dynamic_cast<Editor *> (&editor), group, this), (GdkEvent *) ev);
624 _mouse_state = AddDragging;
625 remove_ghost_note ();
626 editor.verbose_cursor()->hide ();
628 } else if (m == MouseObject) {
629 editor.drags()->set (new MidiRubberbandSelectDrag (dynamic_cast<Editor *> (&editor), this), (GdkEvent *) ev);
631 _mouse_state = SelectRectDragging;
633 } else if (m == MouseRange) {
634 editor.drags()->set (new MidiVerticalSelectDrag (dynamic_cast<Editor *> (&editor), this), (GdkEvent *) ev);
635 _mouse_state = SelectVerticalDragging;
642 case SelectRectDragging:
643 case SelectVerticalDragging:
645 editor.drags()->motion_handler ((GdkEvent *) ev, false);
648 case SelectTouchDragging:
660 MidiRegionView::scroll (GdkEventScroll* ev)
662 if (_selection.empty()) {
666 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
667 /* XXX: bit of a hack; allow PrimaryModifier scroll through so that
668 it still works for zoom.
673 trackview.editor().verbose_cursor()->hide ();
675 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
676 bool together = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
678 if (ev->direction == GDK_SCROLL_UP) {
679 change_velocities (true, fine, false, together);
680 } else if (ev->direction == GDK_SCROLL_DOWN) {
681 change_velocities (false, fine, false, together);
683 /* left, right: we don't use them */
691 MidiRegionView::key_press (GdkEventKey* ev)
693 /* since GTK bindings are generally activated on press, and since
694 detectable auto-repeat is the name of the game and only sends
695 repeated presses, carry out key actions at key press, not release.
698 bool unmodified = Keyboard::no_modifier_keys_pressed (ev);
700 if (unmodified && (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R)) {
701 _mouse_state = SelectTouchDragging;
704 } else if (ev->keyval == GDK_Escape && unmodified) {
708 } else if (unmodified && (ev->keyval == GDK_comma || ev->keyval == GDK_period)) {
710 bool start = (ev->keyval == GDK_comma);
711 bool end = (ev->keyval == GDK_period);
712 bool shorter = Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier);
713 bool fine = Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
715 change_note_lengths (fine, shorter, 0.0, start, end);
719 } else if ((ev->keyval == GDK_BackSpace || ev->keyval == GDK_Delete) && unmodified) {
721 if (_selection.empty()) {
728 } else if (ev->keyval == GDK_Tab) {
730 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
731 goto_previous_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
733 goto_next_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
737 } else if (ev->keyval == GDK_ISO_Left_Tab) {
739 /* Shift-TAB generates ISO Left Tab, for some reason */
741 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
742 goto_previous_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
744 goto_next_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
750 } else if (ev->keyval == GDK_Up) {
752 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
753 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
754 bool together = Keyboard::modifier_state_contains (ev->state, Keyboard::Level4Modifier);
756 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
757 change_velocities (true, fine, allow_smush, together);
759 transpose (true, fine, allow_smush);
763 } else if (ev->keyval == GDK_Down) {
765 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
766 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
767 bool together = Keyboard::modifier_state_contains (ev->state, Keyboard::Level4Modifier);
769 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
770 change_velocities (false, fine, allow_smush, together);
772 transpose (false, fine, allow_smush);
776 } else if (ev->keyval == GDK_Left && unmodified) {
781 } else if (ev->keyval == GDK_Right && unmodified) {
786 } else if (ev->keyval == GDK_c && unmodified) {
790 } else if (ev->keyval == GDK_v && unmodified) {
799 MidiRegionView::key_release (GdkEventKey* ev)
801 if ((_mouse_state == SelectTouchDragging) && (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R)) {
809 MidiRegionView::channel_edit ()
811 if (_selection.empty()) {
815 /* pick a note somewhat at random (since Selection is a set<>) to
816 * provide the "current" channel for the dialog.
819 uint8_t current_channel = (*_selection.begin())->note()->channel ();
820 MidiChannelDialog channel_dialog (current_channel);
821 int ret = channel_dialog.run ();
824 case Gtk::RESPONSE_OK:
830 uint8_t new_channel = channel_dialog.active_channel ();
832 start_note_diff_command (_("channel edit"));
834 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
835 Selection::iterator next = i;
837 change_note_channel (*i, new_channel);
845 MidiRegionView::velocity_edit ()
847 if (_selection.empty()) {
851 /* pick a note somewhat at random (since Selection is a set<>) to
852 * provide the "current" velocity for the dialog.
855 uint8_t current_velocity = (*_selection.begin())->note()->velocity ();
856 MidiVelocityDialog velocity_dialog (current_velocity);
857 int ret = velocity_dialog.run ();
860 case Gtk::RESPONSE_OK:
866 uint8_t new_velocity = velocity_dialog.velocity ();
868 start_note_diff_command (_("velocity edit"));
870 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
871 Selection::iterator next = i;
873 change_note_velocity (*i, new_velocity, false);
881 MidiRegionView::show_list_editor ()
884 _list_editor = new MidiListEditor (trackview.session(), midi_region(), midi_view()->midi_track());
886 _list_editor->present ();
889 /** Add a note to the model, and the view, at a canvas (click) coordinate.
890 * \param t time in frames relative to the position of the region
891 * \param y vertical position in pixels
892 * \param length duration of the note in beats
893 * \param snap_t true to snap t to the grid, otherwise false.
896 MidiRegionView::create_note_at (framepos_t t, double y, double length, bool snap_t)
898 if (length < 2 * DBL_EPSILON) {
902 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
903 MidiStreamView* const view = mtv->midi_view();
905 const double note = view->y_to_note(y);
907 // Start of note in frames relative to region start
909 framecnt_t grid_frames;
910 t = snap_frame_to_grid_underneath (t, grid_frames);
913 const boost::shared_ptr<NoteType> new_note (
914 new NoteType (mtv->get_channel_for_add (),
915 region_frames_to_region_beats(t + _region->start()),
917 (uint8_t)note, 0x40));
919 if (_model->contains (new_note)) {
923 view->update_note_range(new_note->note());
925 MidiModel::NoteDiffCommand* cmd = _model->new_note_diff_command(_("add note"));
927 _model->apply_command(*trackview.session(), cmd);
929 play_midi_note (new_note);
933 MidiRegionView::clear_events (bool with_selection_signal)
935 clear_selection (with_selection_signal);
938 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
939 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
944 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
949 _patch_changes.clear();
951 _optimization_iterator = _events.end();
955 MidiRegionView::display_model(boost::shared_ptr<MidiModel> model)
959 content_connection.disconnect ();
960 _model->ContentsChanged.connect (content_connection, invalidator (*this), boost::bind (&MidiRegionView::redisplay_model, this), gui_context());
964 if (_enable_display) {
970 MidiRegionView::start_note_diff_command (string name)
972 if (!_note_diff_command) {
973 _note_diff_command = _model->new_note_diff_command (name);
978 MidiRegionView::note_diff_add_note (const boost::shared_ptr<NoteType> note, bool selected, bool show_velocity)
980 if (_note_diff_command) {
981 _note_diff_command->add (note);
984 _marked_for_selection.insert(note);
987 _marked_for_velocity.insert(note);
992 MidiRegionView::note_diff_remove_note (NoteBase* ev)
994 if (_note_diff_command && ev->note()) {
995 _note_diff_command->remove(ev->note());
1000 MidiRegionView::note_diff_add_change (NoteBase* ev,
1001 MidiModel::NoteDiffCommand::Property property,
1004 if (_note_diff_command) {
1005 _note_diff_command->change (ev->note(), property, val);
1010 MidiRegionView::note_diff_add_change (NoteBase* ev,
1011 MidiModel::NoteDiffCommand::Property property,
1012 Evoral::MusicalTime val)
1014 if (_note_diff_command) {
1015 _note_diff_command->change (ev->note(), property, val);
1020 MidiRegionView::apply_diff (bool as_subcommand)
1024 if (!_note_diff_command) {
1028 if ((add_or_remove = _note_diff_command->adds_or_removes())) {
1029 // Mark all selected notes for selection when model reloads
1030 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1031 _marked_for_selection.insert((*i)->note());
1035 if (as_subcommand) {
1036 _model->apply_command_as_subcommand (*trackview.session(), _note_diff_command);
1038 _model->apply_command (*trackview.session(), _note_diff_command);
1041 _note_diff_command = 0;
1042 midi_view()->midi_track()->playlist_modified();
1044 if (add_or_remove) {
1045 _marked_for_selection.clear();
1048 _marked_for_velocity.clear();
1052 MidiRegionView::abort_command()
1054 delete _note_diff_command;
1055 _note_diff_command = 0;
1060 MidiRegionView::find_canvas_note (boost::shared_ptr<NoteType> note)
1062 if (_optimization_iterator != _events.end()) {
1063 ++_optimization_iterator;
1066 if (_optimization_iterator != _events.end() && (*_optimization_iterator)->note() == note) {
1067 return *_optimization_iterator;
1070 for (_optimization_iterator = _events.begin(); _optimization_iterator != _events.end(); ++_optimization_iterator) {
1071 if ((*_optimization_iterator)->note() == note) {
1072 return *_optimization_iterator;
1080 MidiRegionView::get_events (Events& e, Evoral::Sequence<Evoral::MusicalTime>::NoteOperator op, uint8_t val, int chan_mask)
1082 MidiModel::Notes notes;
1083 _model->get_notes (notes, op, val, chan_mask);
1085 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1086 NoteBase* cne = find_canvas_note (*n);
1094 MidiRegionView::redisplay_model()
1096 // Don't redisplay the model if we're currently recording and displaying that
1097 if (_active_notes) {
1105 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1106 (*i)->invalidate ();
1109 MidiModel::ReadLock lock(_model->read_lock());
1111 MidiModel::Notes& notes (_model->notes());
1112 _optimization_iterator = _events.begin();
1114 bool empty_when_starting = !_events.empty();
1116 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1118 boost::shared_ptr<NoteType> note (*n);
1122 if (note_in_region_range (note, visible)) {
1124 if (empty_when_starting && (cne = find_canvas_note (note)) != 0) {
1131 if ((cn = dynamic_cast<Note*>(cne)) != 0) {
1133 } else if ((ch = dynamic_cast<Hit*>(cne)) != 0) {
1145 add_note (note, visible);
1150 if (empty_when_starting && (cne = find_canvas_note (note)) != 0) {
1158 /* remove note items that are no longer valid */
1160 if (empty_when_starting) {
1161 for (Events::iterator i = _events.begin(); i != _events.end(); ) {
1162 if (!(*i)->valid ()) {
1164 for (vector<GhostRegion*>::iterator j = ghosts.begin(); j != ghosts.end(); ++j) {
1165 MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*j);
1167 gr->remove_note (*i);
1172 i = _events.erase (i);
1180 _patch_changes.clear();
1184 display_patch_changes ();
1186 _marked_for_selection.clear ();
1187 _marked_for_velocity.clear ();
1189 /* we may have caused _events to contain things out of order (e.g. if a note
1190 moved earlier or later). we don't generally need them in time order, but
1191 make a note that a sort is required for those cases that require it.
1194 _sort_needed = true;
1198 MidiRegionView::display_patch_changes ()
1200 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1201 uint16_t chn_mask = mtv->midi_track()->get_playback_channel_mask();
1203 for (uint8_t i = 0; i < 16; ++i) {
1204 display_patch_changes_on_channel (i, chn_mask & (1 << i));
1208 /** @param active_channel true to display patch changes fully, false to display
1209 * them `greyed-out' (as on an inactive channel)
1212 MidiRegionView::display_patch_changes_on_channel (uint8_t channel, bool active_channel)
1214 for (MidiModel::PatchChanges::const_iterator i = _model->patch_changes().begin(); i != _model->patch_changes().end(); ++i) {
1216 if ((*i)->channel() != channel) {
1220 const string patch_name = instrument_info().get_patch_name ((*i)->bank(), (*i)->program(), channel);
1221 add_canvas_patch_change (*i, patch_name, active_channel);
1226 MidiRegionView::display_sysexes()
1228 bool have_periodic_system_messages = false;
1229 bool display_periodic_messages = true;
1231 if (!Config->get_never_display_periodic_midi()) {
1233 for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
1234 const boost::shared_ptr<const Evoral::MIDIEvent<Evoral::MusicalTime> > mev =
1235 boost::static_pointer_cast<const Evoral::MIDIEvent<Evoral::MusicalTime> > (*i);
1238 if (mev->is_spp() || mev->is_mtc_quarter() || mev->is_mtc_full()) {
1239 have_periodic_system_messages = true;
1245 if (have_periodic_system_messages) {
1246 double zoom = trackview.editor().get_current_zoom (); // frames per pixel
1248 /* get an approximate value for the number of samples per video frame */
1250 double video_frame = trackview.session()->frame_rate() * (1.0/30);
1252 /* if we are zoomed out beyond than the cutoff (i.e. more
1253 * frames per pixel than frames per 4 video frames), don't
1254 * show periodic sysex messages.
1257 if (zoom > (video_frame*4)) {
1258 display_periodic_messages = false;
1262 display_periodic_messages = false;
1265 for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
1267 const boost::shared_ptr<const Evoral::MIDIEvent<Evoral::MusicalTime> > mev =
1268 boost::static_pointer_cast<const Evoral::MIDIEvent<Evoral::MusicalTime> > (*i);
1270 Evoral::MusicalTime time = (*i)->time();
1273 if (mev->is_spp() || mev->is_mtc_quarter() || mev->is_mtc_full()) {
1274 if (!display_periodic_messages) {
1282 for (uint32_t b = 0; b < (*i)->size(); ++b) {
1283 str << int((*i)->buffer()[b]);
1284 if (b != (*i)->size() -1) {
1288 string text = str.str();
1290 const double x = trackview.editor().sample_to_pixel(source_beats_to_region_frames(time));
1292 double height = midi_stream_view()->contents_height();
1294 // CAIROCANVAS: no longer passing *i (the sysex event) to the
1295 // SysEx canvas object!!!
1297 boost::shared_ptr<SysEx> sysex = boost::shared_ptr<SysEx>(
1298 new SysEx (*this, _note_group, text, height, x, 1.0));
1300 // Show unless message is beyond the region bounds
1301 if (time - _region->start() >= _region->length() || time < _region->start()) {
1307 _sys_exes.push_back(sysex);
1311 MidiRegionView::~MidiRegionView ()
1313 in_destructor = true;
1315 trackview.editor().verbose_cursor()->hide ();
1317 note_delete_connection.disconnect ();
1319 delete _list_editor;
1321 RegionViewGoingAway (this); /* EMIT_SIGNAL */
1323 if (_active_notes) {
1327 _selection_cleared_connection.disconnect ();
1330 clear_events (false);
1333 delete _note_diff_command;
1334 delete _step_edit_cursor;
1335 delete _temporary_note_group;
1339 MidiRegionView::region_resized (const PropertyChange& what_changed)
1341 RegionView::region_resized(what_changed);
1343 if (what_changed.contains (ARDOUR::Properties::position)) {
1344 set_duration(_region->length(), 0);
1345 if (_enable_display) {
1352 MidiRegionView::reset_width_dependent_items (double pixel_width)
1354 RegionView::reset_width_dependent_items(pixel_width);
1356 if (_enable_display) {
1360 for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
1361 if ((*x)->canvas_item()->width() >= _pixel_width) {
1368 move_step_edit_cursor (_step_edit_cursor_position);
1369 set_step_edit_cursor_width (_step_edit_cursor_width);
1373 MidiRegionView::set_height (double height)
1375 static const double FUDGE = 2.0;
1376 const double old_height = _height;
1377 RegionView::set_height(height);
1378 _height = height - FUDGE;
1380 apply_note_range(midi_stream_view()->lowest_note(),
1381 midi_stream_view()->highest_note(),
1382 height != old_height + FUDGE);
1385 name_text->raise_to_top();
1388 for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
1389 (*x)->set_height (midi_stream_view()->contents_height());
1392 if (_step_edit_cursor) {
1393 _step_edit_cursor->set_y1 (midi_stream_view()->contents_height());
1398 /** Apply the current note range from the stream view
1399 * by repositioning/hiding notes as necessary
1402 MidiRegionView::apply_note_range (uint8_t min, uint8_t max, bool force)
1404 if (!_enable_display) {
1408 if (!force && _current_range_min == min && _current_range_max == max) {
1412 _current_range_min = min;
1413 _current_range_max = max;
1415 for (Events::const_iterator i = _events.begin(); i != _events.end(); ++i) {
1416 NoteBase* event = *i;
1417 boost::shared_ptr<NoteType> note (event->note());
1419 if (note->note() < _current_range_min ||
1420 note->note() > _current_range_max) {
1426 if (Note* cnote = dynamic_cast<Note*>(event)) {
1428 const double y0 = midi_stream_view()->note_to_y(note->note());
1429 const double y1 = y0 + floor(midi_stream_view()->note_height());
1434 } else if (Hit* chit = dynamic_cast<Hit*>(event)) {
1436 const double diamond_size = update_hit (chit);
1438 chit->set_height (diamond_size);
1444 MidiRegionView::add_ghost (TimeAxisView& tv)
1448 double unit_position = _region->position () / samples_per_pixel;
1449 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(&tv);
1450 MidiGhostRegion* ghost;
1452 if (mtv && mtv->midi_view()) {
1453 /* if ghost is inserted into midi track, use a dedicated midi ghost canvas group
1454 to allow having midi notes on top of note lines and waveforms.
1456 ghost = new MidiGhostRegion (*mtv->midi_view(), trackview, unit_position);
1458 ghost = new MidiGhostRegion (tv, trackview, unit_position);
1461 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1462 if ((note = dynamic_cast<Note*>(*i)) != 0) {
1463 ghost->add_note(note);
1467 ghost->set_height ();
1468 ghost->set_duration (_region->length() / samples_per_pixel);
1469 ghosts.push_back (ghost);
1471 GhostRegion::CatchDeletion.connect (*this, invalidator (*this), boost::bind (&RegionView::remove_ghost, this, _1), gui_context());
1477 /** Begin tracking note state for successive calls to add_event
1480 MidiRegionView::begin_write()
1482 if (_active_notes) {
1483 delete[] _active_notes;
1485 _active_notes = new Note*[128];
1486 for (unsigned i = 0; i < 128; ++i) {
1487 _active_notes[i] = 0;
1492 /** Destroy note state for add_event
1495 MidiRegionView::end_write()
1497 delete[] _active_notes;
1499 _marked_for_selection.clear();
1500 _marked_for_velocity.clear();
1504 /** Resolve an active MIDI note (while recording).
1507 MidiRegionView::resolve_note(uint8_t note, double end_time)
1509 if (midi_view()->note_mode() != Sustained) {
1513 if (_active_notes && _active_notes[note]) {
1515 /* XXX is end_time really region-centric? I think so, because
1516 this is a new region that we're recording, so source zero is
1517 the same as region zero
1519 const framepos_t end_time_frames = region_beats_to_region_frames(end_time);
1521 _active_notes[note]->set_x1 (trackview.editor().sample_to_pixel(end_time_frames));
1522 _active_notes[note]->set_outline_what (0xf);
1523 _active_notes[note] = 0;
1528 /** Extend active notes to rightmost edge of region (if length is changed)
1531 MidiRegionView::extend_active_notes()
1533 if (!_active_notes) {
1537 for (unsigned i=0; i < 128; ++i) {
1538 if (_active_notes[i]) {
1539 _active_notes[i]->set_x1 (trackview.editor().sample_to_pixel(_region->length()));
1546 MidiRegionView::play_midi_note(boost::shared_ptr<NoteType> note)
1548 if (_no_sound_notes || !Config->get_sound_midi_notes()) {
1552 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1554 if (!route_ui || !route_ui->midi_track()) {
1558 NotePlayer* np = new NotePlayer (route_ui->midi_track ());
1562 /* NotePlayer deletes itself */
1566 MidiRegionView::start_playing_midi_note(boost::shared_ptr<NoteType> note)
1568 if (_no_sound_notes || !Config->get_sound_midi_notes()) {
1572 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1574 if (!route_ui || !route_ui->midi_track()) {
1578 delete _note_player;
1579 _note_player = new NotePlayer (route_ui->midi_track ());
1580 _note_player->add (note);
1581 _note_player->on ();
1585 MidiRegionView::start_playing_midi_chord (vector<boost::shared_ptr<NoteType> > notes)
1587 if (_no_sound_notes || !Config->get_sound_midi_notes()) {
1591 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1593 if (!route_ui || !route_ui->midi_track()) {
1597 delete _note_player;
1598 _note_player = new NotePlayer (route_ui->midi_track());
1600 for (vector<boost::shared_ptr<NoteType> >::iterator n = notes.begin(); n != notes.end(); ++n) {
1601 _note_player->add (*n);
1604 _note_player->on ();
1609 MidiRegionView::note_in_region_range (const boost::shared_ptr<NoteType> note, bool& visible) const
1611 const framepos_t note_start_frames = source_beats_to_region_frames (note->time());
1612 bool outside = (note_start_frames < 0) || (note_start_frames > _region->last_frame());
1614 visible = (note->note() >= midi_stream_view()->lowest_note()) &&
1615 (note->note() <= midi_stream_view()->highest_note());
1620 /** Update a canvas note's size from its model note.
1621 * @param ev Canvas note to update.
1622 * @param update_ghost_regions true to update the note in any ghost regions that we have, otherwise false.
1625 MidiRegionView::update_note (Note* ev, bool update_ghost_regions)
1627 boost::shared_ptr<NoteType> note = ev->note();
1628 const double x = trackview.editor().sample_to_pixel (source_beats_to_region_frames (note->time()));
1629 const double y0 = midi_stream_view()->note_to_y(note->note());
1634 /* trim note display to not overlap the end of its region */
1636 if (note->length() > 0) {
1637 const framepos_t note_end_frames = min (source_beats_to_region_frames (note->end_time()), _region->length());
1638 ev->set_x1 (trackview.editor().sample_to_pixel (note_end_frames));
1640 ev->set_x1 (trackview.editor().sample_to_pixel (_region->length()));
1643 ev->set_y1 (y0 + floor(midi_stream_view()->note_height()));
1645 if (note->length() == 0) {
1646 if (_active_notes && note->note() < 128) {
1647 // If this note is already active there's a stuck note,
1648 // finish the old note rectangle
1649 if (_active_notes[note->note()]) {
1650 Note* const old_rect = _active_notes[note->note()];
1651 boost::shared_ptr<NoteType> old_note = old_rect->note();
1652 old_rect->set_x1 (x);
1653 old_rect->set_outline_what (0xF);
1655 _active_notes[note->note()] = ev;
1657 /* outline all but right edge */
1658 ev->set_outline_what (0x1 & 0x4 & 0x8);
1660 /* outline all edges */
1661 ev->set_outline_what (0xF);
1664 if (update_ghost_regions) {
1665 for (std::vector<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
1666 MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*i);
1668 gr->update_note (ev);
1675 MidiRegionView::update_hit (Hit* ev)
1677 boost::shared_ptr<NoteType> note = ev->note();
1679 const framepos_t note_start_frames = source_beats_to_region_frames(note->time());
1680 const double x = trackview.editor().sample_to_pixel(note_start_frames);
1681 const double diamond_size = midi_stream_view()->note_height() / 2.0;
1682 const double y = midi_stream_view()->note_to_y(note->note()) + ((diamond_size-2) / 4.0);
1684 ev->set_position (ArdourCanvas::Duple (x, y));
1686 return diamond_size;
1689 /** Add a MIDI note to the view (with length).
1691 * If in sustained mode, notes with length 0 will be considered active
1692 * notes, and resolve_note should be called when the corresponding note off
1693 * event arrives, to properly display the note.
1696 MidiRegionView::add_note(const boost::shared_ptr<NoteType> note, bool visible)
1698 NoteBase* event = 0;
1700 //ArdourCanvas::Group* const group = (ArdourCanvas::Group*) get_canvas_group();
1702 if (midi_view()->note_mode() == Sustained) {
1704 Note* ev_rect = new Note (*this, _note_group, note);
1706 update_note (ev_rect);
1710 MidiGhostRegion* gr;
1712 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
1713 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
1714 gr->add_note(ev_rect);
1718 } else if (midi_view()->note_mode() == Percussive) {
1720 const double diamond_size = midi_stream_view()->note_height() / 2.0;
1722 Hit* ev_diamond = new Hit (*this, _note_group, diamond_size, note);
1724 update_hit (ev_diamond);
1733 if (_marked_for_selection.find(note) != _marked_for_selection.end()) {
1734 note_selected(event, true);
1737 if (_marked_for_velocity.find(note) != _marked_for_velocity.end()) {
1738 event->show_velocity();
1741 event->on_channel_selection_change (get_selected_channels());
1742 _events.push_back(event);
1751 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1752 MidiStreamView* const view = mtv->midi_view();
1754 view->update_note_range (note->note());
1758 MidiRegionView::step_add_note (uint8_t channel, uint8_t number, uint8_t velocity,
1759 Evoral::MusicalTime pos, Evoral::MusicalTime len)
1761 boost::shared_ptr<NoteType> new_note (new NoteType (channel, pos, len, number, velocity));
1763 /* potentially extend region to hold new note */
1765 framepos_t end_frame = source_beats_to_absolute_frames (new_note->end_time());
1766 framepos_t region_end = _region->last_frame();
1768 if (end_frame > region_end) {
1769 _region->set_length (end_frame - _region->position());
1772 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1773 MidiStreamView* const view = mtv->midi_view();
1775 view->update_note_range(new_note->note());
1777 _marked_for_selection.clear ();
1780 start_note_diff_command (_("step add"));
1781 note_diff_add_note (new_note, true, false);
1784 // last_step_edit_note = new_note;
1788 MidiRegionView::step_sustain (Evoral::MusicalTime beats)
1790 change_note_lengths (false, false, beats, false, true);
1793 /** Add a new patch change flag to the canvas.
1794 * @param patch the patch change to add
1795 * @param the text to display in the flag
1796 * @param active_channel true to display the flag as on an active channel, false to grey it out for an inactive channel.
1799 MidiRegionView::add_canvas_patch_change (MidiModel::PatchChangePtr patch, const string& displaytext, bool /*active_channel*/)
1801 framecnt_t region_frames = source_beats_to_region_frames (patch->time());
1802 const double x = trackview.editor().sample_to_pixel (region_frames);
1804 double const height = midi_stream_view()->contents_height();
1806 // CAIROCANVAS: active_channel info removed from PatcChange constructor
1807 // so we need to do something more sophisticated to keep its color
1808 // appearance (MidiPatchChangeFill/MidiPatchChangeInactiveChannelFill)
1811 boost::shared_ptr<PatchChange> patch_change = boost::shared_ptr<PatchChange>(
1812 new PatchChange(*this, group,
1819 if (patch_change->item().width() < _pixel_width) {
1820 // Show unless patch change is beyond the region bounds
1821 if (region_frames < 0 || region_frames >= _region->length()) {
1822 patch_change->hide();
1824 patch_change->show();
1827 patch_change->hide ();
1830 _patch_changes.push_back (patch_change);
1833 MIDI::Name::PatchPrimaryKey
1834 MidiRegionView::patch_change_to_patch_key (MidiModel::PatchChangePtr p)
1836 return MIDI::Name::PatchPrimaryKey (p->program(), p->bank());
1839 /// Return true iff @p pc applies to the given time on the given channel.
1841 patch_applies (const ARDOUR::MidiModel::constPatchChangePtr pc, double time, uint8_t channel)
1843 return pc->time() <= time && pc->channel() == channel;
1847 MidiRegionView::get_patch_key_at (double time, uint8_t channel, MIDI::Name::PatchPrimaryKey& key) const
1849 // The earliest event not before time
1850 MidiModel::PatchChanges::iterator i = _model->patch_change_lower_bound (time);
1852 // Go backwards until we find the latest PC for this channel, or the start
1853 while (i != _model->patch_changes().begin() &&
1854 (i == _model->patch_changes().end() ||
1855 !patch_applies(*i, time, channel))) {
1859 if (i != _model->patch_changes().end() && patch_applies(*i, time, channel)) {
1860 key.bank_number = (*i)->bank();
1861 key.program_number = (*i)->program ();
1863 key.bank_number = key.program_number = 0;
1866 if (!key.is_sane()) {
1867 error << string_compose(_("insane MIDI patch key %1:%2"),
1868 key.bank_number, key.program_number) << endmsg;
1873 MidiRegionView::change_patch_change (PatchChange& pc, const MIDI::Name::PatchPrimaryKey& new_patch)
1875 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("alter patch change"));
1877 if (pc.patch()->program() != new_patch.program_number) {
1878 c->change_program (pc.patch (), new_patch.program_number);
1881 int const new_bank = new_patch.bank_number;
1882 if (pc.patch()->bank() != new_bank) {
1883 c->change_bank (pc.patch (), new_bank);
1886 _model->apply_command (*trackview.session(), c);
1888 _patch_changes.clear ();
1889 display_patch_changes ();
1893 MidiRegionView::change_patch_change (MidiModel::PatchChangePtr old_change, const Evoral::PatchChange<Evoral::MusicalTime> & new_change)
1895 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("alter patch change"));
1897 if (old_change->time() != new_change.time()) {
1898 c->change_time (old_change, new_change.time());
1901 if (old_change->channel() != new_change.channel()) {
1902 c->change_channel (old_change, new_change.channel());
1905 if (old_change->program() != new_change.program()) {
1906 c->change_program (old_change, new_change.program());
1909 if (old_change->bank() != new_change.bank()) {
1910 c->change_bank (old_change, new_change.bank());
1913 _model->apply_command (*trackview.session(), c);
1915 _patch_changes.clear ();
1916 display_patch_changes ();
1919 /** Add a patch change to the region.
1920 * @param t Time in frames relative to region position
1921 * @param patch Patch to add; time and channel are ignored (time is converted from t, and channel comes from
1922 * MidiTimeAxisView::get_channel_for_add())
1925 MidiRegionView::add_patch_change (framecnt_t t, Evoral::PatchChange<Evoral::MusicalTime> const & patch)
1927 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1929 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("add patch change"));
1930 c->add (MidiModel::PatchChangePtr (
1931 new Evoral::PatchChange<Evoral::MusicalTime> (
1932 absolute_frames_to_source_beats (_region->position() + t),
1933 mtv->get_channel_for_add(), patch.program(), patch.bank()
1938 _model->apply_command (*trackview.session(), c);
1940 _patch_changes.clear ();
1941 display_patch_changes ();
1945 MidiRegionView::move_patch_change (PatchChange& pc, Evoral::MusicalTime t)
1947 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("move patch change"));
1948 c->change_time (pc.patch (), t);
1949 _model->apply_command (*trackview.session(), c);
1951 _patch_changes.clear ();
1952 display_patch_changes ();
1956 MidiRegionView::delete_patch_change (PatchChange* pc)
1958 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("delete patch change"));
1959 c->remove (pc->patch ());
1960 _model->apply_command (*trackview.session(), c);
1962 _patch_changes.clear ();
1963 display_patch_changes ();
1967 MidiRegionView::previous_patch (PatchChange& patch)
1969 if (patch.patch()->program() < 127) {
1970 MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch());
1971 key.program_number++;
1972 change_patch_change (patch, key);
1977 MidiRegionView::next_patch (PatchChange& patch)
1979 if (patch.patch()->program() > 0) {
1980 MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch());
1981 key.program_number--;
1982 change_patch_change (patch, key);
1987 MidiRegionView::next_bank (PatchChange& patch)
1989 if (patch.patch()->program() < 127) {
1990 MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch());
1991 if (key.bank_number > 0) {
1993 change_patch_change (patch, key);
1999 MidiRegionView::previous_bank (PatchChange& patch)
2001 if (patch.patch()->program() > 0) {
2002 MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch());
2003 if (key.bank_number < 127) {
2005 change_patch_change (patch, key);
2011 MidiRegionView::maybe_remove_deleted_note_from_selection (NoteBase* cne)
2013 if (_selection.empty()) {
2017 _selection.erase (cne);
2021 MidiRegionView::delete_selection()
2023 if (_selection.empty()) {
2027 start_note_diff_command (_("delete selection"));
2029 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2030 if ((*i)->selected()) {
2031 _note_diff_command->remove((*i)->note());
2041 MidiRegionView::delete_note (boost::shared_ptr<NoteType> n)
2043 start_note_diff_command (_("delete note"));
2044 _note_diff_command->remove (n);
2047 trackview.editor().verbose_cursor()->hide ();
2051 MidiRegionView::clear_selection_except (NoteBase* ev, bool signal)
2053 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2055 Selection::iterator tmp = i;
2058 (*i)->set_selected (false);
2059 (*i)->hide_velocity ();
2060 _selection.erase (i);
2068 /* this does not change the status of this regionview w.r.t the editor
2073 SelectionCleared (this); /* EMIT SIGNAL */
2078 MidiRegionView::unique_select(NoteBase* ev)
2080 clear_selection_except (ev);
2082 /* don't bother with checking to see if we should remove this
2083 regionview from the editor selection, since we're about to add
2084 another note, and thus put/keep this regionview in the editor
2088 if (!ev->selected()) {
2089 add_to_selection (ev);
2094 MidiRegionView::select_all_notes ()
2098 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2099 add_to_selection (*i);
2104 MidiRegionView::select_range (framepos_t start, framepos_t end)
2108 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2109 framepos_t t = source_beats_to_absolute_frames((*i)->note()->time());
2110 if (t >= start && t <= end) {
2111 add_to_selection (*i);
2117 MidiRegionView::invert_selection ()
2119 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2120 if ((*i)->selected()) {
2121 remove_from_selection(*i);
2123 add_to_selection (*i);
2129 MidiRegionView::select_matching_notes (uint8_t notenum, uint16_t channel_mask, bool add, bool extend)
2131 bool have_selection = !_selection.empty();
2132 uint8_t low_note = 127;
2133 uint8_t high_note = 0;
2134 MidiModel::Notes& notes (_model->notes());
2135 _optimization_iterator = _events.begin();
2137 if (extend && !have_selection) {
2141 /* scan existing selection to get note range */
2143 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2144 if ((*i)->note()->note() < low_note) {
2145 low_note = (*i)->note()->note();
2147 if ((*i)->note()->note() > high_note) {
2148 high_note = (*i)->note()->note();
2155 if (!extend && (low_note == high_note) && (high_note == notenum)) {
2156 /* only note previously selected is the one we are
2157 * reselecting. treat this as cancelling the selection.
2164 low_note = min (low_note, notenum);
2165 high_note = max (high_note, notenum);
2168 _no_sound_notes = true;
2170 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
2172 boost::shared_ptr<NoteType> note (*n);
2174 bool select = false;
2176 if (((1 << note->channel()) & channel_mask) != 0) {
2178 if ((note->note() >= low_note && note->note() <= high_note)) {
2181 } else if (note->note() == notenum) {
2187 if ((cne = find_canvas_note (note)) != 0) {
2188 // extend is false because we've taken care of it,
2189 // since it extends by time range, not pitch.
2190 note_selected (cne, add, false);
2194 add = true; // we need to add all remaining matching notes, even if the passed in value was false (for "set")
2198 _no_sound_notes = false;
2202 MidiRegionView::toggle_matching_notes (uint8_t notenum, uint16_t channel_mask)
2204 MidiModel::Notes& notes (_model->notes());
2205 _optimization_iterator = _events.begin();
2207 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
2209 boost::shared_ptr<NoteType> note (*n);
2212 if (note->note() == notenum && (((0x0001 << note->channel()) & channel_mask) != 0)) {
2213 if ((cne = find_canvas_note (note)) != 0) {
2214 if (cne->selected()) {
2215 note_deselected (cne);
2217 note_selected (cne, true, false);
2225 MidiRegionView::note_selected (NoteBase* ev, bool add, bool extend)
2228 clear_selection_except (ev);
2229 if (!_selection.empty()) {
2230 PublicEditor& editor (trackview.editor());
2231 editor.get_selection().add (this);
2237 if (!ev->selected()) {
2238 add_to_selection (ev);
2242 /* find end of latest note selected, select all between that and the start of "ev" */
2244 Evoral::MusicalTime earliest = Evoral::MaxMusicalTime;
2245 Evoral::MusicalTime latest = 0;
2247 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2248 if ((*i)->note()->end_time() > latest) {
2249 latest = (*i)->note()->end_time();
2251 if ((*i)->note()->time() < earliest) {
2252 earliest = (*i)->note()->time();
2256 if (ev->note()->end_time() > latest) {
2257 latest = ev->note()->end_time();
2260 if (ev->note()->time() < earliest) {
2261 earliest = ev->note()->time();
2264 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2266 /* find notes entirely within OR spanning the earliest..latest range */
2268 if (((*i)->note()->time() >= earliest && (*i)->note()->end_time() <= latest) ||
2269 ((*i)->note()->time() <= earliest && (*i)->note()->end_time() >= latest)) {
2270 add_to_selection (*i);
2278 MidiRegionView::note_deselected(NoteBase* ev)
2280 remove_from_selection (ev);
2284 MidiRegionView::update_drag_selection(double x0, double x1, double y0, double y1, bool extend)
2286 // TODO: Make this faster by storing the last updated selection rect, and only
2287 // adjusting things that are in the area that appears/disappeared.
2288 // We probably need a tree to be able to find events in O(log(n)) time.
2290 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2291 if ((*i)->x0() < x1 && (*i)->x1() > x0 && (*i)->y0() < y1 && (*i)->y1() > y0) {
2292 // Rectangles intersect
2293 if (!(*i)->selected()) {
2294 add_to_selection (*i);
2296 } else if ((*i)->selected() && !extend) {
2297 // Rectangles do not intersect
2298 remove_from_selection (*i);
2304 MidiRegionView::update_vertical_drag_selection (double y1, double y2, bool extend)
2310 // TODO: Make this faster by storing the last updated selection rect, and only
2311 // adjusting things that are in the area that appears/disappeared.
2312 // We probably need a tree to be able to find events in O(log(n)) time.
2314 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2315 if (((*i)->y1() >= y1 && (*i)->y1() <= y2)) {
2316 // within y- (note-) range
2317 if (!(*i)->selected()) {
2318 add_to_selection (*i);
2320 } else if ((*i)->selected() && !extend) {
2321 remove_from_selection (*i);
2327 MidiRegionView::remove_from_selection (NoteBase* ev)
2329 Selection::iterator i = _selection.find (ev);
2331 if (i != _selection.end()) {
2332 _selection.erase (i);
2335 ev->set_selected (false);
2336 ev->hide_velocity ();
2338 if (_selection.empty()) {
2339 PublicEditor& editor (trackview.editor());
2340 editor.get_selection().remove (this);
2345 MidiRegionView::add_to_selection (NoteBase* ev)
2347 bool add_mrv_selection = false;
2349 if (_selection.empty()) {
2350 add_mrv_selection = true;
2353 if (_selection.insert (ev).second) {
2354 ev->set_selected (true);
2355 start_playing_midi_note ((ev)->note());
2358 if (add_mrv_selection) {
2359 PublicEditor& editor (trackview.editor());
2360 editor.get_selection().add (this);
2365 MidiRegionView::move_selection(double dx, double dy, double cumulative_dy)
2367 typedef vector<boost::shared_ptr<NoteType> > PossibleChord;
2368 PossibleChord to_play;
2369 Evoral::MusicalTime earliest = Evoral::MaxMusicalTime;
2371 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2372 if ((*i)->note()->time() < earliest) {
2373 earliest = (*i)->note()->time();
2377 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2378 if (Evoral::musical_time_equal ((*i)->note()->time(), earliest)) {
2379 to_play.push_back ((*i)->note());
2381 (*i)->move_event(dx, dy);
2384 if (dy && !_selection.empty() && !_no_sound_notes && Config->get_sound_midi_notes()) {
2386 if (to_play.size() > 1) {
2388 PossibleChord shifted;
2390 for (PossibleChord::iterator n = to_play.begin(); n != to_play.end(); ++n) {
2391 boost::shared_ptr<NoteType> moved_note (new NoteType (**n));
2392 moved_note->set_note (moved_note->note() + cumulative_dy);
2393 shifted.push_back (moved_note);
2396 start_playing_midi_chord (shifted);
2398 } else if (!to_play.empty()) {
2400 boost::shared_ptr<NoteType> moved_note (new NoteType (*to_play.front()));
2401 moved_note->set_note (moved_note->note() + cumulative_dy);
2402 start_playing_midi_note (moved_note);
2408 MidiRegionView::note_dropped(NoteBase *, frameoffset_t dt, int8_t dnote)
2410 uint8_t lowest_note_in_selection = 127;
2411 uint8_t highest_note_in_selection = 0;
2412 uint8_t highest_note_difference = 0;
2414 // find highest and lowest notes first
2416 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2417 uint8_t pitch = (*i)->note()->note();
2418 lowest_note_in_selection = std::min(lowest_note_in_selection, pitch);
2419 highest_note_in_selection = std::max(highest_note_in_selection, pitch);
2423 cerr << "dnote: " << (int) dnote << endl;
2424 cerr << "lowest note (streamview): " << int(midi_stream_view()->lowest_note())
2425 << " highest note (streamview): " << int(midi_stream_view()->highest_note()) << endl;
2426 cerr << "lowest note (selection): " << int(lowest_note_in_selection) << " highest note(selection): "
2427 << int(highest_note_in_selection) << endl;
2428 cerr << "selection size: " << _selection.size() << endl;
2429 cerr << "Highest note in selection: " << (int) highest_note_in_selection << endl;
2432 // Make sure the note pitch does not exceed the MIDI standard range
2433 if (highest_note_in_selection + dnote > 127) {
2434 highest_note_difference = highest_note_in_selection - 127;
2437 start_note_diff_command (_("move notes"));
2439 for (Selection::iterator i = _selection.begin(); i != _selection.end() ; ++i) {
2441 framepos_t new_frames = source_beats_to_absolute_frames ((*i)->note()->time()) + dt;
2442 Evoral::MusicalTime new_time = absolute_frames_to_source_beats (new_frames);
2448 note_diff_add_change (*i, MidiModel::NoteDiffCommand::StartTime, new_time);
2450 uint8_t original_pitch = (*i)->note()->note();
2451 uint8_t new_pitch = original_pitch + dnote - highest_note_difference;
2453 // keep notes in standard midi range
2454 clamp_to_0_127(new_pitch);
2456 lowest_note_in_selection = std::min(lowest_note_in_selection, new_pitch);
2457 highest_note_in_selection = std::max(highest_note_in_selection, new_pitch);
2459 note_diff_add_change (*i, MidiModel::NoteDiffCommand::NoteNumber, new_pitch);
2464 // care about notes being moved beyond the upper/lower bounds on the canvas
2465 if (lowest_note_in_selection < midi_stream_view()->lowest_note() ||
2466 highest_note_in_selection > midi_stream_view()->highest_note()) {
2467 midi_stream_view()->set_note_range(MidiStreamView::ContentsRange);
2471 /** @param x Pixel relative to the region position.
2472 * @return Snapped frame relative to the region position.
2475 MidiRegionView::snap_pixel_to_sample(double x)
2477 PublicEditor& editor (trackview.editor());
2478 return snap_frame_to_frame (editor.pixel_to_sample (x));
2481 /** @param x Pixel relative to the region position.
2482 * @return Snapped pixel relative to the region position.
2485 MidiRegionView::snap_to_pixel(double x)
2487 return (double) trackview.editor().sample_to_pixel(snap_pixel_to_sample(x));
2491 MidiRegionView::get_position_pixels()
2493 framepos_t region_frame = get_position();
2494 return trackview.editor().sample_to_pixel(region_frame);
2498 MidiRegionView::get_end_position_pixels()
2500 framepos_t frame = get_position() + get_duration ();
2501 return trackview.editor().sample_to_pixel(frame);
2505 MidiRegionView::source_beats_to_absolute_frames(double beats) const
2507 /* the time converter will return the frame corresponding to `beats'
2508 relative to the start of the source. The start of the source
2509 is an implied position given by region->position - region->start
2511 const framepos_t source_start = _region->position() - _region->start();
2512 return source_start + _source_relative_time_converter.to (beats);
2516 MidiRegionView::absolute_frames_to_source_beats(framepos_t frames) const
2518 /* the `frames' argument needs to be converted into a frame count
2519 relative to the start of the source before being passed in to the
2522 const framepos_t source_start = _region->position() - _region->start();
2523 return _source_relative_time_converter.from (frames - source_start);
2527 MidiRegionView::region_beats_to_region_frames(double beats) const
2529 return _region_relative_time_converter.to(beats);
2533 MidiRegionView::region_frames_to_region_beats(framepos_t frames) const
2535 return _region_relative_time_converter.from(frames);
2539 MidiRegionView::begin_resizing (bool /*at_front*/)
2541 _resize_data.clear();
2543 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2544 Note *note = dynamic_cast<Note*> (*i);
2546 // only insert CanvasNotes into the map
2548 NoteResizeData *resize_data = new NoteResizeData();
2549 resize_data->note = note;
2551 // create a new SimpleRect from the note which will be the resize preview
2552 ArdourCanvas::Rectangle *resize_rect = new ArdourCanvas::Rectangle (_note_group,
2553 ArdourCanvas::Rect (note->x0(), note->y0(), note->x0(), note->y1()));
2555 // calculate the colors: get the color settings
2556 uint32_t fill_color = UINT_RGBA_CHANGE_A(
2557 ARDOUR_UI::config()->get_canvasvar_MidiNoteSelected(),
2560 // make the resize preview notes more transparent and bright
2561 fill_color = UINT_INTERPOLATE(fill_color, 0xFFFFFF40, 0.5);
2563 // calculate color based on note velocity
2564 resize_rect->set_fill_color (UINT_INTERPOLATE(
2565 NoteBase::meter_style_fill_color(note->note()->velocity(), note->selected()),
2569 resize_rect->set_outline_color (NoteBase::calculate_outline (
2570 ARDOUR_UI::config()->get_canvasvar_MidiNoteSelected()));
2572 resize_data->resize_rect = resize_rect;
2573 _resize_data.push_back(resize_data);
2578 /** Update resizing notes while user drags.
2579 * @param primary `primary' note for the drag; ie the one that is used as the reference in non-relative mode.
2580 * @param at_front which end of the note (true == note on, false == note off)
2581 * @param delta_x change in mouse position since the start of the drag
2582 * @param relative true if relative resizing is taking place, false if absolute resizing. This only makes
2583 * a difference when multiple notes are being resized; in relative mode, each note's length is changed by the
2584 * amount of the drag. In non-relative mode, all selected notes are set to have the same start or end point
2585 * as the \a primary note.
2588 MidiRegionView::update_resizing (NoteBase* primary, bool at_front, double delta_x, bool relative)
2590 bool cursor_set = false;
2592 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2593 ArdourCanvas::Rectangle* resize_rect = (*i)->resize_rect;
2594 Note* canvas_note = (*i)->note;
2599 current_x = canvas_note->x0() + delta_x;
2601 current_x = primary->x0() + delta_x;
2605 current_x = canvas_note->x1() + delta_x;
2607 current_x = primary->x1() + delta_x;
2612 resize_rect->set_x0 (snap_to_pixel(current_x));
2613 resize_rect->set_x1 (canvas_note->x1());
2615 resize_rect->set_x1 (snap_to_pixel(current_x));
2616 resize_rect->set_x0 (canvas_note->x0());
2622 beats = snap_pixel_to_sample (current_x);
2623 beats = region_frames_to_region_beats (beats);
2628 if (beats < canvas_note->note()->end_time()) {
2629 len = canvas_note->note()->time() - beats;
2630 len += canvas_note->note()->length();
2635 if (beats >= canvas_note->note()->time()) {
2636 len = beats - canvas_note->note()->time();
2643 snprintf (buf, sizeof (buf), "%.3g beats", len);
2644 show_verbose_cursor (buf, 0, 0);
2653 /** Finish resizing notes when the user releases the mouse button.
2654 * Parameters the same as for \a update_resizing().
2657 MidiRegionView::commit_resizing (NoteBase* primary, bool at_front, double delta_x, bool relative)
2659 start_note_diff_command (_("resize notes"));
2661 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2662 Note* canvas_note = (*i)->note;
2663 ArdourCanvas::Rectangle* resize_rect = (*i)->resize_rect;
2665 /* Get the new x position for this resize, which is in pixels relative
2666 * to the region position.
2673 current_x = canvas_note->x0() + delta_x;
2675 current_x = primary->x0() + delta_x;
2679 current_x = canvas_note->x1() + delta_x;
2681 current_x = primary->x1() + delta_x;
2685 /* Convert that to a frame within the source */
2686 current_x = snap_pixel_to_sample (current_x) + _region->start ();
2688 /* and then to beats */
2689 current_x = region_frames_to_region_beats (current_x);
2691 if (at_front && current_x < canvas_note->note()->end_time()) {
2692 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::StartTime, current_x);
2694 double len = canvas_note->note()->time() - current_x;
2695 len += canvas_note->note()->length();
2698 /* XXX convert to beats */
2699 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
2704 double len = current_x - canvas_note->note()->time();
2707 /* XXX convert to beats */
2708 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
2716 _resize_data.clear();
2721 MidiRegionView::abort_resizing ()
2723 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2724 delete (*i)->resize_rect;
2728 _resize_data.clear ();
2732 MidiRegionView::change_note_velocity(NoteBase* event, int8_t velocity, bool relative)
2734 uint8_t new_velocity;
2737 new_velocity = event->note()->velocity() + velocity;
2738 clamp_to_0_127(new_velocity);
2740 new_velocity = velocity;
2743 event->set_selected (event->selected()); // change color
2745 note_diff_add_change (event, MidiModel::NoteDiffCommand::Velocity, new_velocity);
2749 MidiRegionView::change_note_note (NoteBase* event, int8_t note, bool relative)
2754 new_note = event->note()->note() + note;
2759 clamp_to_0_127 (new_note);
2760 note_diff_add_change (event, MidiModel::NoteDiffCommand::NoteNumber, new_note);
2764 MidiRegionView::trim_note (NoteBase* event, Evoral::MusicalTime front_delta, Evoral::MusicalTime end_delta)
2766 bool change_start = false;
2767 bool change_length = false;
2768 Evoral::MusicalTime new_start = 0;
2769 Evoral::MusicalTime new_length = 0;
2771 /* NOTE: the semantics of the two delta arguments are slightly subtle:
2773 front_delta: if positive - move the start of the note later in time (shortening it)
2774 if negative - move the start of the note earlier in time (lengthening it)
2776 end_delta: if positive - move the end of the note later in time (lengthening it)
2777 if negative - move the end of the note earlier in time (shortening it)
2781 if (front_delta < 0) {
2783 if (event->note()->time() < -front_delta) {
2786 new_start = event->note()->time() + front_delta; // moves earlier
2789 /* start moved toward zero, so move the end point out to where it used to be.
2790 Note that front_delta is negative, so this increases the length.
2793 new_length = event->note()->length() - front_delta;
2794 change_start = true;
2795 change_length = true;
2799 Evoral::MusicalTime new_pos = event->note()->time() + front_delta;
2801 if (new_pos < event->note()->end_time()) {
2802 new_start = event->note()->time() + front_delta;
2803 /* start moved toward the end, so move the end point back to where it used to be */
2804 new_length = event->note()->length() - front_delta;
2805 change_start = true;
2806 change_length = true;
2813 bool can_change = true;
2814 if (end_delta < 0) {
2815 if (event->note()->length() < -end_delta) {
2821 new_length = event->note()->length() + end_delta;
2822 change_length = true;
2827 note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_start);
2830 if (change_length) {
2831 note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, new_length);
2836 MidiRegionView::change_note_channel (NoteBase* event, int8_t chn, bool relative)
2838 uint8_t new_channel;
2842 if (event->note()->channel() < -chn) {
2845 new_channel = event->note()->channel() + chn;
2848 new_channel = event->note()->channel() + chn;
2851 new_channel = (uint8_t) chn;
2854 note_diff_add_change (event, MidiModel::NoteDiffCommand::Channel, new_channel);
2858 MidiRegionView::change_note_time (NoteBase* event, Evoral::MusicalTime delta, bool relative)
2860 Evoral::MusicalTime new_time;
2864 if (event->note()->time() < -delta) {
2867 new_time = event->note()->time() + delta;
2870 new_time = event->note()->time() + delta;
2876 note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_time);
2880 MidiRegionView::change_note_length (NoteBase* event, Evoral::MusicalTime t)
2882 note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, t);
2886 MidiRegionView::change_velocities (bool up, bool fine, bool allow_smush, bool all_together)
2891 if (_selection.empty()) {
2906 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2907 if ((*i)->note()->velocity() < -delta || (*i)->note()->velocity() + delta > 127) {
2913 start_note_diff_command (_("change velocities"));
2915 for (Selection::iterator i = _selection.begin(); i != _selection.end();) {
2916 Selection::iterator next = i;
2920 if (i == _selection.begin()) {
2921 change_note_velocity (*i, delta, true);
2922 value = (*i)->note()->velocity() + delta;
2924 change_note_velocity (*i, value, false);
2928 change_note_velocity (*i, delta, true);
2937 if (!_selection.empty()) {
2939 snprintf (buf, sizeof (buf), "Vel %d",
2940 (int) (*_selection.begin())->note()->velocity());
2941 show_verbose_cursor (buf, 10, 10);
2947 MidiRegionView::transpose (bool up, bool fine, bool allow_smush)
2949 if (_selection.empty()) {
2966 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2968 if ((int8_t) (*i)->note()->note() + delta <= 0) {
2972 if ((int8_t) (*i)->note()->note() + delta > 127) {
2979 start_note_diff_command (_("transpose"));
2981 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2982 Selection::iterator next = i;
2984 change_note_note (*i, delta, true);
2992 MidiRegionView::change_note_lengths (bool fine, bool shorter, Evoral::MusicalTime delta, bool start, bool end)
2998 /* grab the current grid distance */
3000 delta = trackview.editor().get_grid_type_as_beats (success, _region->position());
3002 /* XXX cannot get grid type as beats ... should always be possible ... FIX ME */
3003 error << string_compose (_("programming error: %1"), "Grid type not available as beats - TO BE FIXED") << endmsg;
3013 start_note_diff_command (_("change note lengths"));
3015 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3016 Selection::iterator next = i;
3019 /* note the negation of the delta for start */
3021 trim_note (*i, (start ? -delta : 0), (end ? delta : 0));
3030 MidiRegionView::nudge_notes (bool forward)
3032 if (_selection.empty()) {
3036 /* pick a note as the point along the timeline to get the nudge distance.
3037 its not necessarily the earliest note, so we may want to pull the notes out
3038 into a vector and sort before using the first one.
3041 framepos_t ref_point = source_beats_to_absolute_frames ((*(_selection.begin()))->note()->time());
3043 framecnt_t distance;
3045 if (trackview.editor().snap_mode() == Editing::SnapOff) {
3047 /* grid is off - use nudge distance */
3049 distance = trackview.editor().get_nudge_distance (ref_point, unused);
3055 framepos_t next_pos = ref_point;
3058 if (max_framepos - 1 < next_pos) {
3062 if (next_pos == 0) {
3068 trackview.editor().snap_to (next_pos, (forward ? 1 : -1), false);
3069 distance = ref_point - next_pos;
3072 if (distance == 0) {
3076 Evoral::MusicalTime delta = region_frames_to_region_beats (fabs ((double)distance));
3082 start_note_diff_command (_("nudge"));
3084 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3085 Selection::iterator next = i;
3087 change_note_time (*i, delta, true);
3095 MidiRegionView::change_channel(uint8_t channel)
3097 start_note_diff_command(_("change channel"));
3098 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3099 note_diff_add_change (*i, MidiModel::NoteDiffCommand::Channel, channel);
3107 MidiRegionView::note_entered(NoteBase* ev)
3109 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3111 pre_enter_cursor = editor->get_canvas_cursor ();
3113 if (_mouse_state == SelectTouchDragging) {
3114 note_selected (ev, true);
3117 show_verbose_cursor (ev->note ());
3121 MidiRegionView::note_left (NoteBase*)
3123 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3125 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3126 (*i)->hide_velocity ();
3129 editor->verbose_cursor()->hide ();
3131 if (pre_enter_cursor) {
3132 editor->set_canvas_cursor (pre_enter_cursor);
3133 pre_enter_cursor = 0;
3138 MidiRegionView::patch_entered (PatchChange* p)
3141 /* XXX should get patch name if we can */
3142 s << _("Bank ") << (p->patch()->bank() + MIDI_BP_ZERO) << '\n'
3143 << _("Program ") << ((int) p->patch()->program()) + MIDI_BP_ZERO << '\n'
3144 << _("Channel ") << ((int) p->patch()->channel() + 1);
3145 show_verbose_cursor (s.str(), 10, 20);
3146 p->item().grab_focus();
3150 MidiRegionView::patch_left (PatchChange *)
3152 trackview.editor().verbose_cursor()->hide ();
3153 /* focus will transfer back via the enter-notify event sent to this
3159 MidiRegionView::sysex_entered (SysEx* p)
3163 // need a way to extract text from p->_flag->_text
3165 // show_verbose_cursor (s.str(), 10, 20);
3166 p->item().grab_focus();
3170 MidiRegionView::sysex_left (SysEx *)
3172 trackview.editor().verbose_cursor()->hide ();
3173 /* focus will transfer back via the enter-notify event sent to this
3179 MidiRegionView::note_mouse_position (float x_fraction, float /*y_fraction*/, bool can_set_cursor)
3181 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3182 Editing::MouseMode mm = editor->current_mouse_mode();
3183 bool trimmable = (mm == MouseObject || mm == MouseTimeFX || mm == MouseDraw);
3185 if (trimmable && x_fraction > 0.0 && x_fraction < 0.2) {
3186 editor->set_canvas_cursor (editor->cursors()->left_side_trim);
3187 } else if (trimmable && x_fraction >= 0.8 && x_fraction < 1.0) {
3188 editor->set_canvas_cursor (editor->cursors()->right_side_trim);
3190 if (pre_enter_cursor && can_set_cursor) {
3191 editor->set_canvas_cursor (pre_enter_cursor);
3197 MidiRegionView::set_frame_color()
3201 TimeAxisViewItem::set_frame_color ();
3208 f = ARDOUR_UI::config()->get_canvasvar_SelectedFrameBase();
3209 } else if (high_enough_for_name) {
3210 f= ARDOUR_UI::config()->get_canvasvar_MidiFrameBase();
3215 if (!rect_visible) {
3216 f = UINT_RGBA_CHANGE_A (f, 0);
3219 frame->set_fill_color (f);
3223 MidiRegionView::midi_channel_mode_changed ()
3225 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3226 uint16_t mask = mtv->midi_track()->get_playback_channel_mask();
3227 ChannelMode mode = mtv->midi_track()->get_playback_channel_mode ();
3229 if (mode == ForceChannel) {
3230 mask = 0xFFFF; // Show all notes as active (below)
3233 // Update notes for selection
3234 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3235 (*i)->on_channel_selection_change (mask);
3238 _patch_changes.clear ();
3239 display_patch_changes ();
3243 MidiRegionView::instrument_settings_changed ()
3249 MidiRegionView::cut_copy_clear (Editing::CutCopyOp op)
3251 if (_selection.empty()) {
3255 PublicEditor& editor (trackview.editor());
3259 /* XXX what to do ? */
3263 editor.get_cut_buffer().add (selection_as_cut_buffer());
3271 start_note_diff_command();
3273 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3280 note_diff_remove_note (*i);
3290 MidiRegionView::selection_as_cut_buffer () const
3294 for (Selection::const_iterator i = _selection.begin(); i != _selection.end(); ++i) {
3295 NoteType* n = (*i)->note().get();
3296 notes.insert (boost::shared_ptr<NoteType> (new NoteType (*n)));
3299 MidiCutBuffer* cb = new MidiCutBuffer (trackview.session());
3305 /** This method handles undo */
3307 MidiRegionView::paste (framepos_t pos, float times, const MidiCutBuffer& mcb)
3313 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("MIDI paste @ %1 times %2\n", pos, times));
3315 trackview.session()->begin_reversible_command (_("paste"));
3317 start_note_diff_command (_("paste"));
3319 Evoral::MusicalTime beat_delta;
3320 Evoral::MusicalTime paste_pos_beats;
3321 Evoral::MusicalTime duration;
3322 Evoral::MusicalTime end_point = 0;
3324 duration = (*mcb.notes().rbegin())->end_time() - (*mcb.notes().begin())->time();
3325 paste_pos_beats = absolute_frames_to_source_beats (pos);
3326 beat_delta = (*mcb.notes().begin())->time() - paste_pos_beats;
3327 paste_pos_beats = 0;
3329 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste data spans from %1 to %2 (%3) ; paste pos beats = %4 (based on %5 - %6 ; beat delta = %7\n",
3330 (*mcb.notes().begin())->time(),
3331 (*mcb.notes().rbegin())->end_time(),
3332 duration, pos, _region->position(),
3333 paste_pos_beats, beat_delta));
3337 for (int n = 0; n < (int) times; ++n) {
3339 for (Notes::const_iterator i = mcb.notes().begin(); i != mcb.notes().end(); ++i) {
3341 boost::shared_ptr<NoteType> copied_note (new NoteType (*((*i).get())));
3342 copied_note->set_time (paste_pos_beats + copied_note->time() - beat_delta);
3344 /* make all newly added notes selected */
3346 note_diff_add_note (copied_note, true);
3347 end_point = copied_note->end_time();
3350 paste_pos_beats += duration;
3353 /* if we pasted past the current end of the region, extend the region */
3355 framepos_t end_frame = source_beats_to_absolute_frames (end_point);
3356 framepos_t region_end = _region->position() + _region->length() - 1;
3358 if (end_frame > region_end) {
3360 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste extended region from %1 to %2\n", region_end, end_frame));
3362 _region->clear_changes ();
3363 _region->set_length (end_frame - _region->position());
3364 trackview.session()->add_command (new StatefulDiffCommand (_region));
3369 trackview.session()->commit_reversible_command ();
3372 struct EventNoteTimeEarlyFirstComparator {
3373 bool operator() (NoteBase* a, NoteBase* b) {
3374 return a->note()->time() < b->note()->time();
3379 MidiRegionView::time_sort_events ()
3381 if (!_sort_needed) {
3385 EventNoteTimeEarlyFirstComparator cmp;
3388 _sort_needed = false;
3392 MidiRegionView::goto_next_note (bool add_to_selection)
3394 bool use_next = false;
3396 if (_events.back()->selected()) {
3400 time_sort_events ();
3402 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3403 uint16_t const channel_mask = mtv->midi_track()->get_playback_channel_mask();
3405 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3406 if ((*i)->selected()) {
3409 } else if (use_next) {
3410 if (channel_mask & (1 << (*i)->note()->channel())) {
3411 if (!add_to_selection) {
3414 note_selected (*i, true, false);
3421 /* use the first one */
3423 if (!_events.empty() && (channel_mask & (1 << _events.front()->note()->channel ()))) {
3424 unique_select (_events.front());
3429 MidiRegionView::goto_previous_note (bool add_to_selection)
3431 bool use_next = false;
3433 if (_events.front()->selected()) {
3437 time_sort_events ();
3439 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3440 uint16_t const channel_mask = mtv->midi_track()->get_playback_channel_mask ();
3442 for (Events::reverse_iterator i = _events.rbegin(); i != _events.rend(); ++i) {
3443 if ((*i)->selected()) {
3446 } else if (use_next) {
3447 if (channel_mask & (1 << (*i)->note()->channel())) {
3448 if (!add_to_selection) {
3451 note_selected (*i, true, false);
3458 /* use the last one */
3460 if (!_events.empty() && (channel_mask & (1 << (*_events.rbegin())->note()->channel ()))) {
3461 unique_select (*(_events.rbegin()));
3466 MidiRegionView::selection_as_notelist (Notes& selected, bool allow_all_if_none_selected)
3468 bool had_selected = false;
3470 time_sort_events ();
3472 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3473 if ((*i)->selected()) {
3474 selected.insert ((*i)->note());
3475 had_selected = true;
3479 if (allow_all_if_none_selected && !had_selected) {
3480 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3481 selected.insert ((*i)->note());
3487 MidiRegionView::update_ghost_note (double x, double y)
3489 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3494 _note_group->canvas_to_item (x, y);
3496 PublicEditor& editor = trackview.editor ();
3498 framepos_t const unsnapped_frame = editor.pixel_to_sample (x);
3499 framecnt_t grid_frames;
3500 framepos_t const f = snap_frame_to_grid_underneath (unsnapped_frame, grid_frames);
3502 /* use region_frames... because we are converting a delta within the region
3506 double length = editor.get_grid_type_as_beats (success, unsnapped_frame);
3512 /* note that this sets the time of the ghost note in beats relative to
3513 the start of the source; that is how all note times are stored.
3515 _ghost_note->note()->set_time (absolute_frames_to_source_beats (f + _region->position ()));
3516 _ghost_note->note()->set_length (length);
3517 _ghost_note->note()->set_note (midi_stream_view()->y_to_note (y));
3518 _ghost_note->note()->set_channel (mtv->get_channel_for_add ());
3520 /* the ghost note does not appear in ghost regions, so pass false in here */
3521 update_note (_ghost_note, false);
3523 show_verbose_cursor (_ghost_note->note ());
3527 MidiRegionView::create_ghost_note (double x, double y)
3529 remove_ghost_note ();
3531 boost::shared_ptr<NoteType> g (new NoteType);
3532 _ghost_note = new Note (*this, _note_group, g);
3533 _ghost_note->set_ignore_events (true);
3534 _ghost_note->set_outline_color (0x000000aa);
3535 update_ghost_note (x, y);
3536 _ghost_note->show ();
3541 show_verbose_cursor (_ghost_note->note ());
3545 MidiRegionView::remove_ghost_note ()
3552 MidiRegionView::snap_changed ()
3558 create_ghost_note (_last_ghost_x, _last_ghost_y);
3562 MidiRegionView::drop_down_keys ()
3564 _mouse_state = None;
3568 MidiRegionView::maybe_select_by_position (GdkEventButton* ev, double /*x*/, double y)
3570 double note = midi_stream_view()->y_to_note(y);
3572 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3574 uint16_t chn_mask = mtv->midi_track()->get_playback_channel_mask();
3576 if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
3577 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchGreaterThanOrEqual, (uint8_t) floor (note), chn_mask);
3578 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
3579 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchLessThanOrEqual, (uint8_t) floor (note), chn_mask);
3584 bool add_mrv_selection = false;
3586 if (_selection.empty()) {
3587 add_mrv_selection = true;
3590 for (Events::iterator i = e.begin(); i != e.end(); ++i) {
3591 if (_selection.insert (*i).second) {
3592 (*i)->set_selected (true);
3596 if (add_mrv_selection) {
3597 PublicEditor& editor (trackview.editor());
3598 editor.get_selection().add (this);
3603 MidiRegionView::color_handler ()
3605 RegionView::color_handler ();
3607 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3608 (*i)->set_selected ((*i)->selected()); // will change color
3611 /* XXX probably more to do here */
3615 MidiRegionView::enable_display (bool yn)
3617 RegionView::enable_display (yn);
3624 MidiRegionView::show_step_edit_cursor (Evoral::MusicalTime pos)
3626 if (_step_edit_cursor == 0) {
3627 ArdourCanvas::Group* const group = (ArdourCanvas::Group*)get_canvas_group();
3629 _step_edit_cursor = new ArdourCanvas::Rectangle (group);
3630 _step_edit_cursor->set_y0 (0);
3631 _step_edit_cursor->set_y1 (midi_stream_view()->contents_height());
3632 _step_edit_cursor->set_fill_color (RGBA_TO_UINT (45,0,0,90));
3633 _step_edit_cursor->set_outline_color (RGBA_TO_UINT (85,0,0,90));
3636 move_step_edit_cursor (pos);
3637 _step_edit_cursor->show ();
3641 MidiRegionView::move_step_edit_cursor (Evoral::MusicalTime pos)
3643 _step_edit_cursor_position = pos;
3645 if (_step_edit_cursor) {
3646 double pixel = trackview.editor().sample_to_pixel (region_beats_to_region_frames (pos));
3647 _step_edit_cursor->set_x0 (pixel);
3648 set_step_edit_cursor_width (_step_edit_cursor_width);
3653 MidiRegionView::hide_step_edit_cursor ()
3655 if (_step_edit_cursor) {
3656 _step_edit_cursor->hide ();
3661 MidiRegionView::set_step_edit_cursor_width (Evoral::MusicalTime beats)
3663 _step_edit_cursor_width = beats;
3665 if (_step_edit_cursor) {
3666 _step_edit_cursor->set_x1 (_step_edit_cursor->x0() + trackview.editor().sample_to_pixel (region_beats_to_region_frames (beats)));
3670 /** Called when a diskstream on our track has received some data. Update the view, if applicable.
3671 * @param w Source that the data will end up in.
3674 MidiRegionView::data_recorded (boost::weak_ptr<MidiSource> w)
3676 if (!_active_notes) {
3677 /* we aren't actively being recorded to */
3681 boost::shared_ptr<MidiSource> src = w.lock ();
3682 if (!src || src != midi_region()->midi_source()) {
3683 /* recorded data was not destined for our source */
3687 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (&trackview);
3689 boost::shared_ptr<MidiBuffer> buf = mtv->midi_track()->get_gui_feed_buffer ();
3691 BeatsFramesConverter converter (trackview.session()->tempo_map(), mtv->midi_track()->get_capture_start_frame (0));
3693 framepos_t back = max_framepos;
3695 for (MidiBuffer::iterator i = buf->begin(); i != buf->end(); ++i) {
3696 Evoral::MIDIEvent<MidiBuffer::TimeType> const ev (*i, false);
3698 if (ev.is_channel_event()) {
3699 if (get_channel_mode() == FilterChannels) {
3700 if (((uint16_t(1) << ev.channel()) & get_selected_channels()) == 0) {
3706 /* ev.time() is in session frames, so (ev.time() - converter.origin_b()) is
3707 frames from the start of the source, and so time_beats is in terms of the
3711 Evoral::MusicalTime const time_beats = converter.from (ev.time () - converter.origin_b ());
3713 if (ev.type() == MIDI_CMD_NOTE_ON) {
3714 boost::shared_ptr<NoteType> note (
3715 new NoteType (ev.channel(), time_beats, 0, ev.note(), ev.velocity()));
3717 add_note (note, true);
3719 /* fix up our note range */
3720 if (ev.note() < _current_range_min) {
3721 midi_stream_view()->apply_note_range (ev.note(), _current_range_max, true);
3722 } else if (ev.note() > _current_range_max) {
3723 midi_stream_view()->apply_note_range (_current_range_min, ev.note(), true);
3726 } else if (ev.type() == MIDI_CMD_NOTE_OFF) {
3727 resolve_note (ev.note (), time_beats);
3733 midi_stream_view()->check_record_layers (region(), back);
3737 MidiRegionView::trim_front_starting ()
3739 /* Reparent the note group to the region view's parent, so that it doesn't change
3740 when the region view is trimmed.
3742 _temporary_note_group = new ArdourCanvas::Group (group->parent ());
3743 _temporary_note_group->move (group->position ());
3744 _note_group->reparent (_temporary_note_group);
3748 MidiRegionView::trim_front_ending ()
3750 _note_group->reparent (group);
3751 delete _temporary_note_group;
3752 _temporary_note_group = 0;
3754 if (_region->start() < 0) {
3755 /* Trim drag made start time -ve; fix this */
3756 midi_region()->fix_negative_start ();
3761 MidiRegionView::edit_patch_change (PatchChange* pc)
3763 PatchChangeDialog d (&_source_relative_time_converter, trackview.session(), *pc->patch (), instrument_info(), Gtk::Stock::APPLY, true);
3765 int response = d.run();
3768 case Gtk::RESPONSE_ACCEPT:
3770 case Gtk::RESPONSE_REJECT:
3771 delete_patch_change (pc);
3777 change_patch_change (pc->patch(), d.patch ());
3781 MidiRegionView::delete_sysex (SysEx* /*sysex*/)
3784 // sysyex object doesn't have a pointer to a sysex event
3785 // MidiModel::SysExDiffCommand* c = _model->new_sysex_diff_command (_("delete sysex"));
3786 // c->remove (sysex->sysex());
3787 // _model->apply_command (*trackview.session(), c);
3789 //_sys_exes.clear ();
3790 // display_sysexes();
3794 MidiRegionView::show_verbose_cursor (boost::shared_ptr<NoteType> n) const
3796 using namespace MIDI::Name;
3800 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3802 boost::shared_ptr<MasterDeviceNames> device_names(mtv->get_device_names());
3804 MIDI::Name::PatchPrimaryKey patch_key;
3805 get_patch_key_at(n->time(), n->channel(), patch_key);
3806 name = device_names->note_name(mtv->gui_property(X_("midnam-custom-device-mode")),
3808 patch_key.bank_number,
3809 patch_key.program_number,
3815 snprintf (buf, sizeof (buf), "%d %s\nCh %d Vel %d",
3817 name.empty() ? Evoral::midi_note_name (n->note()).c_str() : name.c_str(),
3818 (int) n->channel() + 1,
3819 (int) n->velocity());
3821 show_verbose_cursor(buf, 10, 20);
3825 MidiRegionView::show_verbose_cursor (string const & text, double xoffset, double yoffset) const
3829 trackview.editor().verbose_cursor()->set_text (text);
3830 trackview.editor().get_pointer_position (wx, wy);
3835 /* Flip the cursor above the mouse pointer if it would overlap the bottom of the canvas */
3837 boost::optional<ArdourCanvas::Rect> bbo = trackview.editor().verbose_cursor()->item().bounding_box();
3841 ArdourCanvas::Rect bb = bbo.get();
3843 if ((wy + bb.y1 - bb.y0) > trackview.editor().visible_canvas_height()) {
3844 wy -= (bb.y1 - bb.y0) + 2 * yoffset;
3847 trackview.editor().verbose_cursor()->set_position (wx, wy);
3848 trackview.editor().verbose_cursor()->show ();
3851 /** @param p A session framepos.
3852 * @param grid_frames Filled in with the number of frames that a grid interval is at p.
3853 * @return p snapped to the grid subdivision underneath it.
3856 MidiRegionView::snap_frame_to_grid_underneath (framepos_t p, framecnt_t& grid_frames) const
3858 PublicEditor& editor = trackview.editor ();
3861 Evoral::MusicalTime grid_beats = editor.get_grid_type_as_beats (success, p);
3867 grid_frames = region_beats_to_region_frames (grid_beats);
3869 /* Hack so that we always snap to the note that we are over, instead of snapping
3870 to the next one if we're more than halfway through the one we're over.
3872 if (editor.snap_mode() == SnapNormal && p >= grid_frames / 2) {
3873 p -= grid_frames / 2;
3876 return snap_frame_to_frame (p);
3879 /** Called when the selection has been cleared in any MidiRegionView.
3880 * @param rv MidiRegionView that the selection was cleared in.
3883 MidiRegionView::selection_cleared (MidiRegionView* rv)
3889 /* Clear our selection in sympathy; but don't signal the fact */
3890 clear_selection (false);
3894 MidiRegionView::note_button_release ()
3896 delete _note_player;
3901 MidiRegionView::get_channel_mode () const
3903 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (&trackview);
3904 return rtav->midi_track()->get_playback_channel_mode();
3908 MidiRegionView::get_selected_channels () const
3910 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (&trackview);
3911 return rtav->midi_track()->get_playback_channel_mask();