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"
47 #include "canvas/text.h"
49 #include "automation_region_view.h"
50 #include "automation_time_axis.h"
53 #include "editor_drag.h"
54 #include "ghostregion.h"
55 #include "gui_thread.h"
57 #include "midi_channel_dialog.h"
58 #include "midi_cut_buffer.h"
59 #include "midi_list_editor.h"
60 #include "midi_region_view.h"
61 #include "midi_streamview.h"
62 #include "midi_time_axis.h"
63 #include "midi_util.h"
64 #include "midi_velocity_dialog.h"
65 #include "mouse_cursors.h"
66 #include "note_player.h"
67 #include "public_editor.h"
68 #include "route_time_axis.h"
69 #include "rgb_macros.h"
70 #include "selection.h"
71 #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::Container *parent, RouteTimeAxisView &tv,
92 boost::shared_ptr<MidiRegion> r, double spu, uint32_t 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::Container (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)
115 , pre_note_enter_cursor (0)
118 CANVAS_DEBUG_NAME (_note_group, string_compose ("note group for %1", get_item_name()));
119 _note_group->raise_to_top();
120 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
122 Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&MidiRegionView::parameter_changed, this, _1), gui_context());
123 connect_to_diskstream ();
125 SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), boost::bind (&MidiRegionView::selection_cleared, this, _1), gui_context ());
128 MidiRegionView::MidiRegionView (ArdourCanvas::Container *parent, RouteTimeAxisView &tv,
129 boost::shared_ptr<MidiRegion> r, double spu, uint32_t basic_color,
130 TimeAxisViewItem::Visibility visibility)
131 : RegionView (parent, tv, r, spu, basic_color, false, visibility)
132 , _current_range_min(0)
133 , _current_range_max(0)
135 , _note_group (new ArdourCanvas::Container (parent))
136 , _note_diff_command (0)
138 , _step_edit_cursor (0)
139 , _step_edit_cursor_width (1.0)
140 , _step_edit_cursor_position (0.0)
141 , _channel_selection_scoped_note (0)
142 , _temporary_note_group (0)
145 , _sort_needed (true)
146 , _optimization_iterator (_events.end())
148 , _no_sound_notes (false)
151 , pre_enter_cursor (0)
152 , pre_press_cursor (0)
153 , pre_note_enter_cursor (0)
156 CANVAS_DEBUG_NAME (_note_group, string_compose ("note group for %1", get_item_name()));
157 _note_group->raise_to_top();
159 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
161 connect_to_diskstream ();
163 SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), boost::bind (&MidiRegionView::selection_cleared, this, _1), gui_context ());
167 MidiRegionView::parameter_changed (std::string const & p)
169 if (p == "display-first-midi-bank-as-zero") {
170 if (_enable_display) {
176 MidiRegionView::MidiRegionView (const MidiRegionView& other)
177 : sigc::trackable(other)
179 , _current_range_min(0)
180 , _current_range_max(0)
182 , _note_group (new ArdourCanvas::Container (get_canvas_group()))
183 , _note_diff_command (0)
185 , _step_edit_cursor (0)
186 , _step_edit_cursor_width (1.0)
187 , _step_edit_cursor_position (0.0)
188 , _channel_selection_scoped_note (0)
189 , _temporary_note_group (0)
192 , _sort_needed (true)
193 , _optimization_iterator (_events.end())
195 , _no_sound_notes (false)
198 , pre_enter_cursor (0)
199 , pre_press_cursor (0)
200 , pre_note_enter_cursor (0)
206 MidiRegionView::MidiRegionView (const MidiRegionView& other, boost::shared_ptr<MidiRegion> region)
207 : RegionView (other, boost::shared_ptr<Region> (region))
208 , _current_range_min(0)
209 , _current_range_max(0)
211 , _note_group (new ArdourCanvas::Container (get_canvas_group()))
212 , _note_diff_command (0)
214 , _step_edit_cursor (0)
215 , _step_edit_cursor_width (1.0)
216 , _step_edit_cursor_position (0.0)
217 , _channel_selection_scoped_note (0)
218 , _temporary_note_group (0)
221 , _sort_needed (true)
222 , _optimization_iterator (_events.end())
224 , _no_sound_notes (false)
227 , pre_enter_cursor (0)
228 , pre_press_cursor (0)
229 , pre_note_enter_cursor (0)
236 MidiRegionView::init (bool wfd)
238 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
240 NoteBase::NoteBaseDeleted.connect (note_delete_connection, MISSING_INVALIDATOR,
241 boost::bind (&MidiRegionView::maybe_remove_deleted_note_from_selection, this, _1),
245 midi_region()->midi_source(0)->load_model();
248 _model = midi_region()->midi_source(0)->model();
249 _enable_display = false;
251 RegionView::init (false);
253 set_height (trackview.current_height());
256 region_sync_changed ();
257 region_resized (ARDOUR::bounds_change);
262 _enable_display = true;
265 display_model (_model);
269 reset_width_dependent_items (_pixel_width);
271 group->raise_to_top();
273 midi_view()->midi_track()->PlaybackChannelModeChanged.connect (_channel_mode_changed_connection, invalidator (*this),
274 boost::bind (&MidiRegionView::midi_channel_mode_changed, this),
277 instrument_info().Changed.connect (_instrument_changed_connection, invalidator (*this),
278 boost::bind (&MidiRegionView::instrument_settings_changed, this), gui_context());
280 trackview.editor().SnapChanged.connect(snap_changed_connection, invalidator(*this),
281 boost::bind (&MidiRegionView::snap_changed, this),
284 Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&MidiRegionView::parameter_changed, this, _1), gui_context());
285 connect_to_diskstream ();
287 SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), boost::bind (&MidiRegionView::selection_cleared, this, _1), gui_context ());
291 MidiRegionView::instrument_info () const
293 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
294 return route_ui->route()->instrument_info();
297 const boost::shared_ptr<ARDOUR::MidiRegion>
298 MidiRegionView::midi_region() const
300 return boost::dynamic_pointer_cast<ARDOUR::MidiRegion>(_region);
304 MidiRegionView::connect_to_diskstream ()
306 midi_view()->midi_track()->DataRecorded.connect(
307 *this, invalidator(*this),
308 boost::bind (&MidiRegionView::data_recorded, this, _1),
313 MidiRegionView::canvas_group_event(GdkEvent* ev)
318 case GDK_ENTER_NOTIFY:
319 case GDK_LEAVE_NOTIFY:
320 _last_event_x = ev->crossing.x;
321 _last_event_y = ev->crossing.y;
323 case GDK_MOTION_NOTIFY:
324 _last_event_x = ev->motion.x;
325 _last_event_y = ev->motion.y;
331 if (ev->type == GDK_2BUTTON_PRESS) {
332 // cannot use double-click to exit internal mode if single-click is being used
333 MouseMode m = trackview.editor().current_mouse_mode();
335 if ((m != MouseObject || !Keyboard::modifier_state_contains (ev->button.state, Keyboard::insert_note_modifier())) && (m != MouseDraw)) {
336 return trackview.editor().toggle_internal_editing_from_double_click (ev);
340 if ((!trackview.editor().internal_editing() && trackview.editor().current_mouse_mode() != MouseGain) ||
341 (trackview.editor().current_mouse_mode() == MouseTimeFX)) {
342 // handle non-internal-edit/non-draw modes elsewhere
343 return RegionView::canvas_group_event (ev);
348 if (scroll (&ev->scroll)) {
354 return key_press (&ev->key);
356 case GDK_KEY_RELEASE:
357 return key_release (&ev->key);
359 case GDK_BUTTON_PRESS:
360 return button_press (&ev->button);
362 case GDK_BUTTON_RELEASE:
363 r = button_release (&ev->button);
368 case GDK_ENTER_NOTIFY:
369 // set entered_regionview (among other things)
370 trackview.editor().canvas_region_view_event (ev, group, this);
371 return enter_notify (&ev->crossing);
373 case GDK_LEAVE_NOTIFY:
374 // reset entered_regionview (among other things)
375 trackview.editor().canvas_region_view_event (ev, group, this);
376 return leave_notify (&ev->crossing);
378 case GDK_MOTION_NOTIFY:
379 return motion (&ev->motion);
385 return trackview.editor().canvas_region_view_event (ev, group, this);
389 MidiRegionView::enter_notify (GdkEventCrossing* ev)
391 trackview.editor().MouseModeChanged.connect (
392 _mouse_mode_connection, invalidator (*this), boost::bind (&MidiRegionView::mouse_mode_changed, this), gui_context ()
395 if (trackview.editor().current_mouse_mode() == MouseDraw && _mouse_state != AddDragging) {
396 create_ghost_note (ev->x, ev->y);
399 if (!trackview.editor().internal_editing()) {
400 Keyboard::magic_widget_drop_focus();
402 Keyboard::magic_widget_grab_focus();
406 // if current operation is non-operational in a midi region, change the cursor to so indicate
407 if (trackview.editor().current_mouse_mode() == MouseGain) {
408 Editor* editor = dynamic_cast<Editor *> (&trackview.editor());
409 pre_enter_cursor = editor->get_canvas_cursor();
410 editor->set_canvas_cursor(editor->cursors()->timebar);
417 MidiRegionView::leave_notify (GdkEventCrossing*)
419 _mouse_mode_connection.disconnect ();
421 trackview.editor().verbose_cursor()->hide ();
422 remove_ghost_note ();
424 if (trackview.editor().internal_editing()) {
425 Keyboard::magic_widget_drop_focus();
428 if (pre_enter_cursor) {
429 Editor* editor = dynamic_cast<Editor *> (&trackview.editor());
430 editor->set_canvas_cursor(pre_enter_cursor);
431 pre_enter_cursor = 0;
438 MidiRegionView::mouse_mode_changed ()
440 if (trackview.editor().current_mouse_mode() == MouseDraw && trackview.editor().internal_editing()) {
441 create_ghost_note (_last_event_x, _last_event_y);
443 remove_ghost_note ();
444 trackview.editor().verbose_cursor()->hide ();
447 if (!trackview.editor().internal_editing()) {
448 Keyboard::magic_widget_drop_focus();
450 Keyboard::magic_widget_grab_focus();
456 MidiRegionView::button_press (GdkEventButton* ev)
458 if (ev->button != 1) {
462 Editor* editor = dynamic_cast<Editor *> (&trackview.editor());
463 MouseMode m = editor->current_mouse_mode();
465 if (m == MouseObject && Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())) {
466 pre_press_cursor = editor->get_canvas_cursor ();
467 editor->set_canvas_cursor (editor->cursors()->midi_pencil);
470 if (_mouse_state != SelectTouchDragging) {
472 _pressed_button = ev->button;
473 _mouse_state = Pressed;
478 _pressed_button = ev->button;
484 MidiRegionView::button_release (GdkEventButton* ev)
486 double event_x, event_y;
488 if (ev->button != 1) {
495 group->canvas_to_item (event_x, event_y);
498 PublicEditor& editor = trackview.editor ();
500 if (pre_press_cursor) {
501 dynamic_cast<Editor*>(&editor)->set_canvas_cursor (pre_press_cursor, false);
502 pre_press_cursor = 0;
505 switch (_mouse_state) {
506 case Pressed: // Clicked
508 switch (editor.current_mouse_mode()) {
510 /* no motion occured - simple click */
519 if (Keyboard::is_insert_note_event(ev)) {
521 double event_x, event_y;
525 group->canvas_to_item (event_x, event_y);
528 Evoral::MusicalTime beats = editor.get_grid_type_as_beats (success, editor.pixel_to_sample (event_x));
534 /* Shorten the length by 1 tick so that we can add a new note at the next
535 grid snap without it overlapping this one.
537 beats -= 1.0 / Timecode::BBT_Time::ticks_per_beat;
539 create_note_at (editor.pixel_to_sample (event_x), event_y, beats, true);
547 Evoral::MusicalTime beats = editor.get_grid_type_as_beats (success, editor.pixel_to_sample (event_x));
553 /* Shorten the length by 1 tick so that we can add a new note at the next
554 grid snap without it overlapping this one.
556 beats -= 1.0 / Timecode::BBT_Time::ticks_per_beat;
558 create_note_at (editor.pixel_to_sample (event_x), event_y, beats, true);
569 case SelectRectDragging:
571 editor.drags()->end_grab ((GdkEvent *) ev);
573 create_ghost_note (ev->x, ev->y);
585 MidiRegionView::motion (GdkEventMotion* ev)
587 PublicEditor& editor = trackview.editor ();
589 if (!_ghost_note && editor.current_mouse_mode() == MouseObject &&
590 Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier()) &&
591 _mouse_state != AddDragging) {
593 create_ghost_note (ev->x, ev->y);
595 } else if (_ghost_note && editor.current_mouse_mode() == MouseObject &&
596 Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())) {
598 update_ghost_note (ev->x, ev->y);
600 } else if (_ghost_note && editor.current_mouse_mode() == MouseObject) {
602 remove_ghost_note ();
603 editor.verbose_cursor()->hide ();
605 } else if (_ghost_note && editor.current_mouse_mode() == MouseDraw) {
607 update_ghost_note (ev->x, ev->y);
610 /* any motion immediately hides velocity text that may have been visible */
612 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
613 (*i)->hide_velocity ();
616 switch (_mouse_state) {
619 if (_pressed_button == 1) {
621 MouseMode m = editor.current_mouse_mode();
623 if (m == MouseDraw || (m == MouseObject && Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier()))) {
624 editor.drags()->set (new NoteCreateDrag (dynamic_cast<Editor *> (&editor), group, this), (GdkEvent *) ev);
625 _mouse_state = AddDragging;
626 remove_ghost_note ();
627 editor.verbose_cursor()->hide ();
629 } else if (m == MouseObject) {
630 editor.drags()->set (new MidiRubberbandSelectDrag (dynamic_cast<Editor *> (&editor), this), (GdkEvent *) ev);
632 _mouse_state = SelectRectDragging;
634 } else if (m == MouseRange) {
635 editor.drags()->set (new MidiVerticalSelectDrag (dynamic_cast<Editor *> (&editor), this), (GdkEvent *) ev);
636 _mouse_state = SelectVerticalDragging;
643 case SelectRectDragging:
644 case SelectVerticalDragging:
646 editor.drags()->motion_handler ((GdkEvent *) ev, false);
649 case SelectTouchDragging:
657 /* we may be dragging some non-note object (eg. patch-change, sysex)
660 return editor.drags()->motion_handler ((GdkEvent *) ev, false);
665 MidiRegionView::scroll (GdkEventScroll* ev)
667 if (_selection.empty()) {
671 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
672 /* XXX: bit of a hack; allow PrimaryModifier scroll through so that
673 it still works for zoom.
678 trackview.editor().verbose_cursor()->hide ();
680 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
681 bool together = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
683 if (ev->direction == GDK_SCROLL_UP) {
684 change_velocities (true, fine, false, together);
685 } else if (ev->direction == GDK_SCROLL_DOWN) {
686 change_velocities (false, fine, false, together);
688 /* left, right: we don't use them */
696 MidiRegionView::key_press (GdkEventKey* ev)
698 /* since GTK bindings are generally activated on press, and since
699 detectable auto-repeat is the name of the game and only sends
700 repeated presses, carry out key actions at key press, not release.
703 bool unmodified = Keyboard::no_modifier_keys_pressed (ev);
705 if (unmodified && (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R)) {
706 _mouse_state = SelectTouchDragging;
709 } else if (ev->keyval == GDK_Escape && unmodified) {
713 } else if (unmodified && (ev->keyval == GDK_comma || ev->keyval == GDK_period)) {
715 bool start = (ev->keyval == GDK_comma);
716 bool end = (ev->keyval == GDK_period);
717 bool shorter = Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier);
718 bool fine = Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
720 change_note_lengths (fine, shorter, 0.0, start, end);
724 } else if ((ev->keyval == GDK_BackSpace || ev->keyval == GDK_Delete) && unmodified) {
726 if (_selection.empty()) {
733 } else if (ev->keyval == GDK_Tab) {
735 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
736 goto_previous_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
738 goto_next_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
742 } else if (ev->keyval == GDK_ISO_Left_Tab) {
744 /* Shift-TAB generates ISO Left Tab, for some reason */
746 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
747 goto_previous_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
749 goto_next_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
755 } else if (ev->keyval == GDK_Up) {
757 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
758 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
759 bool together = Keyboard::modifier_state_contains (ev->state, Keyboard::Level4Modifier);
761 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
762 change_velocities (true, fine, allow_smush, together);
764 transpose (true, fine, allow_smush);
768 } else if (ev->keyval == GDK_Down) {
770 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
771 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
772 bool together = Keyboard::modifier_state_contains (ev->state, Keyboard::Level4Modifier);
774 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
775 change_velocities (false, fine, allow_smush, together);
777 transpose (false, fine, allow_smush);
781 } else if (ev->keyval == GDK_Left && unmodified) {
786 } else if (ev->keyval == GDK_Right && unmodified) {
791 } else if (ev->keyval == GDK_c && unmodified) {
795 } else if (ev->keyval == GDK_v && unmodified) {
804 MidiRegionView::key_release (GdkEventKey* ev)
806 if ((_mouse_state == SelectTouchDragging) && (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R)) {
814 MidiRegionView::channel_edit ()
816 if (_selection.empty()) {
820 /* pick a note somewhat at random (since Selection is a set<>) to
821 * provide the "current" channel for the dialog.
824 uint8_t current_channel = (*_selection.begin())->note()->channel ();
825 MidiChannelDialog channel_dialog (current_channel);
826 int ret = channel_dialog.run ();
829 case Gtk::RESPONSE_OK:
835 uint8_t new_channel = channel_dialog.active_channel ();
837 start_note_diff_command (_("channel edit"));
839 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
840 Selection::iterator next = i;
842 change_note_channel (*i, new_channel);
850 MidiRegionView::velocity_edit ()
852 if (_selection.empty()) {
856 /* pick a note somewhat at random (since Selection is a set<>) to
857 * provide the "current" velocity for the dialog.
860 uint8_t current_velocity = (*_selection.begin())->note()->velocity ();
861 MidiVelocityDialog velocity_dialog (current_velocity);
862 int ret = velocity_dialog.run ();
865 case Gtk::RESPONSE_OK:
871 uint8_t new_velocity = velocity_dialog.velocity ();
873 start_note_diff_command (_("velocity edit"));
875 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
876 Selection::iterator next = i;
878 change_note_velocity (*i, new_velocity, false);
886 MidiRegionView::show_list_editor ()
889 _list_editor = new MidiListEditor (trackview.session(), midi_region(), midi_view()->midi_track());
891 _list_editor->present ();
894 /** Add a note to the model, and the view, at a canvas (click) coordinate.
895 * \param t time in frames relative to the position of the region
896 * \param y vertical position in pixels
897 * \param length duration of the note in beats
898 * \param snap_t true to snap t to the grid, otherwise false.
901 MidiRegionView::create_note_at (framepos_t t, double y, double length, bool snap_t)
903 if (length < 2 * DBL_EPSILON) {
907 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
908 MidiStreamView* const view = mtv->midi_view();
910 const double note = view->y_to_note(y);
912 // Start of note in frames relative to region start
914 framecnt_t grid_frames;
915 t = snap_frame_to_grid_underneath (t, grid_frames);
918 const boost::shared_ptr<NoteType> new_note (
919 new NoteType (mtv->get_channel_for_add (),
920 region_frames_to_region_beats(t + _region->start()),
922 (uint8_t)note, 0x40));
924 if (_model->contains (new_note)) {
928 view->update_note_range(new_note->note());
930 MidiModel::NoteDiffCommand* cmd = _model->new_note_diff_command(_("add note"));
932 _model->apply_command(*trackview.session(), cmd);
934 play_midi_note (new_note);
938 MidiRegionView::clear_events (bool with_selection_signal)
940 clear_selection (with_selection_signal);
943 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
944 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
949 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
954 _patch_changes.clear();
956 _optimization_iterator = _events.end();
960 MidiRegionView::display_model(boost::shared_ptr<MidiModel> model)
964 content_connection.disconnect ();
965 _model->ContentsChanged.connect (content_connection, invalidator (*this), boost::bind (&MidiRegionView::redisplay_model, this), gui_context());
969 if (_enable_display) {
975 MidiRegionView::start_note_diff_command (string name)
977 if (!_note_diff_command) {
978 _note_diff_command = _model->new_note_diff_command (name);
983 MidiRegionView::note_diff_add_note (const boost::shared_ptr<NoteType> note, bool selected, bool show_velocity)
985 if (_note_diff_command) {
986 _note_diff_command->add (note);
989 _marked_for_selection.insert(note);
992 _marked_for_velocity.insert(note);
997 MidiRegionView::note_diff_remove_note (NoteBase* ev)
999 if (_note_diff_command && ev->note()) {
1000 _note_diff_command->remove(ev->note());
1005 MidiRegionView::note_diff_add_change (NoteBase* ev,
1006 MidiModel::NoteDiffCommand::Property property,
1009 if (_note_diff_command) {
1010 _note_diff_command->change (ev->note(), property, val);
1015 MidiRegionView::note_diff_add_change (NoteBase* ev,
1016 MidiModel::NoteDiffCommand::Property property,
1017 Evoral::MusicalTime val)
1019 if (_note_diff_command) {
1020 _note_diff_command->change (ev->note(), property, val);
1025 MidiRegionView::apply_diff (bool as_subcommand)
1029 if (!_note_diff_command) {
1033 if ((add_or_remove = _note_diff_command->adds_or_removes())) {
1034 // Mark all selected notes for selection when model reloads
1035 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1036 _marked_for_selection.insert((*i)->note());
1040 if (as_subcommand) {
1041 _model->apply_command_as_subcommand (*trackview.session(), _note_diff_command);
1043 _model->apply_command (*trackview.session(), _note_diff_command);
1046 _note_diff_command = 0;
1047 midi_view()->midi_track()->playlist_modified();
1049 if (add_or_remove) {
1050 _marked_for_selection.clear();
1053 _marked_for_velocity.clear();
1057 MidiRegionView::abort_command()
1059 delete _note_diff_command;
1060 _note_diff_command = 0;
1065 MidiRegionView::find_canvas_note (boost::shared_ptr<NoteType> note)
1067 if (_optimization_iterator != _events.end()) {
1068 ++_optimization_iterator;
1071 if (_optimization_iterator != _events.end() && (*_optimization_iterator)->note() == note) {
1072 return *_optimization_iterator;
1075 for (_optimization_iterator = _events.begin(); _optimization_iterator != _events.end(); ++_optimization_iterator) {
1076 if ((*_optimization_iterator)->note() == note) {
1077 return *_optimization_iterator;
1085 MidiRegionView::get_events (Events& e, Evoral::Sequence<Evoral::MusicalTime>::NoteOperator op, uint8_t val, int chan_mask)
1087 MidiModel::Notes notes;
1088 _model->get_notes (notes, op, val, chan_mask);
1090 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1091 NoteBase* cne = find_canvas_note (*n);
1099 MidiRegionView::redisplay_model()
1101 // Don't redisplay the model if we're currently recording and displaying that
1102 if (_active_notes) {
1110 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1111 (*i)->invalidate ();
1114 MidiModel::ReadLock lock(_model->read_lock());
1116 MidiModel::Notes& notes (_model->notes());
1117 _optimization_iterator = _events.begin();
1119 bool empty_when_starting = _events.empty();
1121 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1123 boost::shared_ptr<NoteType> note (*n);
1127 if (note_in_region_range (note, visible)) {
1129 if (!empty_when_starting && (cne = find_canvas_note (note)) != 0) {
1136 if ((cn = dynamic_cast<Note*>(cne)) != 0) {
1138 } else if ((ch = dynamic_cast<Hit*>(cne)) != 0) {
1150 add_note (note, visible);
1155 if (!empty_when_starting && (cne = find_canvas_note (note)) != 0) {
1163 /* remove note items that are no longer valid */
1165 if (!empty_when_starting) {
1166 for (Events::iterator i = _events.begin(); i != _events.end(); ) {
1167 if (!(*i)->valid ()) {
1169 for (vector<GhostRegion*>::iterator j = ghosts.begin(); j != ghosts.end(); ++j) {
1170 MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*j);
1172 gr->remove_note (*i);
1177 i = _events.erase (i);
1185 _patch_changes.clear();
1189 display_patch_changes ();
1191 _marked_for_selection.clear ();
1192 _marked_for_velocity.clear ();
1194 /* we may have caused _events to contain things out of order (e.g. if a note
1195 moved earlier or later). we don't generally need them in time order, but
1196 make a note that a sort is required for those cases that require it.
1199 _sort_needed = true;
1203 MidiRegionView::display_patch_changes ()
1205 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1206 uint16_t chn_mask = mtv->midi_track()->get_playback_channel_mask();
1208 for (uint8_t i = 0; i < 16; ++i) {
1209 display_patch_changes_on_channel (i, chn_mask & (1 << i));
1213 /** @param active_channel true to display patch changes fully, false to display
1214 * them `greyed-out' (as on an inactive channel)
1217 MidiRegionView::display_patch_changes_on_channel (uint8_t channel, bool active_channel)
1219 for (MidiModel::PatchChanges::const_iterator i = _model->patch_changes().begin(); i != _model->patch_changes().end(); ++i) {
1221 if ((*i)->channel() != channel) {
1225 const string patch_name = instrument_info().get_patch_name ((*i)->bank(), (*i)->program(), channel);
1226 add_canvas_patch_change (*i, patch_name, active_channel);
1231 MidiRegionView::display_sysexes()
1233 bool have_periodic_system_messages = false;
1234 bool display_periodic_messages = true;
1236 if (!Config->get_never_display_periodic_midi()) {
1238 for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
1239 const boost::shared_ptr<const Evoral::MIDIEvent<Evoral::MusicalTime> > mev =
1240 boost::static_pointer_cast<const Evoral::MIDIEvent<Evoral::MusicalTime> > (*i);
1243 if (mev->is_spp() || mev->is_mtc_quarter() || mev->is_mtc_full()) {
1244 have_periodic_system_messages = true;
1250 if (have_periodic_system_messages) {
1251 double zoom = trackview.editor().get_current_zoom (); // frames per pixel
1253 /* get an approximate value for the number of samples per video frame */
1255 double video_frame = trackview.session()->frame_rate() * (1.0/30);
1257 /* if we are zoomed out beyond than the cutoff (i.e. more
1258 * frames per pixel than frames per 4 video frames), don't
1259 * show periodic sysex messages.
1262 if (zoom > (video_frame*4)) {
1263 display_periodic_messages = false;
1267 display_periodic_messages = false;
1270 for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
1272 const boost::shared_ptr<const Evoral::MIDIEvent<Evoral::MusicalTime> > mev =
1273 boost::static_pointer_cast<const Evoral::MIDIEvent<Evoral::MusicalTime> > (*i);
1275 Evoral::MusicalTime time = (*i)->time();
1278 if (mev->is_spp() || mev->is_mtc_quarter() || mev->is_mtc_full()) {
1279 if (!display_periodic_messages) {
1287 for (uint32_t b = 0; b < (*i)->size(); ++b) {
1288 str << int((*i)->buffer()[b]);
1289 if (b != (*i)->size() -1) {
1293 string text = str.str();
1295 const double x = trackview.editor().sample_to_pixel(source_beats_to_region_frames(time));
1297 double height = midi_stream_view()->contents_height();
1299 // CAIROCANVAS: no longer passing *i (the sysex event) to the
1300 // SysEx canvas object!!!
1302 boost::shared_ptr<SysEx> sysex = boost::shared_ptr<SysEx>(
1303 new SysEx (*this, _note_group, text, height, x, 1.0));
1305 // Show unless message is beyond the region bounds
1306 if (time - _region->start() >= _region->length() || time < _region->start()) {
1312 _sys_exes.push_back(sysex);
1316 MidiRegionView::~MidiRegionView ()
1318 in_destructor = true;
1320 trackview.editor().verbose_cursor()->hide ();
1322 note_delete_connection.disconnect ();
1324 delete _list_editor;
1326 RegionViewGoingAway (this); /* EMIT_SIGNAL */
1328 if (_active_notes) {
1332 _selection_cleared_connection.disconnect ();
1335 clear_events (false);
1338 delete _note_diff_command;
1339 delete _step_edit_cursor;
1340 delete _temporary_note_group;
1344 MidiRegionView::region_resized (const PropertyChange& what_changed)
1346 RegionView::region_resized(what_changed);
1348 if (what_changed.contains (ARDOUR::Properties::position)) {
1349 set_duration(_region->length(), 0);
1350 if (_enable_display) {
1357 MidiRegionView::reset_width_dependent_items (double pixel_width)
1359 RegionView::reset_width_dependent_items(pixel_width);
1361 if (_enable_display) {
1365 for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
1366 if ((*x)->canvas_item()->width() >= _pixel_width) {
1373 move_step_edit_cursor (_step_edit_cursor_position);
1374 set_step_edit_cursor_width (_step_edit_cursor_width);
1378 MidiRegionView::set_height (double height)
1380 double old_height = _height;
1381 RegionView::set_height(height);
1383 apply_note_range (midi_stream_view()->lowest_note(),
1384 midi_stream_view()->highest_note(),
1385 height != old_height);
1388 name_text->raise_to_top();
1391 for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
1392 (*x)->set_height (midi_stream_view()->contents_height());
1395 if (_step_edit_cursor) {
1396 _step_edit_cursor->set_y1 (midi_stream_view()->contents_height());
1401 /** Apply the current note range from the stream view
1402 * by repositioning/hiding notes as necessary
1405 MidiRegionView::apply_note_range (uint8_t min, uint8_t max, bool force)
1407 if (!_enable_display) {
1411 if (!force && _current_range_min == min && _current_range_max == max) {
1415 _current_range_min = min;
1416 _current_range_max = max;
1418 for (Events::const_iterator i = _events.begin(); i != _events.end(); ++i) {
1419 NoteBase* event = *i;
1420 boost::shared_ptr<NoteType> note (event->note());
1422 if (note->note() < _current_range_min ||
1423 note->note() > _current_range_max) {
1429 if (Note* cnote = dynamic_cast<Note*>(event)) {
1431 const double y0 = 1. + floor (midi_stream_view()->note_to_y(note->note()));
1432 const double y1 = y0 + std::max(1., floor(midi_stream_view()->note_height()) - 1.);
1437 } else if (Hit* chit = dynamic_cast<Hit*>(event)) {
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_all ();
1523 _active_notes[note] = 0;
1529 /** Extend active notes to rightmost edge of region (if length is changed)
1532 MidiRegionView::extend_active_notes()
1534 if (!_active_notes) {
1538 for (unsigned i=0; i < 128; ++i) {
1539 if (_active_notes[i]) {
1540 _active_notes[i]->set_x1 (trackview.editor().sample_to_pixel(_region->length()));
1547 MidiRegionView::play_midi_note(boost::shared_ptr<NoteType> note)
1549 if (_no_sound_notes || !Config->get_sound_midi_notes()) {
1553 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1555 if (!route_ui || !route_ui->midi_track()) {
1559 NotePlayer* np = new NotePlayer (route_ui->midi_track ());
1563 /* NotePlayer deletes itself */
1567 MidiRegionView::start_playing_midi_note(boost::shared_ptr<NoteType> note)
1569 if (_no_sound_notes || !Config->get_sound_midi_notes()) {
1573 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1575 if (!route_ui || !route_ui->midi_track()) {
1579 delete _note_player;
1580 _note_player = new NotePlayer (route_ui->midi_track ());
1581 _note_player->add (note);
1582 _note_player->on ();
1586 MidiRegionView::start_playing_midi_chord (vector<boost::shared_ptr<NoteType> > notes)
1588 if (_no_sound_notes || !Config->get_sound_midi_notes()) {
1592 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1594 if (!route_ui || !route_ui->midi_track()) {
1598 delete _note_player;
1599 _note_player = new NotePlayer (route_ui->midi_track());
1601 for (vector<boost::shared_ptr<NoteType> >::iterator n = notes.begin(); n != notes.end(); ++n) {
1602 _note_player->add (*n);
1605 _note_player->on ();
1610 MidiRegionView::note_in_region_range (const boost::shared_ptr<NoteType> note, bool& visible) const
1612 const framepos_t note_start_frames = source_beats_to_region_frames (note->time());
1613 bool outside = (note_start_frames < 0) || (note_start_frames > _region->last_frame());
1615 visible = (note->note() >= midi_stream_view()->lowest_note()) &&
1616 (note->note() <= midi_stream_view()->highest_note());
1621 /** Update a canvas note's size from its model note.
1622 * @param ev Canvas note to update.
1623 * @param update_ghost_regions true to update the note in any ghost regions that we have, otherwise false.
1626 MidiRegionView::update_note (Note* ev, bool update_ghost_regions)
1628 boost::shared_ptr<NoteType> note = ev->note();
1629 const double x = trackview.editor().sample_to_pixel (source_beats_to_region_frames (note->time()));
1630 const double y0 = 1 + floor(midi_stream_view()->note_to_y(note->note()));
1635 /* trim note display to not overlap the end of its region */
1637 if (note->length() > 0) {
1638 const framepos_t note_end_frames = min (source_beats_to_region_frames (note->end_time()), _region->length());
1639 ev->set_x1 (trackview.editor().sample_to_pixel (note_end_frames));
1641 ev->set_x1 (trackview.editor().sample_to_pixel (_region->length()));
1644 ev->set_y1 (y0 + std::max(1., floor(midi_stream_view()->note_height()) - 1));
1646 if (note->length() == 0) {
1647 if (_active_notes && note->note() < 128) {
1648 // If this note is already active there's a stuck note,
1649 // finish the old note rectangle
1650 if (_active_notes[note->note()]) {
1651 Note* const old_rect = _active_notes[note->note()];
1652 boost::shared_ptr<NoteType> old_note = old_rect->note();
1653 old_rect->set_x1 (x);
1654 old_rect->set_outline_all ();
1656 _active_notes[note->note()] = ev;
1658 /* outline all but right edge */
1659 ev->set_outline_what (ArdourCanvas::Rectangle::What (
1660 ArdourCanvas::Rectangle::TOP|
1661 ArdourCanvas::Rectangle::LEFT|
1662 ArdourCanvas::Rectangle::BOTTOM));
1664 /* outline all edges */
1665 ev->set_outline_all ();
1668 if (update_ghost_regions) {
1669 for (std::vector<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
1670 MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*i);
1672 gr->update_note (ev);
1679 MidiRegionView::update_hit (Hit* ev)
1681 boost::shared_ptr<NoteType> note = ev->note();
1683 const framepos_t note_start_frames = source_beats_to_region_frames(note->time());
1684 const double x = trackview.editor().sample_to_pixel(note_start_frames);
1685 const double diamond_size = std::max(1., floor(midi_stream_view()->note_height()) - 2.);
1686 const double y = 1.5 + floor(midi_stream_view()->note_to_y(note->note())) + diamond_size * .5;
1688 ev->set_position (ArdourCanvas::Duple (x, y));
1689 ev->set_height (diamond_size);
1692 /** Add a MIDI note to the view (with length).
1694 * If in sustained mode, notes with length 0 will be considered active
1695 * notes, and resolve_note should be called when the corresponding note off
1696 * event arrives, to properly display the note.
1699 MidiRegionView::add_note(const boost::shared_ptr<NoteType> note, bool visible)
1701 NoteBase* event = 0;
1703 if (midi_view()->note_mode() == Sustained) {
1705 Note* ev_rect = new Note (*this, _note_group, note);
1707 update_note (ev_rect);
1711 MidiGhostRegion* gr;
1713 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
1714 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
1715 gr->add_note(ev_rect);
1719 } else if (midi_view()->note_mode() == Percussive) {
1721 const double diamond_size = std::max(1., floor(midi_stream_view()->note_height()) - 2.);
1723 Hit* ev_diamond = new Hit (*this, _note_group, diamond_size, note);
1725 update_hit (ev_diamond);
1734 if (_marked_for_selection.find(note) != _marked_for_selection.end()) {
1735 note_selected(event, true);
1738 if (_marked_for_velocity.find(note) != _marked_for_velocity.end()) {
1739 event->show_velocity();
1742 event->on_channel_selection_change (get_selected_channels());
1743 _events.push_back(event);
1752 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1753 MidiStreamView* const view = mtv->midi_view();
1755 view->update_note_range (note->note());
1759 MidiRegionView::step_add_note (uint8_t channel, uint8_t number, uint8_t velocity,
1760 Evoral::MusicalTime pos, Evoral::MusicalTime len)
1762 boost::shared_ptr<NoteType> new_note (new NoteType (channel, pos, len, number, velocity));
1764 /* potentially extend region to hold new note */
1766 framepos_t end_frame = source_beats_to_absolute_frames (new_note->end_time());
1767 framepos_t region_end = _region->last_frame();
1769 if (end_frame > region_end) {
1770 _region->set_length (end_frame - _region->position());
1773 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1774 MidiStreamView* const view = mtv->midi_view();
1776 view->update_note_range(new_note->note());
1778 _marked_for_selection.clear ();
1781 start_note_diff_command (_("step add"));
1782 note_diff_add_note (new_note, true, false);
1785 // last_step_edit_note = new_note;
1789 MidiRegionView::step_sustain (Evoral::MusicalTime beats)
1791 change_note_lengths (false, false, beats, false, true);
1794 /** Add a new patch change flag to the canvas.
1795 * @param patch the patch change to add
1796 * @param the text to display in the flag
1797 * @param active_channel true to display the flag as on an active channel, false to grey it out for an inactive channel.
1800 MidiRegionView::add_canvas_patch_change (MidiModel::PatchChangePtr patch, const string& displaytext, bool /*active_channel*/)
1802 framecnt_t region_frames = source_beats_to_region_frames (patch->time());
1803 const double x = trackview.editor().sample_to_pixel (region_frames);
1805 double const height = midi_stream_view()->contents_height();
1807 // CAIROCANVAS: active_channel info removed from PatcChange constructor
1808 // so we need to do something more sophisticated to keep its color
1809 // appearance (MidiPatchChangeFill/MidiPatchChangeInactiveChannelFill)
1812 boost::shared_ptr<PatchChange> patch_change = boost::shared_ptr<PatchChange>(
1813 new PatchChange(*this, group,
1820 if (patch_change->item().width() < _pixel_width) {
1821 // Show unless patch change is beyond the region bounds
1822 if (region_frames < 0 || region_frames >= _region->length()) {
1823 patch_change->hide();
1825 patch_change->show();
1828 patch_change->hide ();
1831 _patch_changes.push_back (patch_change);
1834 MIDI::Name::PatchPrimaryKey
1835 MidiRegionView::patch_change_to_patch_key (MidiModel::PatchChangePtr p)
1837 return MIDI::Name::PatchPrimaryKey (p->program(), p->bank());
1840 /// Return true iff @p pc applies to the given time on the given channel.
1842 patch_applies (const ARDOUR::MidiModel::constPatchChangePtr pc, double time, uint8_t channel)
1844 return pc->time() <= time && pc->channel() == channel;
1848 MidiRegionView::get_patch_key_at (double time, uint8_t channel, MIDI::Name::PatchPrimaryKey& key) const
1850 // The earliest event not before time
1851 MidiModel::PatchChanges::iterator i = _model->patch_change_lower_bound (time);
1853 // Go backwards until we find the latest PC for this channel, or the start
1854 while (i != _model->patch_changes().begin() &&
1855 (i == _model->patch_changes().end() ||
1856 !patch_applies(*i, time, channel))) {
1860 if (i != _model->patch_changes().end() && patch_applies(*i, time, channel)) {
1861 key.bank_number = (*i)->bank();
1862 key.program_number = (*i)->program ();
1864 key.bank_number = key.program_number = 0;
1867 if (!key.is_sane()) {
1868 error << string_compose(_("insane MIDI patch key %1:%2"),
1869 key.bank_number, key.program_number) << endmsg;
1874 MidiRegionView::change_patch_change (PatchChange& pc, const MIDI::Name::PatchPrimaryKey& new_patch)
1876 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("alter patch change"));
1878 if (pc.patch()->program() != new_patch.program_number) {
1879 c->change_program (pc.patch (), new_patch.program_number);
1882 int const new_bank = new_patch.bank_number;
1883 if (pc.patch()->bank() != new_bank) {
1884 c->change_bank (pc.patch (), new_bank);
1887 _model->apply_command (*trackview.session(), c);
1889 _patch_changes.clear ();
1890 display_patch_changes ();
1894 MidiRegionView::change_patch_change (MidiModel::PatchChangePtr old_change, const Evoral::PatchChange<Evoral::MusicalTime> & new_change)
1896 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("alter patch change"));
1898 if (old_change->time() != new_change.time()) {
1899 c->change_time (old_change, new_change.time());
1902 if (old_change->channel() != new_change.channel()) {
1903 c->change_channel (old_change, new_change.channel());
1906 if (old_change->program() != new_change.program()) {
1907 c->change_program (old_change, new_change.program());
1910 if (old_change->bank() != new_change.bank()) {
1911 c->change_bank (old_change, new_change.bank());
1914 _model->apply_command (*trackview.session(), c);
1916 _patch_changes.clear ();
1917 display_patch_changes ();
1920 /** Add a patch change to the region.
1921 * @param t Time in frames relative to region position
1922 * @param patch Patch to add; time and channel are ignored (time is converted from t, and channel comes from
1923 * MidiTimeAxisView::get_channel_for_add())
1926 MidiRegionView::add_patch_change (framecnt_t t, Evoral::PatchChange<Evoral::MusicalTime> const & patch)
1928 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1930 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("add patch change"));
1931 c->add (MidiModel::PatchChangePtr (
1932 new Evoral::PatchChange<Evoral::MusicalTime> (
1933 absolute_frames_to_source_beats (_region->position() + t),
1934 mtv->get_channel_for_add(), patch.program(), patch.bank()
1939 _model->apply_command (*trackview.session(), c);
1941 _patch_changes.clear ();
1942 display_patch_changes ();
1946 MidiRegionView::move_patch_change (PatchChange& pc, Evoral::MusicalTime t)
1948 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("move patch change"));
1949 c->change_time (pc.patch (), t);
1950 _model->apply_command (*trackview.session(), c);
1952 _patch_changes.clear ();
1953 display_patch_changes ();
1957 MidiRegionView::delete_patch_change (PatchChange* pc)
1959 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("delete patch change"));
1960 c->remove (pc->patch ());
1961 _model->apply_command (*trackview.session(), c);
1963 _patch_changes.clear ();
1964 display_patch_changes ();
1968 MidiRegionView::previous_patch (PatchChange& patch)
1970 if (patch.patch()->program() < 127) {
1971 MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch());
1972 key.program_number++;
1973 change_patch_change (patch, key);
1978 MidiRegionView::next_patch (PatchChange& patch)
1980 if (patch.patch()->program() > 0) {
1981 MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch());
1982 key.program_number--;
1983 change_patch_change (patch, key);
1988 MidiRegionView::next_bank (PatchChange& patch)
1990 if (patch.patch()->program() < 127) {
1991 MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch());
1992 if (key.bank_number > 0) {
1994 change_patch_change (patch, key);
2000 MidiRegionView::previous_bank (PatchChange& patch)
2002 if (patch.patch()->program() > 0) {
2003 MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch());
2004 if (key.bank_number < 127) {
2006 change_patch_change (patch, key);
2012 MidiRegionView::maybe_remove_deleted_note_from_selection (NoteBase* cne)
2014 if (_selection.empty()) {
2018 _selection.erase (cne);
2022 MidiRegionView::delete_selection()
2024 if (_selection.empty()) {
2028 start_note_diff_command (_("delete selection"));
2030 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2031 if ((*i)->selected()) {
2032 _note_diff_command->remove((*i)->note());
2042 MidiRegionView::delete_note (boost::shared_ptr<NoteType> n)
2044 start_note_diff_command (_("delete note"));
2045 _note_diff_command->remove (n);
2048 trackview.editor().verbose_cursor()->hide ();
2052 MidiRegionView::clear_selection_except (NoteBase* ev, bool signal)
2054 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2056 Selection::iterator tmp = i;
2059 (*i)->set_selected (false);
2060 (*i)->hide_velocity ();
2061 _selection.erase (i);
2069 /* this does not change the status of this regionview w.r.t the editor
2074 SelectionCleared (this); /* EMIT SIGNAL */
2079 MidiRegionView::unique_select(NoteBase* ev)
2081 clear_selection_except (ev);
2083 /* don't bother with checking to see if we should remove this
2084 regionview from the editor selection, since we're about to add
2085 another note, and thus put/keep this regionview in the editor
2089 if (!ev->selected()) {
2090 add_to_selection (ev);
2095 MidiRegionView::select_all_notes ()
2099 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2100 add_to_selection (*i);
2105 MidiRegionView::select_range (framepos_t start, framepos_t end)
2109 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2110 framepos_t t = source_beats_to_absolute_frames((*i)->note()->time());
2111 if (t >= start && t <= end) {
2112 add_to_selection (*i);
2118 MidiRegionView::invert_selection ()
2120 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2121 if ((*i)->selected()) {
2122 remove_from_selection(*i);
2124 add_to_selection (*i);
2130 MidiRegionView::select_matching_notes (uint8_t notenum, uint16_t channel_mask, bool add, bool extend)
2132 bool have_selection = !_selection.empty();
2133 uint8_t low_note = 127;
2134 uint8_t high_note = 0;
2135 MidiModel::Notes& notes (_model->notes());
2136 _optimization_iterator = _events.begin();
2138 if (extend && !have_selection) {
2142 /* scan existing selection to get note range */
2144 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2145 if ((*i)->note()->note() < low_note) {
2146 low_note = (*i)->note()->note();
2148 if ((*i)->note()->note() > high_note) {
2149 high_note = (*i)->note()->note();
2156 if (!extend && (low_note == high_note) && (high_note == notenum)) {
2157 /* only note previously selected is the one we are
2158 * reselecting. treat this as cancelling the selection.
2165 low_note = min (low_note, notenum);
2166 high_note = max (high_note, notenum);
2169 _no_sound_notes = true;
2171 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
2173 boost::shared_ptr<NoteType> note (*n);
2175 bool select = false;
2177 if (((1 << note->channel()) & channel_mask) != 0) {
2179 if ((note->note() >= low_note && note->note() <= high_note)) {
2182 } else if (note->note() == notenum) {
2188 if ((cne = find_canvas_note (note)) != 0) {
2189 // extend is false because we've taken care of it,
2190 // since it extends by time range, not pitch.
2191 note_selected (cne, add, false);
2195 add = true; // we need to add all remaining matching notes, even if the passed in value was false (for "set")
2199 _no_sound_notes = false;
2203 MidiRegionView::toggle_matching_notes (uint8_t notenum, uint16_t channel_mask)
2205 MidiModel::Notes& notes (_model->notes());
2206 _optimization_iterator = _events.begin();
2208 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
2210 boost::shared_ptr<NoteType> note (*n);
2213 if (note->note() == notenum && (((0x0001 << note->channel()) & channel_mask) != 0)) {
2214 if ((cne = find_canvas_note (note)) != 0) {
2215 if (cne->selected()) {
2216 note_deselected (cne);
2218 note_selected (cne, true, false);
2226 MidiRegionView::note_selected (NoteBase* ev, bool add, bool extend)
2229 clear_selection_except (ev);
2230 if (!_selection.empty()) {
2231 PublicEditor& editor (trackview.editor());
2232 editor.get_selection().add (this);
2238 if (!ev->selected()) {
2239 add_to_selection (ev);
2243 /* find end of latest note selected, select all between that and the start of "ev" */
2245 Evoral::MusicalTime earliest = Evoral::MaxMusicalTime;
2246 Evoral::MusicalTime latest = 0;
2248 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2249 if ((*i)->note()->end_time() > latest) {
2250 latest = (*i)->note()->end_time();
2252 if ((*i)->note()->time() < earliest) {
2253 earliest = (*i)->note()->time();
2257 if (ev->note()->end_time() > latest) {
2258 latest = ev->note()->end_time();
2261 if (ev->note()->time() < earliest) {
2262 earliest = ev->note()->time();
2265 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2267 /* find notes entirely within OR spanning the earliest..latest range */
2269 if (((*i)->note()->time() >= earliest && (*i)->note()->end_time() <= latest) ||
2270 ((*i)->note()->time() <= earliest && (*i)->note()->end_time() >= latest)) {
2271 add_to_selection (*i);
2279 MidiRegionView::note_deselected(NoteBase* ev)
2281 remove_from_selection (ev);
2285 MidiRegionView::update_drag_selection(double x0, double x1, double y0, double y1, bool extend)
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);
2305 MidiRegionView::update_vertical_drag_selection (double y1, double y2, bool extend)
2311 // TODO: Make this faster by storing the last updated selection rect, and only
2312 // adjusting things that are in the area that appears/disappeared.
2313 // We probably need a tree to be able to find events in O(log(n)) time.
2315 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2316 if (((*i)->y1() >= y1 && (*i)->y1() <= y2)) {
2317 // within y- (note-) range
2318 if (!(*i)->selected()) {
2319 add_to_selection (*i);
2321 } else if ((*i)->selected() && !extend) {
2322 remove_from_selection (*i);
2328 MidiRegionView::remove_from_selection (NoteBase* ev)
2330 Selection::iterator i = _selection.find (ev);
2332 if (i != _selection.end()) {
2333 _selection.erase (i);
2336 ev->set_selected (false);
2337 ev->hide_velocity ();
2339 if (_selection.empty()) {
2340 PublicEditor& editor (trackview.editor());
2341 editor.get_selection().remove (this);
2346 MidiRegionView::add_to_selection (NoteBase* ev)
2348 bool add_mrv_selection = false;
2350 if (_selection.empty()) {
2351 add_mrv_selection = true;
2354 if (_selection.insert (ev).second) {
2355 ev->set_selected (true);
2356 start_playing_midi_note ((ev)->note());
2359 if (add_mrv_selection) {
2360 PublicEditor& editor (trackview.editor());
2361 editor.get_selection().add (this);
2366 MidiRegionView::move_selection(double dx, double dy, double cumulative_dy)
2368 typedef vector<boost::shared_ptr<NoteType> > PossibleChord;
2369 PossibleChord to_play;
2370 Evoral::MusicalTime earliest = Evoral::MaxMusicalTime;
2372 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2373 if ((*i)->note()->time() < earliest) {
2374 earliest = (*i)->note()->time();
2378 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2379 if (Evoral::musical_time_equal ((*i)->note()->time(), earliest)) {
2380 to_play.push_back ((*i)->note());
2382 (*i)->move_event(dx, dy);
2385 if (dy && !_selection.empty() && !_no_sound_notes && Config->get_sound_midi_notes()) {
2387 if (to_play.size() > 1) {
2389 PossibleChord shifted;
2391 for (PossibleChord::iterator n = to_play.begin(); n != to_play.end(); ++n) {
2392 boost::shared_ptr<NoteType> moved_note (new NoteType (**n));
2393 moved_note->set_note (moved_note->note() + cumulative_dy);
2394 shifted.push_back (moved_note);
2397 start_playing_midi_chord (shifted);
2399 } else if (!to_play.empty()) {
2401 boost::shared_ptr<NoteType> moved_note (new NoteType (*to_play.front()));
2402 moved_note->set_note (moved_note->note() + cumulative_dy);
2403 start_playing_midi_note (moved_note);
2409 MidiRegionView::note_dropped(NoteBase *, frameoffset_t dt, int8_t dnote)
2411 uint8_t lowest_note_in_selection = 127;
2412 uint8_t highest_note_in_selection = 0;
2413 uint8_t highest_note_difference = 0;
2415 // find highest and lowest notes first
2417 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2418 uint8_t pitch = (*i)->note()->note();
2419 lowest_note_in_selection = std::min(lowest_note_in_selection, pitch);
2420 highest_note_in_selection = std::max(highest_note_in_selection, pitch);
2424 cerr << "dnote: " << (int) dnote << endl;
2425 cerr << "lowest note (streamview): " << int(midi_stream_view()->lowest_note())
2426 << " highest note (streamview): " << int(midi_stream_view()->highest_note()) << endl;
2427 cerr << "lowest note (selection): " << int(lowest_note_in_selection) << " highest note(selection): "
2428 << int(highest_note_in_selection) << endl;
2429 cerr << "selection size: " << _selection.size() << endl;
2430 cerr << "Highest note in selection: " << (int) highest_note_in_selection << endl;
2433 // Make sure the note pitch does not exceed the MIDI standard range
2434 if (highest_note_in_selection + dnote > 127) {
2435 highest_note_difference = highest_note_in_selection - 127;
2438 start_note_diff_command (_("move notes"));
2440 for (Selection::iterator i = _selection.begin(); i != _selection.end() ; ++i) {
2442 framepos_t new_frames = source_beats_to_absolute_frames ((*i)->note()->time()) + dt;
2443 Evoral::MusicalTime new_time = absolute_frames_to_source_beats (new_frames);
2449 note_diff_add_change (*i, MidiModel::NoteDiffCommand::StartTime, new_time);
2451 uint8_t original_pitch = (*i)->note()->note();
2452 uint8_t new_pitch = original_pitch + dnote - highest_note_difference;
2454 // keep notes in standard midi range
2455 clamp_to_0_127(new_pitch);
2457 lowest_note_in_selection = std::min(lowest_note_in_selection, new_pitch);
2458 highest_note_in_selection = std::max(highest_note_in_selection, new_pitch);
2460 note_diff_add_change (*i, MidiModel::NoteDiffCommand::NoteNumber, new_pitch);
2465 // care about notes being moved beyond the upper/lower bounds on the canvas
2466 if (lowest_note_in_selection < midi_stream_view()->lowest_note() ||
2467 highest_note_in_selection > midi_stream_view()->highest_note()) {
2468 midi_stream_view()->set_note_range(MidiStreamView::ContentsRange);
2472 /** @param x Pixel relative to the region position.
2473 * @return Snapped frame relative to the region position.
2476 MidiRegionView::snap_pixel_to_sample(double x)
2478 PublicEditor& editor (trackview.editor());
2479 return snap_frame_to_frame (editor.pixel_to_sample (x));
2482 /** @param x Pixel relative to the region position.
2483 * @return Snapped pixel relative to the region position.
2486 MidiRegionView::snap_to_pixel(double x)
2488 return (double) trackview.editor().sample_to_pixel(snap_pixel_to_sample(x));
2492 MidiRegionView::get_position_pixels()
2494 framepos_t region_frame = get_position();
2495 return trackview.editor().sample_to_pixel(region_frame);
2499 MidiRegionView::get_end_position_pixels()
2501 framepos_t frame = get_position() + get_duration ();
2502 return trackview.editor().sample_to_pixel(frame);
2506 MidiRegionView::source_beats_to_absolute_frames(double beats) const
2508 /* the time converter will return the frame corresponding to `beats'
2509 relative to the start of the source. The start of the source
2510 is an implied position given by region->position - region->start
2512 const framepos_t source_start = _region->position() - _region->start();
2513 return source_start + _source_relative_time_converter.to (beats);
2517 MidiRegionView::absolute_frames_to_source_beats(framepos_t frames) const
2519 /* the `frames' argument needs to be converted into a frame count
2520 relative to the start of the source before being passed in to the
2523 const framepos_t source_start = _region->position() - _region->start();
2524 return _source_relative_time_converter.from (frames - source_start);
2528 MidiRegionView::region_beats_to_region_frames(double beats) const
2530 return _region_relative_time_converter.to(beats);
2534 MidiRegionView::region_frames_to_region_beats(framepos_t frames) const
2536 return _region_relative_time_converter.from(frames);
2540 MidiRegionView::begin_resizing (bool /*at_front*/)
2542 _resize_data.clear();
2544 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2545 Note *note = dynamic_cast<Note*> (*i);
2547 // only insert CanvasNotes into the map
2549 NoteResizeData *resize_data = new NoteResizeData();
2550 resize_data->note = note;
2552 // create a new SimpleRect from the note which will be the resize preview
2553 ArdourCanvas::Rectangle *resize_rect = new ArdourCanvas::Rectangle (_note_group,
2554 ArdourCanvas::Rect (note->x0(), note->y0(), note->x0(), note->y1()));
2556 // calculate the colors: get the color settings
2557 uint32_t fill_color = UINT_RGBA_CHANGE_A(
2558 ARDOUR_UI::config()->get_MidiNoteSelected(),
2561 // make the resize preview notes more transparent and bright
2562 fill_color = UINT_INTERPOLATE(fill_color, 0xFFFFFF40, 0.5);
2564 // calculate color based on note velocity
2565 resize_rect->set_fill_color (UINT_INTERPOLATE(
2566 NoteBase::meter_style_fill_color(note->note()->velocity(), note->selected()),
2570 resize_rect->set_outline_color (NoteBase::calculate_outline (
2571 ARDOUR_UI::config()->get_MidiNoteSelected()));
2573 resize_data->resize_rect = resize_rect;
2574 _resize_data.push_back(resize_data);
2579 /** Update resizing notes while user drags.
2580 * @param primary `primary' note for the drag; ie the one that is used as the reference in non-relative mode.
2581 * @param at_front which end of the note (true == note on, false == note off)
2582 * @param delta_x change in mouse position since the start of the drag
2583 * @param relative true if relative resizing is taking place, false if absolute resizing. This only makes
2584 * a difference when multiple notes are being resized; in relative mode, each note's length is changed by the
2585 * amount of the drag. In non-relative mode, all selected notes are set to have the same start or end point
2586 * as the \a primary note.
2589 MidiRegionView::update_resizing (NoteBase* primary, bool at_front, double delta_x, bool relative)
2591 bool cursor_set = false;
2593 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2594 ArdourCanvas::Rectangle* resize_rect = (*i)->resize_rect;
2595 Note* canvas_note = (*i)->note;
2600 current_x = canvas_note->x0() + delta_x;
2602 current_x = primary->x0() + delta_x;
2606 current_x = canvas_note->x1() + delta_x;
2608 current_x = primary->x1() + delta_x;
2612 if (current_x < 0) {
2613 // This works even with snapping because RegionView::snap_frame_to_frame()
2614 // snaps forward if the snapped sample is before the beginning of the region
2617 if (current_x > trackview.editor().sample_to_pixel(_region->length())) {
2618 current_x = trackview.editor().sample_to_pixel(_region->length());
2622 resize_rect->set_x0 (snap_to_pixel(current_x));
2623 resize_rect->set_x1 (canvas_note->x1());
2625 resize_rect->set_x1 (snap_to_pixel(current_x));
2626 resize_rect->set_x0 (canvas_note->x0());
2632 beats = snap_pixel_to_sample (current_x);
2633 beats = region_frames_to_region_beats (beats);
2638 if (beats < canvas_note->note()->end_time()) {
2639 len = canvas_note->note()->time() - beats;
2640 len += canvas_note->note()->length();
2645 if (beats >= canvas_note->note()->time()) {
2646 len = beats - canvas_note->note()->time();
2653 snprintf (buf, sizeof (buf), "%.3g beats", len);
2654 show_verbose_cursor (buf, 0, 0);
2663 /** Finish resizing notes when the user releases the mouse button.
2664 * Parameters the same as for \a update_resizing().
2667 MidiRegionView::commit_resizing (NoteBase* primary, bool at_front, double delta_x, bool relative)
2669 start_note_diff_command (_("resize notes"));
2671 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2672 Note* canvas_note = (*i)->note;
2673 ArdourCanvas::Rectangle* resize_rect = (*i)->resize_rect;
2675 /* Get the new x position for this resize, which is in pixels relative
2676 * to the region position.
2683 current_x = canvas_note->x0() + delta_x;
2685 current_x = primary->x0() + delta_x;
2689 current_x = canvas_note->x1() + delta_x;
2691 current_x = primary->x1() + delta_x;
2695 if (current_x < 0) {
2698 if (current_x > trackview.editor().sample_to_pixel(_region->length())) {
2699 current_x = trackview.editor().sample_to_pixel(_region->length());
2702 /* Convert that to a frame within the source */
2703 current_x = snap_pixel_to_sample (current_x) + _region->start ();
2705 /* and then to beats */
2706 current_x = region_frames_to_region_beats (current_x);
2708 if (at_front && current_x < canvas_note->note()->end_time()) {
2709 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::StartTime, current_x);
2711 double len = canvas_note->note()->time() - current_x;
2712 len += canvas_note->note()->length();
2715 /* XXX convert to beats */
2716 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
2721 double len = current_x - canvas_note->note()->time();
2724 /* XXX convert to beats */
2725 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
2733 _resize_data.clear();
2738 MidiRegionView::abort_resizing ()
2740 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2741 delete (*i)->resize_rect;
2745 _resize_data.clear ();
2749 MidiRegionView::change_note_velocity(NoteBase* event, int8_t velocity, bool relative)
2751 uint8_t new_velocity;
2754 new_velocity = event->note()->velocity() + velocity;
2755 clamp_to_0_127(new_velocity);
2757 new_velocity = velocity;
2760 event->set_selected (event->selected()); // change color
2762 note_diff_add_change (event, MidiModel::NoteDiffCommand::Velocity, new_velocity);
2766 MidiRegionView::change_note_note (NoteBase* event, int8_t note, bool relative)
2771 new_note = event->note()->note() + note;
2776 clamp_to_0_127 (new_note);
2777 note_diff_add_change (event, MidiModel::NoteDiffCommand::NoteNumber, new_note);
2781 MidiRegionView::trim_note (NoteBase* event, Evoral::MusicalTime front_delta, Evoral::MusicalTime end_delta)
2783 bool change_start = false;
2784 bool change_length = false;
2785 Evoral::MusicalTime new_start = 0;
2786 Evoral::MusicalTime new_length = 0;
2788 /* NOTE: the semantics of the two delta arguments are slightly subtle:
2790 front_delta: if positive - move the start of the note later in time (shortening it)
2791 if negative - move the start of the note earlier in time (lengthening it)
2793 end_delta: if positive - move the end of the note later in time (lengthening it)
2794 if negative - move the end of the note earlier in time (shortening it)
2798 if (front_delta < 0) {
2800 if (event->note()->time() < -front_delta) {
2803 new_start = event->note()->time() + front_delta; // moves earlier
2806 /* start moved toward zero, so move the end point out to where it used to be.
2807 Note that front_delta is negative, so this increases the length.
2810 new_length = event->note()->length() - front_delta;
2811 change_start = true;
2812 change_length = true;
2816 Evoral::MusicalTime new_pos = event->note()->time() + front_delta;
2818 if (new_pos < event->note()->end_time()) {
2819 new_start = event->note()->time() + front_delta;
2820 /* start moved toward the end, so move the end point back to where it used to be */
2821 new_length = event->note()->length() - front_delta;
2822 change_start = true;
2823 change_length = true;
2830 bool can_change = true;
2831 if (end_delta < 0) {
2832 if (event->note()->length() < -end_delta) {
2838 new_length = event->note()->length() + end_delta;
2839 change_length = true;
2844 note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_start);
2847 if (change_length) {
2848 note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, new_length);
2853 MidiRegionView::change_note_channel (NoteBase* event, int8_t chn, bool relative)
2855 uint8_t new_channel;
2859 if (event->note()->channel() < -chn) {
2862 new_channel = event->note()->channel() + chn;
2865 new_channel = event->note()->channel() + chn;
2868 new_channel = (uint8_t) chn;
2871 note_diff_add_change (event, MidiModel::NoteDiffCommand::Channel, new_channel);
2875 MidiRegionView::change_note_time (NoteBase* event, Evoral::MusicalTime delta, bool relative)
2877 Evoral::MusicalTime new_time;
2881 if (event->note()->time() < -delta) {
2884 new_time = event->note()->time() + delta;
2887 new_time = event->note()->time() + delta;
2893 note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_time);
2897 MidiRegionView::change_note_length (NoteBase* event, Evoral::MusicalTime t)
2899 note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, t);
2903 MidiRegionView::change_velocities (bool up, bool fine, bool allow_smush, bool all_together)
2908 if (_selection.empty()) {
2923 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2924 if ((*i)->note()->velocity() < -delta || (*i)->note()->velocity() + delta > 127) {
2930 start_note_diff_command (_("change velocities"));
2932 for (Selection::iterator i = _selection.begin(); i != _selection.end();) {
2933 Selection::iterator next = i;
2937 if (i == _selection.begin()) {
2938 change_note_velocity (*i, delta, true);
2939 value = (*i)->note()->velocity() + delta;
2941 change_note_velocity (*i, value, false);
2945 change_note_velocity (*i, delta, true);
2954 if (!_selection.empty()) {
2956 snprintf (buf, sizeof (buf), "Vel %d",
2957 (int) (*_selection.begin())->note()->velocity());
2958 show_verbose_cursor (buf, 10, 10);
2964 MidiRegionView::transpose (bool up, bool fine, bool allow_smush)
2966 if (_selection.empty()) {
2983 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2985 if ((int8_t) (*i)->note()->note() + delta <= 0) {
2989 if ((int8_t) (*i)->note()->note() + delta > 127) {
2996 start_note_diff_command (_("transpose"));
2998 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2999 Selection::iterator next = i;
3001 change_note_note (*i, delta, true);
3009 MidiRegionView::change_note_lengths (bool fine, bool shorter, Evoral::MusicalTime delta, bool start, bool end)
3015 /* grab the current grid distance */
3017 delta = trackview.editor().get_grid_type_as_beats (success, _region->position());
3019 /* XXX cannot get grid type as beats ... should always be possible ... FIX ME */
3020 error << string_compose (_("programming error: %1"), "Grid type not available as beats - TO BE FIXED") << endmsg;
3030 start_note_diff_command (_("change note lengths"));
3032 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3033 Selection::iterator next = i;
3036 /* note the negation of the delta for start */
3038 trim_note (*i, (start ? -delta : 0), (end ? delta : 0));
3047 MidiRegionView::nudge_notes (bool forward)
3049 if (_selection.empty()) {
3053 /* pick a note as the point along the timeline to get the nudge distance.
3054 its not necessarily the earliest note, so we may want to pull the notes out
3055 into a vector and sort before using the first one.
3058 framepos_t ref_point = source_beats_to_absolute_frames ((*(_selection.begin()))->note()->time());
3060 framecnt_t distance;
3062 if (trackview.editor().snap_mode() == Editing::SnapOff) {
3064 /* grid is off - use nudge distance */
3066 distance = trackview.editor().get_nudge_distance (ref_point, unused);
3072 framepos_t next_pos = ref_point;
3075 if (max_framepos - 1 < next_pos) {
3079 if (next_pos == 0) {
3085 trackview.editor().snap_to (next_pos, (forward ? 1 : -1), false);
3086 distance = ref_point - next_pos;
3089 if (distance == 0) {
3093 Evoral::MusicalTime delta = region_frames_to_region_beats (fabs ((double)distance));
3099 start_note_diff_command (_("nudge"));
3101 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3102 Selection::iterator next = i;
3104 change_note_time (*i, delta, true);
3112 MidiRegionView::change_channel(uint8_t channel)
3114 start_note_diff_command(_("change channel"));
3115 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3116 note_diff_add_change (*i, MidiModel::NoteDiffCommand::Channel, channel);
3124 MidiRegionView::note_entered(NoteBase* ev)
3126 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3128 pre_note_enter_cursor = editor->get_canvas_cursor ();
3130 if (_mouse_state == SelectTouchDragging) {
3131 note_selected (ev, true);
3134 show_verbose_cursor (ev->note ());
3138 MidiRegionView::note_left (NoteBase*)
3140 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3142 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3143 (*i)->hide_velocity ();
3146 editor->verbose_cursor()->hide ();
3148 if (pre_note_enter_cursor) {
3149 editor->set_canvas_cursor (pre_note_enter_cursor);
3150 pre_note_enter_cursor = 0;
3155 MidiRegionView::patch_entered (PatchChange* p)
3158 /* XXX should get patch name if we can */
3159 s << _("Bank ") << (p->patch()->bank() + MIDI_BP_ZERO) << '\n'
3160 << _("Program ") << ((int) p->patch()->program()) + MIDI_BP_ZERO << '\n'
3161 << _("Channel ") << ((int) p->patch()->channel() + 1);
3162 show_verbose_cursor (s.str(), 10, 20);
3163 p->item().grab_focus();
3167 MidiRegionView::patch_left (PatchChange *)
3169 trackview.editor().verbose_cursor()->hide ();
3170 /* focus will transfer back via the enter-notify event sent to this
3176 MidiRegionView::sysex_entered (SysEx* p)
3180 // need a way to extract text from p->_flag->_text
3182 // show_verbose_cursor (s.str(), 10, 20);
3183 p->item().grab_focus();
3187 MidiRegionView::sysex_left (SysEx *)
3189 trackview.editor().verbose_cursor()->hide ();
3190 /* focus will transfer back via the enter-notify event sent to this
3196 MidiRegionView::note_mouse_position (float x_fraction, float /*y_fraction*/, bool can_set_cursor)
3198 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3199 Editing::MouseMode mm = editor->current_mouse_mode();
3200 bool trimmable = (mm == MouseObject || mm == MouseTimeFX || mm == MouseDraw);
3202 if (can_set_cursor) {
3203 if (trimmable && x_fraction > 0.0 && x_fraction < 0.2) {
3204 editor->set_canvas_cursor (editor->cursors()->left_side_trim);
3205 } else if (trimmable && x_fraction >= 0.8 && x_fraction < 1.0) {
3206 editor->set_canvas_cursor (editor->cursors()->right_side_trim);
3207 } else if (pre_note_enter_cursor) {
3208 editor->set_canvas_cursor (pre_note_enter_cursor);
3214 MidiRegionView::set_frame_color()
3218 TimeAxisViewItem::set_frame_color ();
3225 f = ARDOUR_UI::config()->get_SelectedFrameBase();
3226 } else if (high_enough_for_name) {
3227 f= ARDOUR_UI::config()->get_MidiFrameBase();
3232 if (!rect_visible) {
3233 f = UINT_RGBA_CHANGE_A (f, 80);
3236 frame->set_fill_color (f);
3240 MidiRegionView::midi_channel_mode_changed ()
3242 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3243 uint16_t mask = mtv->midi_track()->get_playback_channel_mask();
3244 ChannelMode mode = mtv->midi_track()->get_playback_channel_mode ();
3246 if (mode == ForceChannel) {
3247 mask = 0xFFFF; // Show all notes as active (below)
3250 // Update notes for selection
3251 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3252 (*i)->on_channel_selection_change (mask);
3255 _patch_changes.clear ();
3256 display_patch_changes ();
3260 MidiRegionView::instrument_settings_changed ()
3266 MidiRegionView::cut_copy_clear (Editing::CutCopyOp op)
3268 if (_selection.empty()) {
3272 PublicEditor& editor (trackview.editor());
3276 /* XXX what to do ? */
3280 editor.get_cut_buffer().add (selection_as_cut_buffer());
3288 start_note_diff_command();
3290 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3297 note_diff_remove_note (*i);
3307 MidiRegionView::selection_as_cut_buffer () const
3311 for (Selection::const_iterator i = _selection.begin(); i != _selection.end(); ++i) {
3312 NoteType* n = (*i)->note().get();
3313 notes.insert (boost::shared_ptr<NoteType> (new NoteType (*n)));
3316 MidiCutBuffer* cb = new MidiCutBuffer (trackview.session());
3322 /** This method handles undo */
3324 MidiRegionView::paste (framepos_t pos, unsigned paste_count, float times, const MidiCutBuffer& mcb)
3330 PublicEditor& editor = trackview.editor ();
3332 trackview.session()->begin_reversible_command (_("paste"));
3334 start_note_diff_command (_("paste"));
3336 /* get snap duration, default to 1 beat if not snapped to anything musical */
3337 bool success = true;
3338 double snap_beats = editor.get_grid_type_as_beats(success, pos);
3343 const Evoral::MusicalTime first_time = (*mcb.notes().begin())->time();
3344 const Evoral::MusicalTime last_time = (*mcb.notes().rbegin())->end_time();
3345 const Evoral::MusicalTime duration = last_time - first_time;
3346 const Evoral::MusicalTime snap_duration = ceil(duration / snap_beats) * snap_beats;
3347 const Evoral::MusicalTime paste_offset = paste_count * snap_duration;
3348 const Evoral::MusicalTime pos_beats = absolute_frames_to_source_beats(pos) + paste_offset;
3349 Evoral::MusicalTime end_point = 0;
3351 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste data spans from %1 to %2 (%3) ; paste pos beats = %4 (based on %5 - %6)\n",
3354 duration, pos, _region->position(),
3359 for (int n = 0; n < (int) times; ++n) {
3361 for (Notes::const_iterator i = mcb.notes().begin(); i != mcb.notes().end(); ++i) {
3363 boost::shared_ptr<NoteType> copied_note (new NoteType (*((*i).get())));
3364 copied_note->set_time (pos_beats + copied_note->time() - first_time);
3366 /* make all newly added notes selected */
3368 note_diff_add_note (copied_note, true);
3369 end_point = copied_note->end_time();
3373 /* if we pasted past the current end of the region, extend the region */
3375 framepos_t end_frame = source_beats_to_absolute_frames (end_point);
3376 framepos_t region_end = _region->position() + _region->length() - 1;
3378 if (end_frame > region_end) {
3380 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste extended region from %1 to %2\n", region_end, end_frame));
3382 _region->clear_changes ();
3383 _region->set_length (end_frame - _region->position());
3384 trackview.session()->add_command (new StatefulDiffCommand (_region));
3389 trackview.session()->commit_reversible_command ();
3392 struct EventNoteTimeEarlyFirstComparator {
3393 bool operator() (NoteBase* a, NoteBase* b) {
3394 return a->note()->time() < b->note()->time();
3399 MidiRegionView::time_sort_events ()
3401 if (!_sort_needed) {
3405 EventNoteTimeEarlyFirstComparator cmp;
3408 _sort_needed = false;
3412 MidiRegionView::goto_next_note (bool add_to_selection)
3414 bool use_next = false;
3416 if (_events.back()->selected()) {
3420 time_sort_events ();
3422 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3423 uint16_t const channel_mask = mtv->midi_track()->get_playback_channel_mask();
3425 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3426 if ((*i)->selected()) {
3429 } else if (use_next) {
3430 if (channel_mask & (1 << (*i)->note()->channel())) {
3431 if (!add_to_selection) {
3434 note_selected (*i, true, false);
3441 /* use the first one */
3443 if (!_events.empty() && (channel_mask & (1 << _events.front()->note()->channel ()))) {
3444 unique_select (_events.front());
3449 MidiRegionView::goto_previous_note (bool add_to_selection)
3451 bool use_next = false;
3453 if (_events.front()->selected()) {
3457 time_sort_events ();
3459 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3460 uint16_t const channel_mask = mtv->midi_track()->get_playback_channel_mask ();
3462 for (Events::reverse_iterator i = _events.rbegin(); i != _events.rend(); ++i) {
3463 if ((*i)->selected()) {
3466 } else if (use_next) {
3467 if (channel_mask & (1 << (*i)->note()->channel())) {
3468 if (!add_to_selection) {
3471 note_selected (*i, true, false);
3478 /* use the last one */
3480 if (!_events.empty() && (channel_mask & (1 << (*_events.rbegin())->note()->channel ()))) {
3481 unique_select (*(_events.rbegin()));
3486 MidiRegionView::selection_as_notelist (Notes& selected, bool allow_all_if_none_selected)
3488 bool had_selected = false;
3490 time_sort_events ();
3492 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3493 if ((*i)->selected()) {
3494 selected.insert ((*i)->note());
3495 had_selected = true;
3499 if (allow_all_if_none_selected && !had_selected) {
3500 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3501 selected.insert ((*i)->note());
3507 MidiRegionView::update_ghost_note (double x, double y)
3509 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3514 _note_group->canvas_to_item (x, y);
3516 PublicEditor& editor = trackview.editor ();
3518 framepos_t const unsnapped_frame = editor.pixel_to_sample (x);
3519 framecnt_t grid_frames;
3520 framepos_t const f = snap_frame_to_grid_underneath (unsnapped_frame, grid_frames);
3522 /* use region_frames... because we are converting a delta within the region
3526 double length = editor.get_grid_type_as_beats (success, unsnapped_frame);
3532 /* note that this sets the time of the ghost note in beats relative to
3533 the start of the source; that is how all note times are stored.
3535 _ghost_note->note()->set_time (
3536 std::max(0.0, absolute_frames_to_source_beats (f + _region->position ())));
3537 _ghost_note->note()->set_length (length);
3538 _ghost_note->note()->set_note (midi_stream_view()->y_to_note (y));
3539 _ghost_note->note()->set_channel (mtv->get_channel_for_add ());
3541 /* the ghost note does not appear in ghost regions, so pass false in here */
3542 update_note (_ghost_note, false);
3544 show_verbose_cursor (_ghost_note->note ());
3548 MidiRegionView::create_ghost_note (double x, double y)
3550 remove_ghost_note ();
3552 boost::shared_ptr<NoteType> g (new NoteType);
3553 _ghost_note = new Note (*this, _note_group, g);
3554 _ghost_note->set_ignore_events (true);
3555 _ghost_note->set_outline_color (0x000000aa);
3556 if (x < 0) { x = 0; }
3557 update_ghost_note (x, y);
3558 _ghost_note->show ();
3563 show_verbose_cursor (_ghost_note->note ());
3567 MidiRegionView::remove_ghost_note ()
3574 MidiRegionView::snap_changed ()
3580 create_ghost_note (_last_ghost_x, _last_ghost_y);
3584 MidiRegionView::drop_down_keys ()
3586 _mouse_state = None;
3590 MidiRegionView::maybe_select_by_position (GdkEventButton* ev, double /*x*/, double y)
3592 double note = midi_stream_view()->y_to_note(y);
3594 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3596 uint16_t chn_mask = mtv->midi_track()->get_playback_channel_mask();
3598 if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
3599 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchGreaterThanOrEqual, (uint8_t) floor (note), chn_mask);
3600 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
3601 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchLessThanOrEqual, (uint8_t) floor (note), chn_mask);
3606 bool add_mrv_selection = false;
3608 if (_selection.empty()) {
3609 add_mrv_selection = true;
3612 for (Events::iterator i = e.begin(); i != e.end(); ++i) {
3613 if (_selection.insert (*i).second) {
3614 (*i)->set_selected (true);
3618 if (add_mrv_selection) {
3619 PublicEditor& editor (trackview.editor());
3620 editor.get_selection().add (this);
3625 MidiRegionView::color_handler ()
3627 RegionView::color_handler ();
3629 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3630 (*i)->set_selected ((*i)->selected()); // will change color
3633 /* XXX probably more to do here */
3637 MidiRegionView::enable_display (bool yn)
3639 RegionView::enable_display (yn);
3646 MidiRegionView::show_step_edit_cursor (Evoral::MusicalTime pos)
3648 if (_step_edit_cursor == 0) {
3649 ArdourCanvas::Item* const group = get_canvas_group();
3651 _step_edit_cursor = new ArdourCanvas::Rectangle (group);
3652 _step_edit_cursor->set_y0 (0);
3653 _step_edit_cursor->set_y1 (midi_stream_view()->contents_height());
3654 _step_edit_cursor->set_fill_color (RGBA_TO_UINT (45,0,0,90));
3655 _step_edit_cursor->set_outline_color (RGBA_TO_UINT (85,0,0,90));
3658 move_step_edit_cursor (pos);
3659 _step_edit_cursor->show ();
3663 MidiRegionView::move_step_edit_cursor (Evoral::MusicalTime pos)
3665 _step_edit_cursor_position = pos;
3667 if (_step_edit_cursor) {
3668 double pixel = trackview.editor().sample_to_pixel (region_beats_to_region_frames (pos));
3669 _step_edit_cursor->set_x0 (pixel);
3670 set_step_edit_cursor_width (_step_edit_cursor_width);
3675 MidiRegionView::hide_step_edit_cursor ()
3677 if (_step_edit_cursor) {
3678 _step_edit_cursor->hide ();
3683 MidiRegionView::set_step_edit_cursor_width (Evoral::MusicalTime beats)
3685 _step_edit_cursor_width = beats;
3687 if (_step_edit_cursor) {
3688 _step_edit_cursor->set_x1 (_step_edit_cursor->x0() + trackview.editor().sample_to_pixel (region_beats_to_region_frames (beats)));
3692 /** Called when a diskstream on our track has received some data. Update the view, if applicable.
3693 * @param w Source that the data will end up in.
3696 MidiRegionView::data_recorded (boost::weak_ptr<MidiSource> w)
3698 if (!_active_notes) {
3699 /* we aren't actively being recorded to */
3703 boost::shared_ptr<MidiSource> src = w.lock ();
3704 if (!src || src != midi_region()->midi_source()) {
3705 /* recorded data was not destined for our source */
3709 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (&trackview);
3711 boost::shared_ptr<MidiBuffer> buf = mtv->midi_track()->get_gui_feed_buffer ();
3713 BeatsFramesConverter converter (trackview.session()->tempo_map(), mtv->midi_track()->get_capture_start_frame (0));
3715 framepos_t back = max_framepos;
3717 for (MidiBuffer::iterator i = buf->begin(); i != buf->end(); ++i) {
3718 Evoral::MIDIEvent<MidiBuffer::TimeType> const ev (*i, false);
3720 if (ev.is_channel_event()) {
3721 if (get_channel_mode() == FilterChannels) {
3722 if (((uint16_t(1) << ev.channel()) & get_selected_channels()) == 0) {
3728 /* ev.time() is in session frames, so (ev.time() - converter.origin_b()) is
3729 frames from the start of the source, and so time_beats is in terms of the
3733 Evoral::MusicalTime const time_beats = converter.from (ev.time () - converter.origin_b ());
3735 if (ev.type() == MIDI_CMD_NOTE_ON) {
3736 boost::shared_ptr<NoteType> note (
3737 new NoteType (ev.channel(), time_beats, 0, ev.note(), ev.velocity()));
3739 add_note (note, true);
3741 /* fix up our note range */
3742 if (ev.note() < _current_range_min) {
3743 midi_stream_view()->apply_note_range (ev.note(), _current_range_max, true);
3744 } else if (ev.note() > _current_range_max) {
3745 midi_stream_view()->apply_note_range (_current_range_min, ev.note(), true);
3748 } else if (ev.type() == MIDI_CMD_NOTE_OFF) {
3749 resolve_note (ev.note (), time_beats);
3755 midi_stream_view()->check_record_layers (region(), back);
3759 MidiRegionView::trim_front_starting ()
3761 /* Reparent the note group to the region view's parent, so that it doesn't change
3762 when the region view is trimmed.
3764 _temporary_note_group = new ArdourCanvas::Container (group->parent ());
3765 _temporary_note_group->move (group->position ());
3766 _note_group->reparent (_temporary_note_group);
3770 MidiRegionView::trim_front_ending ()
3772 _note_group->reparent (group);
3773 delete _temporary_note_group;
3774 _temporary_note_group = 0;
3776 if (_region->start() < 0) {
3777 /* Trim drag made start time -ve; fix this */
3778 midi_region()->fix_negative_start ();
3783 MidiRegionView::edit_patch_change (PatchChange* pc)
3785 PatchChangeDialog d (&_source_relative_time_converter, trackview.session(), *pc->patch (), instrument_info(), Gtk::Stock::APPLY, true);
3787 int response = d.run();
3790 case Gtk::RESPONSE_ACCEPT:
3792 case Gtk::RESPONSE_REJECT:
3793 delete_patch_change (pc);
3799 change_patch_change (pc->patch(), d.patch ());
3803 MidiRegionView::delete_sysex (SysEx* /*sysex*/)
3806 // sysyex object doesn't have a pointer to a sysex event
3807 // MidiModel::SysExDiffCommand* c = _model->new_sysex_diff_command (_("delete sysex"));
3808 // c->remove (sysex->sysex());
3809 // _model->apply_command (*trackview.session(), c);
3811 //_sys_exes.clear ();
3812 // display_sysexes();
3816 MidiRegionView::show_verbose_cursor (boost::shared_ptr<NoteType> n) const
3818 using namespace MIDI::Name;
3822 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3824 boost::shared_ptr<MasterDeviceNames> device_names(mtv->get_device_names());
3826 MIDI::Name::PatchPrimaryKey patch_key;
3827 get_patch_key_at(n->time(), n->channel(), patch_key);
3828 name = device_names->note_name(mtv->gui_property(X_("midnam-custom-device-mode")),
3830 patch_key.bank_number,
3831 patch_key.program_number,
3837 snprintf (buf, sizeof (buf), "%d %s\nCh %d Vel %d",
3839 name.empty() ? Evoral::midi_note_name (n->note()).c_str() : name.c_str(),
3840 (int) n->channel() + 1,
3841 (int) n->velocity());
3843 show_verbose_cursor(buf, 10, 20);
3847 MidiRegionView::show_verbose_cursor (string const & text, double xoffset, double yoffset) const
3849 trackview.editor().verbose_cursor()->set (text);
3850 trackview.editor().verbose_cursor()->show ();
3851 trackview.editor().verbose_cursor()->set_offset (ArdourCanvas::Duple (xoffset, yoffset));
3854 /** @param p A session framepos.
3855 * @param grid_frames Filled in with the number of frames that a grid interval is at p.
3856 * @return p snapped to the grid subdivision underneath it.
3859 MidiRegionView::snap_frame_to_grid_underneath (framepos_t p, framecnt_t& grid_frames) const
3861 PublicEditor& editor = trackview.editor ();
3864 Evoral::MusicalTime grid_beats = editor.get_grid_type_as_beats (success, p);
3870 grid_frames = region_beats_to_region_frames (grid_beats);
3872 /* Hack so that we always snap to the note that we are over, instead of snapping
3873 to the next one if we're more than halfway through the one we're over.
3875 if (editor.snap_mode() == SnapNormal && p >= grid_frames / 2) {
3876 p -= grid_frames / 2;
3879 return snap_frame_to_frame (p);
3882 /** Called when the selection has been cleared in any MidiRegionView.
3883 * @param rv MidiRegionView that the selection was cleared in.
3886 MidiRegionView::selection_cleared (MidiRegionView* rv)
3892 /* Clear our selection in sympathy; but don't signal the fact */
3893 clear_selection (false);
3897 MidiRegionView::note_button_release ()
3899 delete _note_player;
3904 MidiRegionView::get_channel_mode () const
3906 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (&trackview);
3907 return rtav->midi_track()->get_playback_channel_mode();
3911 MidiRegionView::get_selected_channels () const
3913 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (&trackview);
3914 return rtav->midi_track()->get_playback_channel_mask();