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)
117 CANVAS_DEBUG_NAME (_note_group, string_compose ("note group for %1", get_item_name()));
118 _note_group->raise_to_top();
119 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
121 Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&MidiRegionView::parameter_changed, this, _1), gui_context());
122 connect_to_diskstream ();
124 SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), boost::bind (&MidiRegionView::selection_cleared, this, _1), gui_context ());
127 MidiRegionView::MidiRegionView (ArdourCanvas::Container *parent, RouteTimeAxisView &tv,
128 boost::shared_ptr<MidiRegion> r, double spu, uint32_t basic_color,
129 TimeAxisViewItem::Visibility visibility)
130 : RegionView (parent, tv, r, spu, basic_color, false, visibility)
131 , _current_range_min(0)
132 , _current_range_max(0)
134 , _note_group (new ArdourCanvas::Container (parent))
135 , _note_diff_command (0)
137 , _step_edit_cursor (0)
138 , _step_edit_cursor_width (1.0)
139 , _step_edit_cursor_position (0.0)
140 , _channel_selection_scoped_note (0)
141 , _temporary_note_group (0)
144 , _sort_needed (true)
145 , _optimization_iterator (_events.end())
147 , _no_sound_notes (false)
150 , pre_enter_cursor (0)
151 , pre_press_cursor (0)
154 CANVAS_DEBUG_NAME (_note_group, string_compose ("note group for %1", get_item_name()));
155 _note_group->raise_to_top();
157 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
159 connect_to_diskstream ();
161 SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), boost::bind (&MidiRegionView::selection_cleared, this, _1), gui_context ());
165 MidiRegionView::parameter_changed (std::string const & p)
167 if (p == "display-first-midi-bank-as-zero") {
168 if (_enable_display) {
174 MidiRegionView::MidiRegionView (const MidiRegionView& other)
175 : sigc::trackable(other)
177 , _current_range_min(0)
178 , _current_range_max(0)
180 , _note_group (new ArdourCanvas::Container (get_canvas_group()))
181 , _note_diff_command (0)
183 , _step_edit_cursor (0)
184 , _step_edit_cursor_width (1.0)
185 , _step_edit_cursor_position (0.0)
186 , _channel_selection_scoped_note (0)
187 , _temporary_note_group (0)
190 , _sort_needed (true)
191 , _optimization_iterator (_events.end())
193 , _no_sound_notes (false)
196 , pre_enter_cursor (0)
197 , pre_press_cursor (0)
203 MidiRegionView::MidiRegionView (const MidiRegionView& other, boost::shared_ptr<MidiRegion> region)
204 : RegionView (other, boost::shared_ptr<Region> (region))
205 , _current_range_min(0)
206 , _current_range_max(0)
208 , _note_group (new ArdourCanvas::Container (get_canvas_group()))
209 , _note_diff_command (0)
211 , _step_edit_cursor (0)
212 , _step_edit_cursor_width (1.0)
213 , _step_edit_cursor_position (0.0)
214 , _channel_selection_scoped_note (0)
215 , _temporary_note_group (0)
218 , _sort_needed (true)
219 , _optimization_iterator (_events.end())
221 , _no_sound_notes (false)
224 , pre_enter_cursor (0)
225 , pre_press_cursor (0)
232 MidiRegionView::init (bool wfd)
234 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
236 NoteBase::NoteBaseDeleted.connect (note_delete_connection, MISSING_INVALIDATOR,
237 boost::bind (&MidiRegionView::maybe_remove_deleted_note_from_selection, this, _1),
241 midi_region()->midi_source(0)->load_model();
244 _model = midi_region()->midi_source(0)->model();
245 _enable_display = false;
247 RegionView::init (false);
249 set_height (trackview.current_height());
252 region_sync_changed ();
253 region_resized (ARDOUR::bounds_change);
258 _enable_display = true;
261 display_model (_model);
265 reset_width_dependent_items (_pixel_width);
267 group->raise_to_top();
269 midi_view()->midi_track()->PlaybackChannelModeChanged.connect (_channel_mode_changed_connection, invalidator (*this),
270 boost::bind (&MidiRegionView::midi_channel_mode_changed, this),
273 instrument_info().Changed.connect (_instrument_changed_connection, invalidator (*this),
274 boost::bind (&MidiRegionView::instrument_settings_changed, this), gui_context());
276 trackview.editor().SnapChanged.connect(snap_changed_connection, invalidator(*this),
277 boost::bind (&MidiRegionView::snap_changed, this),
280 Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&MidiRegionView::parameter_changed, this, _1), gui_context());
281 connect_to_diskstream ();
283 SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), boost::bind (&MidiRegionView::selection_cleared, this, _1), gui_context ());
287 MidiRegionView::instrument_info () const
289 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
290 return route_ui->route()->instrument_info();
293 const boost::shared_ptr<ARDOUR::MidiRegion>
294 MidiRegionView::midi_region() const
296 return boost::dynamic_pointer_cast<ARDOUR::MidiRegion>(_region);
300 MidiRegionView::connect_to_diskstream ()
302 midi_view()->midi_track()->DataRecorded.connect(
303 *this, invalidator(*this),
304 boost::bind (&MidiRegionView::data_recorded, this, _1),
309 MidiRegionView::canvas_group_event(GdkEvent* ev)
314 case GDK_ENTER_NOTIFY:
315 case GDK_LEAVE_NOTIFY:
316 _last_event_x = ev->crossing.x;
317 _last_event_y = ev->crossing.y;
319 case GDK_MOTION_NOTIFY:
320 _last_event_x = ev->motion.x;
321 _last_event_y = ev->motion.y;
327 if (ev->type == GDK_2BUTTON_PRESS) {
328 // cannot use double-click to exit internal mode if single-click is being used
329 MouseMode m = trackview.editor().current_mouse_mode();
331 if ((m != MouseObject || !Keyboard::modifier_state_contains (ev->button.state, Keyboard::insert_note_modifier())) && (m != MouseDraw)) {
332 return trackview.editor().toggle_internal_editing_from_double_click (ev);
336 if ((!trackview.editor().internal_editing() && trackview.editor().current_mouse_mode() != MouseGain) ||
337 (trackview.editor().current_mouse_mode() == MouseTimeFX) ||
338 (trackview.editor().current_mouse_mode() == MouseZoom)) {
339 // handle non-internal-edit/non-draw modes elsewhere
340 return RegionView::canvas_group_event (ev);
345 return scroll (&ev->scroll);
348 return key_press (&ev->key);
350 case GDK_KEY_RELEASE:
351 return key_release (&ev->key);
353 case GDK_BUTTON_PRESS:
354 return button_press (&ev->button);
356 case GDK_BUTTON_RELEASE:
357 r = button_release (&ev->button);
362 case GDK_ENTER_NOTIFY:
363 return enter_notify (&ev->crossing);
365 case GDK_LEAVE_NOTIFY:
366 return leave_notify (&ev->crossing);
368 case GDK_MOTION_NOTIFY:
369 return motion (&ev->motion);
375 return trackview.editor().canvas_region_view_event (ev, group, this);
379 MidiRegionView::enter_notify (GdkEventCrossing* ev)
381 trackview.editor().MouseModeChanged.connect (
382 _mouse_mode_connection, invalidator (*this), boost::bind (&MidiRegionView::mouse_mode_changed, this), gui_context ()
385 if (trackview.editor().current_mouse_mode() == MouseDraw && _mouse_state != AddDragging) {
386 create_ghost_note (ev->x, ev->y);
389 if (!trackview.editor().internal_editing()) {
390 Keyboard::magic_widget_drop_focus();
392 Keyboard::magic_widget_grab_focus();
396 // if current operation is non-operational in a midi region, change the cursor to so indicate
397 if (trackview.editor().current_mouse_mode() == MouseGain) {
398 Editor* editor = dynamic_cast<Editor *> (&trackview.editor());
399 pre_enter_cursor = editor->get_canvas_cursor();
400 editor->set_canvas_cursor(editor->cursors()->timebar);
407 MidiRegionView::leave_notify (GdkEventCrossing*)
409 _mouse_mode_connection.disconnect ();
411 trackview.editor().verbose_cursor()->hide ();
412 remove_ghost_note ();
414 if (pre_enter_cursor) {
415 Editor* editor = dynamic_cast<Editor *> (&trackview.editor());
416 editor->set_canvas_cursor(pre_enter_cursor);
423 MidiRegionView::mouse_mode_changed ()
425 if (trackview.editor().current_mouse_mode() == MouseDraw && trackview.editor().internal_editing()) {
426 create_ghost_note (_last_event_x, _last_event_y);
428 remove_ghost_note ();
429 trackview.editor().verbose_cursor()->hide ();
432 if (!trackview.editor().internal_editing()) {
433 Keyboard::magic_widget_drop_focus();
435 Keyboard::magic_widget_grab_focus();
441 MidiRegionView::button_press (GdkEventButton* ev)
443 if (ev->button != 1) {
447 Editor* editor = dynamic_cast<Editor *> (&trackview.editor());
448 MouseMode m = editor->current_mouse_mode();
450 if (m == MouseObject && Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())) {
451 pre_press_cursor = editor->get_canvas_cursor ();
452 editor->set_canvas_cursor (editor->cursors()->midi_pencil);
455 if (_mouse_state != SelectTouchDragging) {
457 _pressed_button = ev->button;
458 _mouse_state = Pressed;
463 _pressed_button = ev->button;
469 MidiRegionView::button_release (GdkEventButton* ev)
471 double event_x, event_y;
473 if (ev->button != 1) {
480 group->canvas_to_item (event_x, event_y);
483 PublicEditor& editor = trackview.editor ();
485 if (pre_press_cursor) {
486 dynamic_cast<Editor*>(&editor)->set_canvas_cursor (pre_press_cursor, false);
487 pre_press_cursor = 0;
490 switch (_mouse_state) {
491 case Pressed: // Clicked
493 switch (editor.current_mouse_mode()) {
495 /* no motion occured - simple click */
504 if (Keyboard::is_insert_note_event(ev)) {
506 double event_x, event_y;
510 group->canvas_to_item (event_x, event_y);
513 Evoral::MusicalTime beats = editor.get_grid_type_as_beats (success, editor.pixel_to_sample (event_x));
519 /* Shorten the length by 1 tick so that we can add a new note at the next
520 grid snap without it overlapping this one.
522 beats -= 1.0 / Timecode::BBT_Time::ticks_per_beat;
524 create_note_at (editor.pixel_to_sample (event_x), event_y, beats, true);
532 Evoral::MusicalTime beats = editor.get_grid_type_as_beats (success, editor.pixel_to_sample (event_x));
538 /* Shorten the length by 1 tick so that we can add a new note at the next
539 grid snap without it overlapping this one.
541 beats -= 1.0 / Timecode::BBT_Time::ticks_per_beat;
543 create_note_at (editor.pixel_to_sample (event_x), event_y, beats, true);
554 case SelectRectDragging:
556 editor.drags()->end_grab ((GdkEvent *) ev);
558 create_ghost_note (ev->x, ev->y);
570 MidiRegionView::motion (GdkEventMotion* ev)
572 PublicEditor& editor = trackview.editor ();
574 if (!_ghost_note && editor.current_mouse_mode() == MouseObject &&
575 Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier()) &&
576 _mouse_state != AddDragging) {
578 create_ghost_note (ev->x, ev->y);
580 } else if (_ghost_note && editor.current_mouse_mode() == MouseObject &&
581 Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())) {
583 update_ghost_note (ev->x, ev->y);
585 } else if (_ghost_note && editor.current_mouse_mode() == MouseObject) {
587 remove_ghost_note ();
588 editor.verbose_cursor()->hide ();
590 } else if (_ghost_note && editor.current_mouse_mode() == MouseDraw) {
592 update_ghost_note (ev->x, ev->y);
595 /* any motion immediately hides velocity text that may have been visible */
597 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
598 (*i)->hide_velocity ();
601 switch (_mouse_state) {
604 if (_pressed_button == 1) {
606 MouseMode m = editor.current_mouse_mode();
608 if (m == MouseDraw || (m == MouseObject && Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier()))) {
609 editor.drags()->set (new NoteCreateDrag (dynamic_cast<Editor *> (&editor), group, this), (GdkEvent *) ev);
610 _mouse_state = AddDragging;
611 remove_ghost_note ();
612 editor.verbose_cursor()->hide ();
614 } else if (m == MouseObject) {
615 editor.drags()->set (new MidiRubberbandSelectDrag (dynamic_cast<Editor *> (&editor), this), (GdkEvent *) ev);
617 _mouse_state = SelectRectDragging;
619 } else if (m == MouseRange) {
620 editor.drags()->set (new MidiVerticalSelectDrag (dynamic_cast<Editor *> (&editor), this), (GdkEvent *) ev);
621 _mouse_state = SelectVerticalDragging;
628 case SelectRectDragging:
629 case SelectVerticalDragging:
631 editor.drags()->motion_handler ((GdkEvent *) ev, false);
634 case SelectTouchDragging:
642 /* we may be dragging some non-note object (eg. patch-change, sysex)
645 return editor.drags()->motion_handler ((GdkEvent *) ev, false);
650 MidiRegionView::scroll (GdkEventScroll* ev)
652 if (_selection.empty()) {
656 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
657 /* XXX: bit of a hack; allow PrimaryModifier scroll through so that
658 it still works for zoom.
663 trackview.editor().verbose_cursor()->hide ();
665 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
666 bool together = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
668 if (ev->direction == GDK_SCROLL_UP) {
669 change_velocities (true, fine, false, together);
670 } else if (ev->direction == GDK_SCROLL_DOWN) {
671 change_velocities (false, fine, false, together);
673 /* left, right: we don't use them */
681 MidiRegionView::key_press (GdkEventKey* ev)
683 /* since GTK bindings are generally activated on press, and since
684 detectable auto-repeat is the name of the game and only sends
685 repeated presses, carry out key actions at key press, not release.
688 bool unmodified = Keyboard::no_modifier_keys_pressed (ev);
690 if (unmodified && (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R)) {
691 _mouse_state = SelectTouchDragging;
694 } else if (ev->keyval == GDK_Escape && unmodified) {
698 } else if (unmodified && (ev->keyval == GDK_comma || ev->keyval == GDK_period)) {
700 bool start = (ev->keyval == GDK_comma);
701 bool end = (ev->keyval == GDK_period);
702 bool shorter = Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier);
703 bool fine = Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
705 change_note_lengths (fine, shorter, 0.0, start, end);
709 } else if ((ev->keyval == GDK_BackSpace || ev->keyval == GDK_Delete) && unmodified) {
711 if (_selection.empty()) {
718 } else if (ev->keyval == GDK_Tab) {
720 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
721 goto_previous_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
723 goto_next_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
727 } else if (ev->keyval == GDK_ISO_Left_Tab) {
729 /* Shift-TAB generates ISO Left Tab, for some reason */
731 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
732 goto_previous_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
734 goto_next_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
740 } else if (ev->keyval == GDK_Up) {
742 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
743 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
744 bool together = Keyboard::modifier_state_contains (ev->state, Keyboard::Level4Modifier);
746 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
747 change_velocities (true, fine, allow_smush, together);
749 transpose (true, fine, allow_smush);
753 } else if (ev->keyval == GDK_Down) {
755 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
756 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
757 bool together = Keyboard::modifier_state_contains (ev->state, Keyboard::Level4Modifier);
759 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
760 change_velocities (false, fine, allow_smush, together);
762 transpose (false, fine, allow_smush);
766 } else if (ev->keyval == GDK_Left && unmodified) {
771 } else if (ev->keyval == GDK_Right && unmodified) {
776 } else if (ev->keyval == GDK_c && unmodified) {
780 } else if (ev->keyval == GDK_v && unmodified) {
789 MidiRegionView::key_release (GdkEventKey* ev)
791 if ((_mouse_state == SelectTouchDragging) && (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R)) {
799 MidiRegionView::channel_edit ()
801 if (_selection.empty()) {
805 /* pick a note somewhat at random (since Selection is a set<>) to
806 * provide the "current" channel for the dialog.
809 uint8_t current_channel = (*_selection.begin())->note()->channel ();
810 MidiChannelDialog channel_dialog (current_channel);
811 int ret = channel_dialog.run ();
814 case Gtk::RESPONSE_OK:
820 uint8_t new_channel = channel_dialog.active_channel ();
822 start_note_diff_command (_("channel edit"));
824 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
825 Selection::iterator next = i;
827 change_note_channel (*i, new_channel);
835 MidiRegionView::velocity_edit ()
837 if (_selection.empty()) {
841 /* pick a note somewhat at random (since Selection is a set<>) to
842 * provide the "current" velocity for the dialog.
845 uint8_t current_velocity = (*_selection.begin())->note()->velocity ();
846 MidiVelocityDialog velocity_dialog (current_velocity);
847 int ret = velocity_dialog.run ();
850 case Gtk::RESPONSE_OK:
856 uint8_t new_velocity = velocity_dialog.velocity ();
858 start_note_diff_command (_("velocity edit"));
860 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
861 Selection::iterator next = i;
863 change_note_velocity (*i, new_velocity, false);
871 MidiRegionView::show_list_editor ()
874 _list_editor = new MidiListEditor (trackview.session(), midi_region(), midi_view()->midi_track());
876 _list_editor->present ();
879 /** Add a note to the model, and the view, at a canvas (click) coordinate.
880 * \param t time in frames relative to the position of the region
881 * \param y vertical position in pixels
882 * \param length duration of the note in beats
883 * \param snap_t true to snap t to the grid, otherwise false.
886 MidiRegionView::create_note_at (framepos_t t, double y, double length, bool snap_t)
888 if (length < 2 * DBL_EPSILON) {
892 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
893 MidiStreamView* const view = mtv->midi_view();
895 const double note = view->y_to_note(y);
897 // Start of note in frames relative to region start
899 framecnt_t grid_frames;
900 t = snap_frame_to_grid_underneath (t, grid_frames);
903 const boost::shared_ptr<NoteType> new_note (
904 new NoteType (mtv->get_channel_for_add (),
905 region_frames_to_region_beats(t + _region->start()),
907 (uint8_t)note, 0x40));
909 if (_model->contains (new_note)) {
913 view->update_note_range(new_note->note());
915 MidiModel::NoteDiffCommand* cmd = _model->new_note_diff_command(_("add note"));
917 _model->apply_command(*trackview.session(), cmd);
919 play_midi_note (new_note);
923 MidiRegionView::clear_events (bool with_selection_signal)
925 clear_selection (with_selection_signal);
928 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
929 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
934 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
939 _patch_changes.clear();
941 _optimization_iterator = _events.end();
945 MidiRegionView::display_model(boost::shared_ptr<MidiModel> model)
949 content_connection.disconnect ();
950 _model->ContentsChanged.connect (content_connection, invalidator (*this), boost::bind (&MidiRegionView::redisplay_model, this), gui_context());
954 if (_enable_display) {
960 MidiRegionView::start_note_diff_command (string name)
962 if (!_note_diff_command) {
963 _note_diff_command = _model->new_note_diff_command (name);
968 MidiRegionView::note_diff_add_note (const boost::shared_ptr<NoteType> note, bool selected, bool show_velocity)
970 if (_note_diff_command) {
971 _note_diff_command->add (note);
974 _marked_for_selection.insert(note);
977 _marked_for_velocity.insert(note);
982 MidiRegionView::note_diff_remove_note (NoteBase* ev)
984 if (_note_diff_command && ev->note()) {
985 _note_diff_command->remove(ev->note());
990 MidiRegionView::note_diff_add_change (NoteBase* ev,
991 MidiModel::NoteDiffCommand::Property property,
994 if (_note_diff_command) {
995 _note_diff_command->change (ev->note(), property, val);
1000 MidiRegionView::note_diff_add_change (NoteBase* ev,
1001 MidiModel::NoteDiffCommand::Property property,
1002 Evoral::MusicalTime val)
1004 if (_note_diff_command) {
1005 _note_diff_command->change (ev->note(), property, val);
1010 MidiRegionView::apply_diff (bool as_subcommand)
1014 if (!_note_diff_command) {
1018 if ((add_or_remove = _note_diff_command->adds_or_removes())) {
1019 // Mark all selected notes for selection when model reloads
1020 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1021 _marked_for_selection.insert((*i)->note());
1025 if (as_subcommand) {
1026 _model->apply_command_as_subcommand (*trackview.session(), _note_diff_command);
1028 _model->apply_command (*trackview.session(), _note_diff_command);
1031 _note_diff_command = 0;
1032 midi_view()->midi_track()->playlist_modified();
1034 if (add_or_remove) {
1035 _marked_for_selection.clear();
1038 _marked_for_velocity.clear();
1042 MidiRegionView::abort_command()
1044 delete _note_diff_command;
1045 _note_diff_command = 0;
1050 MidiRegionView::find_canvas_note (boost::shared_ptr<NoteType> note)
1052 if (_optimization_iterator != _events.end()) {
1053 ++_optimization_iterator;
1056 if (_optimization_iterator != _events.end() && (*_optimization_iterator)->note() == note) {
1057 return *_optimization_iterator;
1060 for (_optimization_iterator = _events.begin(); _optimization_iterator != _events.end(); ++_optimization_iterator) {
1061 if ((*_optimization_iterator)->note() == note) {
1062 return *_optimization_iterator;
1070 MidiRegionView::get_events (Events& e, Evoral::Sequence<Evoral::MusicalTime>::NoteOperator op, uint8_t val, int chan_mask)
1072 MidiModel::Notes notes;
1073 _model->get_notes (notes, op, val, chan_mask);
1075 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1076 NoteBase* cne = find_canvas_note (*n);
1084 MidiRegionView::redisplay_model()
1086 // Don't redisplay the model if we're currently recording and displaying that
1087 if (_active_notes) {
1095 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1096 (*i)->invalidate ();
1099 MidiModel::ReadLock lock(_model->read_lock());
1101 MidiModel::Notes& notes (_model->notes());
1102 _optimization_iterator = _events.begin();
1104 bool empty_when_starting = _events.empty();
1106 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1108 boost::shared_ptr<NoteType> note (*n);
1112 if (note_in_region_range (note, visible)) {
1114 if (!empty_when_starting && (cne = find_canvas_note (note)) != 0) {
1121 if ((cn = dynamic_cast<Note*>(cne)) != 0) {
1123 } else if ((ch = dynamic_cast<Hit*>(cne)) != 0) {
1135 add_note (note, visible);
1140 if (!empty_when_starting && (cne = find_canvas_note (note)) != 0) {
1148 /* remove note items that are no longer valid */
1150 if (!empty_when_starting) {
1151 for (Events::iterator i = _events.begin(); i != _events.end(); ) {
1152 if (!(*i)->valid ()) {
1154 for (vector<GhostRegion*>::iterator j = ghosts.begin(); j != ghosts.end(); ++j) {
1155 MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*j);
1157 gr->remove_note (*i);
1162 i = _events.erase (i);
1170 _patch_changes.clear();
1174 display_patch_changes ();
1176 _marked_for_selection.clear ();
1177 _marked_for_velocity.clear ();
1179 /* we may have caused _events to contain things out of order (e.g. if a note
1180 moved earlier or later). we don't generally need them in time order, but
1181 make a note that a sort is required for those cases that require it.
1184 _sort_needed = true;
1188 MidiRegionView::display_patch_changes ()
1190 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1191 uint16_t chn_mask = mtv->midi_track()->get_playback_channel_mask();
1193 for (uint8_t i = 0; i < 16; ++i) {
1194 display_patch_changes_on_channel (i, chn_mask & (1 << i));
1198 /** @param active_channel true to display patch changes fully, false to display
1199 * them `greyed-out' (as on an inactive channel)
1202 MidiRegionView::display_patch_changes_on_channel (uint8_t channel, bool active_channel)
1204 for (MidiModel::PatchChanges::const_iterator i = _model->patch_changes().begin(); i != _model->patch_changes().end(); ++i) {
1206 if ((*i)->channel() != channel) {
1210 const string patch_name = instrument_info().get_patch_name ((*i)->bank(), (*i)->program(), channel);
1211 add_canvas_patch_change (*i, patch_name, active_channel);
1216 MidiRegionView::display_sysexes()
1218 bool have_periodic_system_messages = false;
1219 bool display_periodic_messages = true;
1221 if (!Config->get_never_display_periodic_midi()) {
1223 for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
1224 const boost::shared_ptr<const Evoral::MIDIEvent<Evoral::MusicalTime> > mev =
1225 boost::static_pointer_cast<const Evoral::MIDIEvent<Evoral::MusicalTime> > (*i);
1228 if (mev->is_spp() || mev->is_mtc_quarter() || mev->is_mtc_full()) {
1229 have_periodic_system_messages = true;
1235 if (have_periodic_system_messages) {
1236 double zoom = trackview.editor().get_current_zoom (); // frames per pixel
1238 /* get an approximate value for the number of samples per video frame */
1240 double video_frame = trackview.session()->frame_rate() * (1.0/30);
1242 /* if we are zoomed out beyond than the cutoff (i.e. more
1243 * frames per pixel than frames per 4 video frames), don't
1244 * show periodic sysex messages.
1247 if (zoom > (video_frame*4)) {
1248 display_periodic_messages = false;
1252 display_periodic_messages = false;
1255 for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
1257 const boost::shared_ptr<const Evoral::MIDIEvent<Evoral::MusicalTime> > mev =
1258 boost::static_pointer_cast<const Evoral::MIDIEvent<Evoral::MusicalTime> > (*i);
1260 Evoral::MusicalTime time = (*i)->time();
1263 if (mev->is_spp() || mev->is_mtc_quarter() || mev->is_mtc_full()) {
1264 if (!display_periodic_messages) {
1272 for (uint32_t b = 0; b < (*i)->size(); ++b) {
1273 str << int((*i)->buffer()[b]);
1274 if (b != (*i)->size() -1) {
1278 string text = str.str();
1280 const double x = trackview.editor().sample_to_pixel(source_beats_to_region_frames(time));
1282 double height = midi_stream_view()->contents_height();
1284 // CAIROCANVAS: no longer passing *i (the sysex event) to the
1285 // SysEx canvas object!!!
1287 boost::shared_ptr<SysEx> sysex = boost::shared_ptr<SysEx>(
1288 new SysEx (*this, _note_group, text, height, x, 1.0));
1290 // Show unless message is beyond the region bounds
1291 if (time - _region->start() >= _region->length() || time < _region->start()) {
1297 _sys_exes.push_back(sysex);
1301 MidiRegionView::~MidiRegionView ()
1303 in_destructor = true;
1305 trackview.editor().verbose_cursor()->hide ();
1307 note_delete_connection.disconnect ();
1309 delete _list_editor;
1311 RegionViewGoingAway (this); /* EMIT_SIGNAL */
1313 if (_active_notes) {
1317 _selection_cleared_connection.disconnect ();
1320 clear_events (false);
1323 delete _note_diff_command;
1324 delete _step_edit_cursor;
1325 delete _temporary_note_group;
1329 MidiRegionView::region_resized (const PropertyChange& what_changed)
1331 RegionView::region_resized(what_changed);
1333 if (what_changed.contains (ARDOUR::Properties::position)) {
1334 set_duration(_region->length(), 0);
1335 if (_enable_display) {
1342 MidiRegionView::reset_width_dependent_items (double pixel_width)
1344 RegionView::reset_width_dependent_items(pixel_width);
1346 if (_enable_display) {
1350 for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
1351 if ((*x)->canvas_item()->width() >= _pixel_width) {
1358 move_step_edit_cursor (_step_edit_cursor_position);
1359 set_step_edit_cursor_width (_step_edit_cursor_width);
1363 MidiRegionView::set_height (double height)
1365 double old_height = _height;
1366 RegionView::set_height(height);
1368 apply_note_range (midi_stream_view()->lowest_note(),
1369 midi_stream_view()->highest_note(),
1370 height != old_height);
1373 name_text->raise_to_top();
1376 for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
1377 (*x)->set_height (midi_stream_view()->contents_height());
1380 if (_step_edit_cursor) {
1381 _step_edit_cursor->set_y1 (midi_stream_view()->contents_height());
1386 /** Apply the current note range from the stream view
1387 * by repositioning/hiding notes as necessary
1390 MidiRegionView::apply_note_range (uint8_t min, uint8_t max, bool force)
1392 if (!_enable_display) {
1396 if (!force && _current_range_min == min && _current_range_max == max) {
1400 _current_range_min = min;
1401 _current_range_max = max;
1403 for (Events::const_iterator i = _events.begin(); i != _events.end(); ++i) {
1404 NoteBase* event = *i;
1405 boost::shared_ptr<NoteType> note (event->note());
1407 if (note->note() < _current_range_min ||
1408 note->note() > _current_range_max) {
1414 if (Note* cnote = dynamic_cast<Note*>(event)) {
1416 const double y0 = midi_stream_view()->note_to_y(note->note());
1417 const double y1 = y0 + floor(midi_stream_view()->note_height());
1422 } else if (Hit* chit = dynamic_cast<Hit*>(event)) {
1429 MidiRegionView::add_ghost (TimeAxisView& tv)
1433 double unit_position = _region->position () / samples_per_pixel;
1434 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(&tv);
1435 MidiGhostRegion* ghost;
1437 if (mtv && mtv->midi_view()) {
1438 /* if ghost is inserted into midi track, use a dedicated midi ghost canvas group
1439 to allow having midi notes on top of note lines and waveforms.
1441 ghost = new MidiGhostRegion (*mtv->midi_view(), trackview, unit_position);
1443 ghost = new MidiGhostRegion (tv, trackview, unit_position);
1446 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1447 if ((note = dynamic_cast<Note*>(*i)) != 0) {
1448 ghost->add_note(note);
1452 ghost->set_height ();
1453 ghost->set_duration (_region->length() / samples_per_pixel);
1454 ghosts.push_back (ghost);
1456 GhostRegion::CatchDeletion.connect (*this, invalidator (*this), boost::bind (&RegionView::remove_ghost, this, _1), gui_context());
1462 /** Begin tracking note state for successive calls to add_event
1465 MidiRegionView::begin_write()
1467 if (_active_notes) {
1468 delete[] _active_notes;
1470 _active_notes = new Note*[128];
1471 for (unsigned i = 0; i < 128; ++i) {
1472 _active_notes[i] = 0;
1477 /** Destroy note state for add_event
1480 MidiRegionView::end_write()
1482 delete[] _active_notes;
1484 _marked_for_selection.clear();
1485 _marked_for_velocity.clear();
1489 /** Resolve an active MIDI note (while recording).
1492 MidiRegionView::resolve_note(uint8_t note, double end_time)
1494 if (midi_view()->note_mode() != Sustained) {
1498 if (_active_notes && _active_notes[note]) {
1500 /* XXX is end_time really region-centric? I think so, because
1501 this is a new region that we're recording, so source zero is
1502 the same as region zero
1504 const framepos_t end_time_frames = region_beats_to_region_frames(end_time);
1506 _active_notes[note]->set_x1 (trackview.editor().sample_to_pixel(end_time_frames));
1507 _active_notes[note]->set_outline_all ();
1508 _active_notes[note] = 0;
1514 /** Extend active notes to rightmost edge of region (if length is changed)
1517 MidiRegionView::extend_active_notes()
1519 if (!_active_notes) {
1523 for (unsigned i=0; i < 128; ++i) {
1524 if (_active_notes[i]) {
1525 _active_notes[i]->set_x1 (trackview.editor().sample_to_pixel(_region->length()));
1532 MidiRegionView::play_midi_note(boost::shared_ptr<NoteType> note)
1534 if (_no_sound_notes || !Config->get_sound_midi_notes()) {
1538 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1540 if (!route_ui || !route_ui->midi_track()) {
1544 NotePlayer* np = new NotePlayer (route_ui->midi_track ());
1548 /* NotePlayer deletes itself */
1552 MidiRegionView::start_playing_midi_note(boost::shared_ptr<NoteType> note)
1554 if (_no_sound_notes || !Config->get_sound_midi_notes()) {
1558 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1560 if (!route_ui || !route_ui->midi_track()) {
1564 delete _note_player;
1565 _note_player = new NotePlayer (route_ui->midi_track ());
1566 _note_player->add (note);
1567 _note_player->on ();
1571 MidiRegionView::start_playing_midi_chord (vector<boost::shared_ptr<NoteType> > notes)
1573 if (_no_sound_notes || !Config->get_sound_midi_notes()) {
1577 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1579 if (!route_ui || !route_ui->midi_track()) {
1583 delete _note_player;
1584 _note_player = new NotePlayer (route_ui->midi_track());
1586 for (vector<boost::shared_ptr<NoteType> >::iterator n = notes.begin(); n != notes.end(); ++n) {
1587 _note_player->add (*n);
1590 _note_player->on ();
1595 MidiRegionView::note_in_region_range (const boost::shared_ptr<NoteType> note, bool& visible) const
1597 const framepos_t note_start_frames = source_beats_to_region_frames (note->time());
1598 bool outside = (note_start_frames < 0) || (note_start_frames > _region->last_frame());
1600 visible = (note->note() >= midi_stream_view()->lowest_note()) &&
1601 (note->note() <= midi_stream_view()->highest_note());
1606 /** Update a canvas note's size from its model note.
1607 * @param ev Canvas note to update.
1608 * @param update_ghost_regions true to update the note in any ghost regions that we have, otherwise false.
1611 MidiRegionView::update_note (Note* ev, bool update_ghost_regions)
1613 boost::shared_ptr<NoteType> note = ev->note();
1614 const double x = trackview.editor().sample_to_pixel (source_beats_to_region_frames (note->time()));
1615 const double y0 = midi_stream_view()->note_to_y(note->note());
1620 /* trim note display to not overlap the end of its region */
1622 if (note->length() > 0) {
1623 const framepos_t note_end_frames = min (source_beats_to_region_frames (note->end_time()), _region->length());
1624 ev->set_x1 (trackview.editor().sample_to_pixel (note_end_frames));
1626 ev->set_x1 (trackview.editor().sample_to_pixel (_region->length()));
1629 ev->set_y1 (y0 + floor(midi_stream_view()->note_height()));
1631 if (note->length() == 0) {
1632 if (_active_notes && note->note() < 128) {
1633 // If this note is already active there's a stuck note,
1634 // finish the old note rectangle
1635 if (_active_notes[note->note()]) {
1636 Note* const old_rect = _active_notes[note->note()];
1637 boost::shared_ptr<NoteType> old_note = old_rect->note();
1638 old_rect->set_x1 (x);
1639 old_rect->set_outline_all ();
1641 _active_notes[note->note()] = ev;
1643 /* outline all but right edge */
1644 ev->set_outline_what (ArdourCanvas::Rectangle::What (
1645 ArdourCanvas::Rectangle::TOP|
1646 ArdourCanvas::Rectangle::LEFT|
1647 ArdourCanvas::Rectangle::BOTTOM));
1649 /* outline all edges */
1650 ev->set_outline_all ();
1653 if (update_ghost_regions) {
1654 for (std::vector<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
1655 MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*i);
1657 gr->update_note (ev);
1664 MidiRegionView::update_hit (Hit* ev)
1666 boost::shared_ptr<NoteType> note = ev->note();
1668 const framepos_t note_start_frames = source_beats_to_region_frames(note->time());
1669 const double x = trackview.editor().sample_to_pixel(note_start_frames);
1670 const double diamond_size = midi_stream_view()->note_height();
1671 const double y = midi_stream_view()->note_to_y(note->note()) + (diamond_size/2.0);
1673 ev->set_position (ArdourCanvas::Duple (x, y));
1674 ev->set_height (diamond_size);
1677 /** Add a MIDI note to the view (with length).
1679 * If in sustained mode, notes with length 0 will be considered active
1680 * notes, and resolve_note should be called when the corresponding note off
1681 * event arrives, to properly display the note.
1684 MidiRegionView::add_note(const boost::shared_ptr<NoteType> note, bool visible)
1686 NoteBase* event = 0;
1688 if (midi_view()->note_mode() == Sustained) {
1690 Note* ev_rect = new Note (*this, _note_group, note);
1692 update_note (ev_rect);
1696 MidiGhostRegion* gr;
1698 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
1699 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
1700 gr->add_note(ev_rect);
1704 } else if (midi_view()->note_mode() == Percussive) {
1706 const double diamond_size = midi_stream_view()->note_height() / 2.0;
1708 Hit* ev_diamond = new Hit (*this, _note_group, diamond_size, note);
1710 update_hit (ev_diamond);
1719 if (_marked_for_selection.find(note) != _marked_for_selection.end()) {
1720 note_selected(event, true);
1723 if (_marked_for_velocity.find(note) != _marked_for_velocity.end()) {
1724 event->show_velocity();
1727 event->on_channel_selection_change (get_selected_channels());
1728 _events.push_back(event);
1737 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1738 MidiStreamView* const view = mtv->midi_view();
1740 view->update_note_range (note->note());
1744 MidiRegionView::step_add_note (uint8_t channel, uint8_t number, uint8_t velocity,
1745 Evoral::MusicalTime pos, Evoral::MusicalTime len)
1747 boost::shared_ptr<NoteType> new_note (new NoteType (channel, pos, len, number, velocity));
1749 /* potentially extend region to hold new note */
1751 framepos_t end_frame = source_beats_to_absolute_frames (new_note->end_time());
1752 framepos_t region_end = _region->last_frame();
1754 if (end_frame > region_end) {
1755 _region->set_length (end_frame - _region->position());
1758 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1759 MidiStreamView* const view = mtv->midi_view();
1761 view->update_note_range(new_note->note());
1763 _marked_for_selection.clear ();
1766 start_note_diff_command (_("step add"));
1767 note_diff_add_note (new_note, true, false);
1770 // last_step_edit_note = new_note;
1774 MidiRegionView::step_sustain (Evoral::MusicalTime beats)
1776 change_note_lengths (false, false, beats, false, true);
1779 /** Add a new patch change flag to the canvas.
1780 * @param patch the patch change to add
1781 * @param the text to display in the flag
1782 * @param active_channel true to display the flag as on an active channel, false to grey it out for an inactive channel.
1785 MidiRegionView::add_canvas_patch_change (MidiModel::PatchChangePtr patch, const string& displaytext, bool /*active_channel*/)
1787 framecnt_t region_frames = source_beats_to_region_frames (patch->time());
1788 const double x = trackview.editor().sample_to_pixel (region_frames);
1790 double const height = midi_stream_view()->contents_height();
1792 // CAIROCANVAS: active_channel info removed from PatcChange constructor
1793 // so we need to do something more sophisticated to keep its color
1794 // appearance (MidiPatchChangeFill/MidiPatchChangeInactiveChannelFill)
1797 boost::shared_ptr<PatchChange> patch_change = boost::shared_ptr<PatchChange>(
1798 new PatchChange(*this, group,
1805 if (patch_change->item().width() < _pixel_width) {
1806 // Show unless patch change is beyond the region bounds
1807 if (region_frames < 0 || region_frames >= _region->length()) {
1808 patch_change->hide();
1810 patch_change->show();
1813 patch_change->hide ();
1816 _patch_changes.push_back (patch_change);
1819 MIDI::Name::PatchPrimaryKey
1820 MidiRegionView::patch_change_to_patch_key (MidiModel::PatchChangePtr p)
1822 return MIDI::Name::PatchPrimaryKey (p->program(), p->bank());
1825 /// Return true iff @p pc applies to the given time on the given channel.
1827 patch_applies (const ARDOUR::MidiModel::constPatchChangePtr pc, double time, uint8_t channel)
1829 return pc->time() <= time && pc->channel() == channel;
1833 MidiRegionView::get_patch_key_at (double time, uint8_t channel, MIDI::Name::PatchPrimaryKey& key) const
1835 // The earliest event not before time
1836 MidiModel::PatchChanges::iterator i = _model->patch_change_lower_bound (time);
1838 // Go backwards until we find the latest PC for this channel, or the start
1839 while (i != _model->patch_changes().begin() &&
1840 (i == _model->patch_changes().end() ||
1841 !patch_applies(*i, time, channel))) {
1845 if (i != _model->patch_changes().end() && patch_applies(*i, time, channel)) {
1846 key.bank_number = (*i)->bank();
1847 key.program_number = (*i)->program ();
1849 key.bank_number = key.program_number = 0;
1852 if (!key.is_sane()) {
1853 error << string_compose(_("insane MIDI patch key %1:%2"),
1854 key.bank_number, key.program_number) << endmsg;
1859 MidiRegionView::change_patch_change (PatchChange& pc, const MIDI::Name::PatchPrimaryKey& new_patch)
1861 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("alter patch change"));
1863 if (pc.patch()->program() != new_patch.program_number) {
1864 c->change_program (pc.patch (), new_patch.program_number);
1867 int const new_bank = new_patch.bank_number;
1868 if (pc.patch()->bank() != new_bank) {
1869 c->change_bank (pc.patch (), new_bank);
1872 _model->apply_command (*trackview.session(), c);
1874 _patch_changes.clear ();
1875 display_patch_changes ();
1879 MidiRegionView::change_patch_change (MidiModel::PatchChangePtr old_change, const Evoral::PatchChange<Evoral::MusicalTime> & new_change)
1881 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("alter patch change"));
1883 if (old_change->time() != new_change.time()) {
1884 c->change_time (old_change, new_change.time());
1887 if (old_change->channel() != new_change.channel()) {
1888 c->change_channel (old_change, new_change.channel());
1891 if (old_change->program() != new_change.program()) {
1892 c->change_program (old_change, new_change.program());
1895 if (old_change->bank() != new_change.bank()) {
1896 c->change_bank (old_change, new_change.bank());
1899 _model->apply_command (*trackview.session(), c);
1901 _patch_changes.clear ();
1902 display_patch_changes ();
1905 /** Add a patch change to the region.
1906 * @param t Time in frames relative to region position
1907 * @param patch Patch to add; time and channel are ignored (time is converted from t, and channel comes from
1908 * MidiTimeAxisView::get_channel_for_add())
1911 MidiRegionView::add_patch_change (framecnt_t t, Evoral::PatchChange<Evoral::MusicalTime> const & patch)
1913 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1915 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("add patch change"));
1916 c->add (MidiModel::PatchChangePtr (
1917 new Evoral::PatchChange<Evoral::MusicalTime> (
1918 absolute_frames_to_source_beats (_region->position() + t),
1919 mtv->get_channel_for_add(), patch.program(), patch.bank()
1924 _model->apply_command (*trackview.session(), c);
1926 _patch_changes.clear ();
1927 display_patch_changes ();
1931 MidiRegionView::move_patch_change (PatchChange& pc, Evoral::MusicalTime t)
1933 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("move patch change"));
1934 c->change_time (pc.patch (), t);
1935 _model->apply_command (*trackview.session(), c);
1937 _patch_changes.clear ();
1938 display_patch_changes ();
1942 MidiRegionView::delete_patch_change (PatchChange* pc)
1944 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("delete patch change"));
1945 c->remove (pc->patch ());
1946 _model->apply_command (*trackview.session(), c);
1948 _patch_changes.clear ();
1949 display_patch_changes ();
1953 MidiRegionView::previous_patch (PatchChange& patch)
1955 if (patch.patch()->program() < 127) {
1956 MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch());
1957 key.program_number++;
1958 change_patch_change (patch, key);
1963 MidiRegionView::next_patch (PatchChange& patch)
1965 if (patch.patch()->program() > 0) {
1966 MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch());
1967 key.program_number--;
1968 change_patch_change (patch, key);
1973 MidiRegionView::next_bank (PatchChange& patch)
1975 if (patch.patch()->program() < 127) {
1976 MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch());
1977 if (key.bank_number > 0) {
1979 change_patch_change (patch, key);
1985 MidiRegionView::previous_bank (PatchChange& patch)
1987 if (patch.patch()->program() > 0) {
1988 MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch());
1989 if (key.bank_number < 127) {
1991 change_patch_change (patch, key);
1997 MidiRegionView::maybe_remove_deleted_note_from_selection (NoteBase* cne)
1999 if (_selection.empty()) {
2003 _selection.erase (cne);
2007 MidiRegionView::delete_selection()
2009 if (_selection.empty()) {
2013 start_note_diff_command (_("delete selection"));
2015 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2016 if ((*i)->selected()) {
2017 _note_diff_command->remove((*i)->note());
2027 MidiRegionView::delete_note (boost::shared_ptr<NoteType> n)
2029 start_note_diff_command (_("delete note"));
2030 _note_diff_command->remove (n);
2033 trackview.editor().verbose_cursor()->hide ();
2037 MidiRegionView::clear_selection_except (NoteBase* ev, bool signal)
2039 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2041 Selection::iterator tmp = i;
2044 (*i)->set_selected (false);
2045 (*i)->hide_velocity ();
2046 _selection.erase (i);
2054 /* this does not change the status of this regionview w.r.t the editor
2059 SelectionCleared (this); /* EMIT SIGNAL */
2064 MidiRegionView::unique_select(NoteBase* ev)
2066 clear_selection_except (ev);
2068 /* don't bother with checking to see if we should remove this
2069 regionview from the editor selection, since we're about to add
2070 another note, and thus put/keep this regionview in the editor
2074 if (!ev->selected()) {
2075 add_to_selection (ev);
2080 MidiRegionView::select_all_notes ()
2084 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2085 add_to_selection (*i);
2090 MidiRegionView::select_range (framepos_t start, framepos_t end)
2094 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2095 framepos_t t = source_beats_to_absolute_frames((*i)->note()->time());
2096 if (t >= start && t <= end) {
2097 add_to_selection (*i);
2103 MidiRegionView::invert_selection ()
2105 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2106 if ((*i)->selected()) {
2107 remove_from_selection(*i);
2109 add_to_selection (*i);
2115 MidiRegionView::select_matching_notes (uint8_t notenum, uint16_t channel_mask, bool add, bool extend)
2117 bool have_selection = !_selection.empty();
2118 uint8_t low_note = 127;
2119 uint8_t high_note = 0;
2120 MidiModel::Notes& notes (_model->notes());
2121 _optimization_iterator = _events.begin();
2123 if (extend && !have_selection) {
2127 /* scan existing selection to get note range */
2129 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2130 if ((*i)->note()->note() < low_note) {
2131 low_note = (*i)->note()->note();
2133 if ((*i)->note()->note() > high_note) {
2134 high_note = (*i)->note()->note();
2141 if (!extend && (low_note == high_note) && (high_note == notenum)) {
2142 /* only note previously selected is the one we are
2143 * reselecting. treat this as cancelling the selection.
2150 low_note = min (low_note, notenum);
2151 high_note = max (high_note, notenum);
2154 _no_sound_notes = true;
2156 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
2158 boost::shared_ptr<NoteType> note (*n);
2160 bool select = false;
2162 if (((1 << note->channel()) & channel_mask) != 0) {
2164 if ((note->note() >= low_note && note->note() <= high_note)) {
2167 } else if (note->note() == notenum) {
2173 if ((cne = find_canvas_note (note)) != 0) {
2174 // extend is false because we've taken care of it,
2175 // since it extends by time range, not pitch.
2176 note_selected (cne, add, false);
2180 add = true; // we need to add all remaining matching notes, even if the passed in value was false (for "set")
2184 _no_sound_notes = false;
2188 MidiRegionView::toggle_matching_notes (uint8_t notenum, uint16_t channel_mask)
2190 MidiModel::Notes& notes (_model->notes());
2191 _optimization_iterator = _events.begin();
2193 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
2195 boost::shared_ptr<NoteType> note (*n);
2198 if (note->note() == notenum && (((0x0001 << note->channel()) & channel_mask) != 0)) {
2199 if ((cne = find_canvas_note (note)) != 0) {
2200 if (cne->selected()) {
2201 note_deselected (cne);
2203 note_selected (cne, true, false);
2211 MidiRegionView::note_selected (NoteBase* ev, bool add, bool extend)
2214 clear_selection_except (ev);
2215 if (!_selection.empty()) {
2216 PublicEditor& editor (trackview.editor());
2217 editor.get_selection().add (this);
2223 if (!ev->selected()) {
2224 add_to_selection (ev);
2228 /* find end of latest note selected, select all between that and the start of "ev" */
2230 Evoral::MusicalTime earliest = Evoral::MaxMusicalTime;
2231 Evoral::MusicalTime latest = 0;
2233 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2234 if ((*i)->note()->end_time() > latest) {
2235 latest = (*i)->note()->end_time();
2237 if ((*i)->note()->time() < earliest) {
2238 earliest = (*i)->note()->time();
2242 if (ev->note()->end_time() > latest) {
2243 latest = ev->note()->end_time();
2246 if (ev->note()->time() < earliest) {
2247 earliest = ev->note()->time();
2250 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2252 /* find notes entirely within OR spanning the earliest..latest range */
2254 if (((*i)->note()->time() >= earliest && (*i)->note()->end_time() <= latest) ||
2255 ((*i)->note()->time() <= earliest && (*i)->note()->end_time() >= latest)) {
2256 add_to_selection (*i);
2264 MidiRegionView::note_deselected(NoteBase* ev)
2266 remove_from_selection (ev);
2270 MidiRegionView::update_drag_selection(double x0, double x1, double y0, double y1, bool extend)
2272 // TODO: Make this faster by storing the last updated selection rect, and only
2273 // adjusting things that are in the area that appears/disappeared.
2274 // We probably need a tree to be able to find events in O(log(n)) time.
2276 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2277 if ((*i)->x0() < x1 && (*i)->x1() > x0 && (*i)->y0() < y1 && (*i)->y1() > y0) {
2278 // Rectangles intersect
2279 if (!(*i)->selected()) {
2280 add_to_selection (*i);
2282 } else if ((*i)->selected() && !extend) {
2283 // Rectangles do not intersect
2284 remove_from_selection (*i);
2290 MidiRegionView::update_vertical_drag_selection (double y1, double y2, bool extend)
2296 // TODO: Make this faster by storing the last updated selection rect, and only
2297 // adjusting things that are in the area that appears/disappeared.
2298 // We probably need a tree to be able to find events in O(log(n)) time.
2300 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2301 if (((*i)->y1() >= y1 && (*i)->y1() <= y2)) {
2302 // within y- (note-) range
2303 if (!(*i)->selected()) {
2304 add_to_selection (*i);
2306 } else if ((*i)->selected() && !extend) {
2307 remove_from_selection (*i);
2313 MidiRegionView::remove_from_selection (NoteBase* ev)
2315 Selection::iterator i = _selection.find (ev);
2317 if (i != _selection.end()) {
2318 _selection.erase (i);
2321 ev->set_selected (false);
2322 ev->hide_velocity ();
2324 if (_selection.empty()) {
2325 PublicEditor& editor (trackview.editor());
2326 editor.get_selection().remove (this);
2331 MidiRegionView::add_to_selection (NoteBase* ev)
2333 bool add_mrv_selection = false;
2335 if (_selection.empty()) {
2336 add_mrv_selection = true;
2339 if (_selection.insert (ev).second) {
2340 ev->set_selected (true);
2341 start_playing_midi_note ((ev)->note());
2344 if (add_mrv_selection) {
2345 PublicEditor& editor (trackview.editor());
2346 editor.get_selection().add (this);
2351 MidiRegionView::move_selection(double dx, double dy, double cumulative_dy)
2353 typedef vector<boost::shared_ptr<NoteType> > PossibleChord;
2354 PossibleChord to_play;
2355 Evoral::MusicalTime earliest = Evoral::MaxMusicalTime;
2357 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2358 if ((*i)->note()->time() < earliest) {
2359 earliest = (*i)->note()->time();
2363 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2364 if (Evoral::musical_time_equal ((*i)->note()->time(), earliest)) {
2365 to_play.push_back ((*i)->note());
2367 (*i)->move_event(dx, dy);
2370 if (dy && !_selection.empty() && !_no_sound_notes && Config->get_sound_midi_notes()) {
2372 if (to_play.size() > 1) {
2374 PossibleChord shifted;
2376 for (PossibleChord::iterator n = to_play.begin(); n != to_play.end(); ++n) {
2377 boost::shared_ptr<NoteType> moved_note (new NoteType (**n));
2378 moved_note->set_note (moved_note->note() + cumulative_dy);
2379 shifted.push_back (moved_note);
2382 start_playing_midi_chord (shifted);
2384 } else if (!to_play.empty()) {
2386 boost::shared_ptr<NoteType> moved_note (new NoteType (*to_play.front()));
2387 moved_note->set_note (moved_note->note() + cumulative_dy);
2388 start_playing_midi_note (moved_note);
2394 MidiRegionView::note_dropped(NoteBase *, frameoffset_t dt, int8_t dnote)
2396 uint8_t lowest_note_in_selection = 127;
2397 uint8_t highest_note_in_selection = 0;
2398 uint8_t highest_note_difference = 0;
2400 // find highest and lowest notes first
2402 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2403 uint8_t pitch = (*i)->note()->note();
2404 lowest_note_in_selection = std::min(lowest_note_in_selection, pitch);
2405 highest_note_in_selection = std::max(highest_note_in_selection, pitch);
2409 cerr << "dnote: " << (int) dnote << endl;
2410 cerr << "lowest note (streamview): " << int(midi_stream_view()->lowest_note())
2411 << " highest note (streamview): " << int(midi_stream_view()->highest_note()) << endl;
2412 cerr << "lowest note (selection): " << int(lowest_note_in_selection) << " highest note(selection): "
2413 << int(highest_note_in_selection) << endl;
2414 cerr << "selection size: " << _selection.size() << endl;
2415 cerr << "Highest note in selection: " << (int) highest_note_in_selection << endl;
2418 // Make sure the note pitch does not exceed the MIDI standard range
2419 if (highest_note_in_selection + dnote > 127) {
2420 highest_note_difference = highest_note_in_selection - 127;
2423 start_note_diff_command (_("move notes"));
2425 for (Selection::iterator i = _selection.begin(); i != _selection.end() ; ++i) {
2427 framepos_t new_frames = source_beats_to_absolute_frames ((*i)->note()->time()) + dt;
2428 Evoral::MusicalTime new_time = absolute_frames_to_source_beats (new_frames);
2434 note_diff_add_change (*i, MidiModel::NoteDiffCommand::StartTime, new_time);
2436 uint8_t original_pitch = (*i)->note()->note();
2437 uint8_t new_pitch = original_pitch + dnote - highest_note_difference;
2439 // keep notes in standard midi range
2440 clamp_to_0_127(new_pitch);
2442 lowest_note_in_selection = std::min(lowest_note_in_selection, new_pitch);
2443 highest_note_in_selection = std::max(highest_note_in_selection, new_pitch);
2445 note_diff_add_change (*i, MidiModel::NoteDiffCommand::NoteNumber, new_pitch);
2450 // care about notes being moved beyond the upper/lower bounds on the canvas
2451 if (lowest_note_in_selection < midi_stream_view()->lowest_note() ||
2452 highest_note_in_selection > midi_stream_view()->highest_note()) {
2453 midi_stream_view()->set_note_range(MidiStreamView::ContentsRange);
2457 /** @param x Pixel relative to the region position.
2458 * @return Snapped frame relative to the region position.
2461 MidiRegionView::snap_pixel_to_sample(double x)
2463 PublicEditor& editor (trackview.editor());
2464 return snap_frame_to_frame (editor.pixel_to_sample (x));
2467 /** @param x Pixel relative to the region position.
2468 * @return Snapped pixel relative to the region position.
2471 MidiRegionView::snap_to_pixel(double x)
2473 return (double) trackview.editor().sample_to_pixel(snap_pixel_to_sample(x));
2477 MidiRegionView::get_position_pixels()
2479 framepos_t region_frame = get_position();
2480 return trackview.editor().sample_to_pixel(region_frame);
2484 MidiRegionView::get_end_position_pixels()
2486 framepos_t frame = get_position() + get_duration ();
2487 return trackview.editor().sample_to_pixel(frame);
2491 MidiRegionView::source_beats_to_absolute_frames(double beats) const
2493 /* the time converter will return the frame corresponding to `beats'
2494 relative to the start of the source. The start of the source
2495 is an implied position given by region->position - region->start
2497 const framepos_t source_start = _region->position() - _region->start();
2498 return source_start + _source_relative_time_converter.to (beats);
2502 MidiRegionView::absolute_frames_to_source_beats(framepos_t frames) const
2504 /* the `frames' argument needs to be converted into a frame count
2505 relative to the start of the source before being passed in to the
2508 const framepos_t source_start = _region->position() - _region->start();
2509 return _source_relative_time_converter.from (frames - source_start);
2513 MidiRegionView::region_beats_to_region_frames(double beats) const
2515 return _region_relative_time_converter.to(beats);
2519 MidiRegionView::region_frames_to_region_beats(framepos_t frames) const
2521 return _region_relative_time_converter.from(frames);
2525 MidiRegionView::begin_resizing (bool /*at_front*/)
2527 _resize_data.clear();
2529 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2530 Note *note = dynamic_cast<Note*> (*i);
2532 // only insert CanvasNotes into the map
2534 NoteResizeData *resize_data = new NoteResizeData();
2535 resize_data->note = note;
2537 // create a new SimpleRect from the note which will be the resize preview
2538 ArdourCanvas::Rectangle *resize_rect = new ArdourCanvas::Rectangle (_note_group,
2539 ArdourCanvas::Rect (note->x0(), note->y0(), note->x0(), note->y1()));
2541 // calculate the colors: get the color settings
2542 uint32_t fill_color = UINT_RGBA_CHANGE_A(
2543 ARDOUR_UI::config()->get_canvasvar_MidiNoteSelected(),
2546 // make the resize preview notes more transparent and bright
2547 fill_color = UINT_INTERPOLATE(fill_color, 0xFFFFFF40, 0.5);
2549 // calculate color based on note velocity
2550 resize_rect->set_fill_color (UINT_INTERPOLATE(
2551 NoteBase::meter_style_fill_color(note->note()->velocity(), note->selected()),
2555 resize_rect->set_outline_color (NoteBase::calculate_outline (
2556 ARDOUR_UI::config()->get_canvasvar_MidiNoteSelected()));
2558 resize_data->resize_rect = resize_rect;
2559 _resize_data.push_back(resize_data);
2564 /** Update resizing notes while user drags.
2565 * @param primary `primary' note for the drag; ie the one that is used as the reference in non-relative mode.
2566 * @param at_front which end of the note (true == note on, false == note off)
2567 * @param delta_x change in mouse position since the start of the drag
2568 * @param relative true if relative resizing is taking place, false if absolute resizing. This only makes
2569 * a difference when multiple notes are being resized; in relative mode, each note's length is changed by the
2570 * amount of the drag. In non-relative mode, all selected notes are set to have the same start or end point
2571 * as the \a primary note.
2574 MidiRegionView::update_resizing (NoteBase* primary, bool at_front, double delta_x, bool relative)
2576 bool cursor_set = false;
2578 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2579 ArdourCanvas::Rectangle* resize_rect = (*i)->resize_rect;
2580 Note* canvas_note = (*i)->note;
2585 current_x = canvas_note->x0() + delta_x;
2587 current_x = primary->x0() + delta_x;
2591 current_x = canvas_note->x1() + delta_x;
2593 current_x = primary->x1() + delta_x;
2598 resize_rect->set_x0 (snap_to_pixel(current_x));
2599 resize_rect->set_x1 (canvas_note->x1());
2601 resize_rect->set_x1 (snap_to_pixel(current_x));
2602 resize_rect->set_x0 (canvas_note->x0());
2608 beats = snap_pixel_to_sample (current_x);
2609 beats = region_frames_to_region_beats (beats);
2614 if (beats < canvas_note->note()->end_time()) {
2615 len = canvas_note->note()->time() - beats;
2616 len += canvas_note->note()->length();
2621 if (beats >= canvas_note->note()->time()) {
2622 len = beats - canvas_note->note()->time();
2629 snprintf (buf, sizeof (buf), "%.3g beats", len);
2630 show_verbose_cursor (buf, 0, 0);
2639 /** Finish resizing notes when the user releases the mouse button.
2640 * Parameters the same as for \a update_resizing().
2643 MidiRegionView::commit_resizing (NoteBase* primary, bool at_front, double delta_x, bool relative)
2645 start_note_diff_command (_("resize notes"));
2647 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2648 Note* canvas_note = (*i)->note;
2649 ArdourCanvas::Rectangle* resize_rect = (*i)->resize_rect;
2651 /* Get the new x position for this resize, which is in pixels relative
2652 * to the region position.
2659 current_x = canvas_note->x0() + delta_x;
2661 current_x = primary->x0() + delta_x;
2665 current_x = canvas_note->x1() + delta_x;
2667 current_x = primary->x1() + delta_x;
2671 /* Convert that to a frame within the source */
2672 current_x = snap_pixel_to_sample (current_x) + _region->start ();
2674 /* and then to beats */
2675 current_x = region_frames_to_region_beats (current_x);
2677 if (at_front && current_x < canvas_note->note()->end_time()) {
2678 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::StartTime, current_x);
2680 double len = canvas_note->note()->time() - current_x;
2681 len += canvas_note->note()->length();
2684 /* XXX convert to beats */
2685 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
2690 double len = current_x - canvas_note->note()->time();
2693 /* XXX convert to beats */
2694 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
2702 _resize_data.clear();
2707 MidiRegionView::abort_resizing ()
2709 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2710 delete (*i)->resize_rect;
2714 _resize_data.clear ();
2718 MidiRegionView::change_note_velocity(NoteBase* event, int8_t velocity, bool relative)
2720 uint8_t new_velocity;
2723 new_velocity = event->note()->velocity() + velocity;
2724 clamp_to_0_127(new_velocity);
2726 new_velocity = velocity;
2729 event->set_selected (event->selected()); // change color
2731 note_diff_add_change (event, MidiModel::NoteDiffCommand::Velocity, new_velocity);
2735 MidiRegionView::change_note_note (NoteBase* event, int8_t note, bool relative)
2740 new_note = event->note()->note() + note;
2745 clamp_to_0_127 (new_note);
2746 note_diff_add_change (event, MidiModel::NoteDiffCommand::NoteNumber, new_note);
2750 MidiRegionView::trim_note (NoteBase* event, Evoral::MusicalTime front_delta, Evoral::MusicalTime end_delta)
2752 bool change_start = false;
2753 bool change_length = false;
2754 Evoral::MusicalTime new_start = 0;
2755 Evoral::MusicalTime new_length = 0;
2757 /* NOTE: the semantics of the two delta arguments are slightly subtle:
2759 front_delta: if positive - move the start of the note later in time (shortening it)
2760 if negative - move the start of the note earlier in time (lengthening it)
2762 end_delta: if positive - move the end of the note later in time (lengthening it)
2763 if negative - move the end of the note earlier in time (shortening it)
2767 if (front_delta < 0) {
2769 if (event->note()->time() < -front_delta) {
2772 new_start = event->note()->time() + front_delta; // moves earlier
2775 /* start moved toward zero, so move the end point out to where it used to be.
2776 Note that front_delta is negative, so this increases the length.
2779 new_length = event->note()->length() - front_delta;
2780 change_start = true;
2781 change_length = true;
2785 Evoral::MusicalTime new_pos = event->note()->time() + front_delta;
2787 if (new_pos < event->note()->end_time()) {
2788 new_start = event->note()->time() + front_delta;
2789 /* start moved toward the end, so move the end point back to where it used to be */
2790 new_length = event->note()->length() - front_delta;
2791 change_start = true;
2792 change_length = true;
2799 bool can_change = true;
2800 if (end_delta < 0) {
2801 if (event->note()->length() < -end_delta) {
2807 new_length = event->note()->length() + end_delta;
2808 change_length = true;
2813 note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_start);
2816 if (change_length) {
2817 note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, new_length);
2822 MidiRegionView::change_note_channel (NoteBase* event, int8_t chn, bool relative)
2824 uint8_t new_channel;
2828 if (event->note()->channel() < -chn) {
2831 new_channel = event->note()->channel() + chn;
2834 new_channel = event->note()->channel() + chn;
2837 new_channel = (uint8_t) chn;
2840 note_diff_add_change (event, MidiModel::NoteDiffCommand::Channel, new_channel);
2844 MidiRegionView::change_note_time (NoteBase* event, Evoral::MusicalTime delta, bool relative)
2846 Evoral::MusicalTime new_time;
2850 if (event->note()->time() < -delta) {
2853 new_time = event->note()->time() + delta;
2856 new_time = event->note()->time() + delta;
2862 note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_time);
2866 MidiRegionView::change_note_length (NoteBase* event, Evoral::MusicalTime t)
2868 note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, t);
2872 MidiRegionView::change_velocities (bool up, bool fine, bool allow_smush, bool all_together)
2877 if (_selection.empty()) {
2892 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2893 if ((*i)->note()->velocity() < -delta || (*i)->note()->velocity() + delta > 127) {
2899 start_note_diff_command (_("change velocities"));
2901 for (Selection::iterator i = _selection.begin(); i != _selection.end();) {
2902 Selection::iterator next = i;
2906 if (i == _selection.begin()) {
2907 change_note_velocity (*i, delta, true);
2908 value = (*i)->note()->velocity() + delta;
2910 change_note_velocity (*i, value, false);
2914 change_note_velocity (*i, delta, true);
2923 if (!_selection.empty()) {
2925 snprintf (buf, sizeof (buf), "Vel %d",
2926 (int) (*_selection.begin())->note()->velocity());
2927 show_verbose_cursor (buf, 10, 10);
2933 MidiRegionView::transpose (bool up, bool fine, bool allow_smush)
2935 if (_selection.empty()) {
2952 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2954 if ((int8_t) (*i)->note()->note() + delta <= 0) {
2958 if ((int8_t) (*i)->note()->note() + delta > 127) {
2965 start_note_diff_command (_("transpose"));
2967 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2968 Selection::iterator next = i;
2970 change_note_note (*i, delta, true);
2978 MidiRegionView::change_note_lengths (bool fine, bool shorter, Evoral::MusicalTime delta, bool start, bool end)
2984 /* grab the current grid distance */
2986 delta = trackview.editor().get_grid_type_as_beats (success, _region->position());
2988 /* XXX cannot get grid type as beats ... should always be possible ... FIX ME */
2989 error << string_compose (_("programming error: %1"), "Grid type not available as beats - TO BE FIXED") << endmsg;
2999 start_note_diff_command (_("change note lengths"));
3001 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3002 Selection::iterator next = i;
3005 /* note the negation of the delta for start */
3007 trim_note (*i, (start ? -delta : 0), (end ? delta : 0));
3016 MidiRegionView::nudge_notes (bool forward)
3018 if (_selection.empty()) {
3022 /* pick a note as the point along the timeline to get the nudge distance.
3023 its not necessarily the earliest note, so we may want to pull the notes out
3024 into a vector and sort before using the first one.
3027 framepos_t ref_point = source_beats_to_absolute_frames ((*(_selection.begin()))->note()->time());
3029 framecnt_t distance;
3031 if (trackview.editor().snap_mode() == Editing::SnapOff) {
3033 /* grid is off - use nudge distance */
3035 distance = trackview.editor().get_nudge_distance (ref_point, unused);
3041 framepos_t next_pos = ref_point;
3044 if (max_framepos - 1 < next_pos) {
3048 if (next_pos == 0) {
3054 trackview.editor().snap_to (next_pos, (forward ? 1 : -1), false);
3055 distance = ref_point - next_pos;
3058 if (distance == 0) {
3062 Evoral::MusicalTime delta = region_frames_to_region_beats (fabs ((double)distance));
3068 start_note_diff_command (_("nudge"));
3070 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3071 Selection::iterator next = i;
3073 change_note_time (*i, delta, true);
3081 MidiRegionView::change_channel(uint8_t channel)
3083 start_note_diff_command(_("change channel"));
3084 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3085 note_diff_add_change (*i, MidiModel::NoteDiffCommand::Channel, channel);
3093 MidiRegionView::note_entered(NoteBase* ev)
3095 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3097 pre_enter_cursor = editor->get_canvas_cursor ();
3099 if (_mouse_state == SelectTouchDragging) {
3100 note_selected (ev, true);
3103 show_verbose_cursor (ev->note ());
3107 MidiRegionView::note_left (NoteBase*)
3109 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3111 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3112 (*i)->hide_velocity ();
3115 editor->verbose_cursor()->hide ();
3117 if (pre_enter_cursor) {
3118 editor->set_canvas_cursor (pre_enter_cursor);
3119 pre_enter_cursor = 0;
3124 MidiRegionView::patch_entered (PatchChange* p)
3127 /* XXX should get patch name if we can */
3128 s << _("Bank ") << (p->patch()->bank() + MIDI_BP_ZERO) << '\n'
3129 << _("Program ") << ((int) p->patch()->program()) + MIDI_BP_ZERO << '\n'
3130 << _("Channel ") << ((int) p->patch()->channel() + 1);
3131 show_verbose_cursor (s.str(), 10, 20);
3132 p->item().grab_focus();
3136 MidiRegionView::patch_left (PatchChange *)
3138 trackview.editor().verbose_cursor()->hide ();
3139 /* focus will transfer back via the enter-notify event sent to this
3145 MidiRegionView::sysex_entered (SysEx* p)
3149 // need a way to extract text from p->_flag->_text
3151 // show_verbose_cursor (s.str(), 10, 20);
3152 p->item().grab_focus();
3156 MidiRegionView::sysex_left (SysEx *)
3158 trackview.editor().verbose_cursor()->hide ();
3159 /* focus will transfer back via the enter-notify event sent to this
3165 MidiRegionView::note_mouse_position (float x_fraction, float /*y_fraction*/, bool can_set_cursor)
3167 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3168 Editing::MouseMode mm = editor->current_mouse_mode();
3169 bool trimmable = (mm == MouseObject || mm == MouseTimeFX || mm == MouseDraw);
3171 if (trimmable && x_fraction > 0.0 && x_fraction < 0.2) {
3172 editor->set_canvas_cursor (editor->cursors()->left_side_trim);
3173 } else if (trimmable && x_fraction >= 0.8 && x_fraction < 1.0) {
3174 editor->set_canvas_cursor (editor->cursors()->right_side_trim);
3176 if (pre_enter_cursor && can_set_cursor) {
3177 editor->set_canvas_cursor (pre_enter_cursor);
3183 MidiRegionView::set_frame_color()
3187 TimeAxisViewItem::set_frame_color ();
3194 f = ARDOUR_UI::config()->get_canvasvar_SelectedFrameBase();
3195 } else if (high_enough_for_name) {
3196 f= ARDOUR_UI::config()->get_canvasvar_MidiFrameBase();
3201 if (!rect_visible) {
3202 f = UINT_RGBA_CHANGE_A (f, 0);
3205 frame->set_fill_color (f);
3209 MidiRegionView::midi_channel_mode_changed ()
3211 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3212 uint16_t mask = mtv->midi_track()->get_playback_channel_mask();
3213 ChannelMode mode = mtv->midi_track()->get_playback_channel_mode ();
3215 if (mode == ForceChannel) {
3216 mask = 0xFFFF; // Show all notes as active (below)
3219 // Update notes for selection
3220 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3221 (*i)->on_channel_selection_change (mask);
3224 _patch_changes.clear ();
3225 display_patch_changes ();
3229 MidiRegionView::instrument_settings_changed ()
3235 MidiRegionView::cut_copy_clear (Editing::CutCopyOp op)
3237 if (_selection.empty()) {
3241 PublicEditor& editor (trackview.editor());
3245 /* XXX what to do ? */
3249 editor.get_cut_buffer().add (selection_as_cut_buffer());
3257 start_note_diff_command();
3259 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3266 note_diff_remove_note (*i);
3276 MidiRegionView::selection_as_cut_buffer () const
3280 for (Selection::const_iterator i = _selection.begin(); i != _selection.end(); ++i) {
3281 NoteType* n = (*i)->note().get();
3282 notes.insert (boost::shared_ptr<NoteType> (new NoteType (*n)));
3285 MidiCutBuffer* cb = new MidiCutBuffer (trackview.session());
3291 /** This method handles undo */
3293 MidiRegionView::paste (framepos_t pos, float times, const MidiCutBuffer& mcb)
3299 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("MIDI paste @ %1 times %2\n", pos, times));
3301 trackview.session()->begin_reversible_command (_("paste"));
3303 start_note_diff_command (_("paste"));
3305 Evoral::MusicalTime beat_delta;
3306 Evoral::MusicalTime paste_pos_beats;
3307 Evoral::MusicalTime duration;
3308 Evoral::MusicalTime end_point = 0;
3310 duration = (*mcb.notes().rbegin())->end_time() - (*mcb.notes().begin())->time();
3311 paste_pos_beats = absolute_frames_to_source_beats (pos);
3312 beat_delta = (*mcb.notes().begin())->time() - paste_pos_beats;
3313 paste_pos_beats = 0;
3315 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste data spans from %1 to %2 (%3) ; paste pos beats = %4 (based on %5 - %6 ; beat delta = %7\n",
3316 (*mcb.notes().begin())->time(),
3317 (*mcb.notes().rbegin())->end_time(),
3318 duration, pos, _region->position(),
3319 paste_pos_beats, beat_delta));
3323 for (int n = 0; n < (int) times; ++n) {
3325 for (Notes::const_iterator i = mcb.notes().begin(); i != mcb.notes().end(); ++i) {
3327 boost::shared_ptr<NoteType> copied_note (new NoteType (*((*i).get())));
3328 copied_note->set_time (paste_pos_beats + copied_note->time() - beat_delta);
3330 /* make all newly added notes selected */
3332 note_diff_add_note (copied_note, true);
3333 end_point = copied_note->end_time();
3336 paste_pos_beats += duration;
3339 /* if we pasted past the current end of the region, extend the region */
3341 framepos_t end_frame = source_beats_to_absolute_frames (end_point);
3342 framepos_t region_end = _region->position() + _region->length() - 1;
3344 if (end_frame > region_end) {
3346 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste extended region from %1 to %2\n", region_end, end_frame));
3348 _region->clear_changes ();
3349 _region->set_length (end_frame - _region->position());
3350 trackview.session()->add_command (new StatefulDiffCommand (_region));
3355 trackview.session()->commit_reversible_command ();
3358 struct EventNoteTimeEarlyFirstComparator {
3359 bool operator() (NoteBase* a, NoteBase* b) {
3360 return a->note()->time() < b->note()->time();
3365 MidiRegionView::time_sort_events ()
3367 if (!_sort_needed) {
3371 EventNoteTimeEarlyFirstComparator cmp;
3374 _sort_needed = false;
3378 MidiRegionView::goto_next_note (bool add_to_selection)
3380 bool use_next = false;
3382 if (_events.back()->selected()) {
3386 time_sort_events ();
3388 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3389 uint16_t const channel_mask = mtv->midi_track()->get_playback_channel_mask();
3391 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3392 if ((*i)->selected()) {
3395 } else if (use_next) {
3396 if (channel_mask & (1 << (*i)->note()->channel())) {
3397 if (!add_to_selection) {
3400 note_selected (*i, true, false);
3407 /* use the first one */
3409 if (!_events.empty() && (channel_mask & (1 << _events.front()->note()->channel ()))) {
3410 unique_select (_events.front());
3415 MidiRegionView::goto_previous_note (bool add_to_selection)
3417 bool use_next = false;
3419 if (_events.front()->selected()) {
3423 time_sort_events ();
3425 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3426 uint16_t const channel_mask = mtv->midi_track()->get_playback_channel_mask ();
3428 for (Events::reverse_iterator i = _events.rbegin(); i != _events.rend(); ++i) {
3429 if ((*i)->selected()) {
3432 } else if (use_next) {
3433 if (channel_mask & (1 << (*i)->note()->channel())) {
3434 if (!add_to_selection) {
3437 note_selected (*i, true, false);
3444 /* use the last one */
3446 if (!_events.empty() && (channel_mask & (1 << (*_events.rbegin())->note()->channel ()))) {
3447 unique_select (*(_events.rbegin()));
3452 MidiRegionView::selection_as_notelist (Notes& selected, bool allow_all_if_none_selected)
3454 bool had_selected = false;
3456 time_sort_events ();
3458 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3459 if ((*i)->selected()) {
3460 selected.insert ((*i)->note());
3461 had_selected = true;
3465 if (allow_all_if_none_selected && !had_selected) {
3466 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3467 selected.insert ((*i)->note());
3473 MidiRegionView::update_ghost_note (double x, double y)
3475 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3480 _note_group->canvas_to_item (x, y);
3482 PublicEditor& editor = trackview.editor ();
3484 framepos_t const unsnapped_frame = editor.pixel_to_sample (x);
3485 framecnt_t grid_frames;
3486 framepos_t const f = snap_frame_to_grid_underneath (unsnapped_frame, grid_frames);
3488 /* use region_frames... because we are converting a delta within the region
3492 double length = editor.get_grid_type_as_beats (success, unsnapped_frame);
3498 /* note that this sets the time of the ghost note in beats relative to
3499 the start of the source; that is how all note times are stored.
3501 _ghost_note->note()->set_time (absolute_frames_to_source_beats (f + _region->position ()));
3502 _ghost_note->note()->set_length (length);
3503 _ghost_note->note()->set_note (midi_stream_view()->y_to_note (y));
3504 _ghost_note->note()->set_channel (mtv->get_channel_for_add ());
3506 /* the ghost note does not appear in ghost regions, so pass false in here */
3507 update_note (_ghost_note, false);
3509 show_verbose_cursor (_ghost_note->note ());
3513 MidiRegionView::create_ghost_note (double x, double y)
3515 remove_ghost_note ();
3517 boost::shared_ptr<NoteType> g (new NoteType);
3518 _ghost_note = new Note (*this, _note_group, g);
3519 _ghost_note->set_ignore_events (true);
3520 _ghost_note->set_outline_color (0x000000aa);
3521 update_ghost_note (x, y);
3522 _ghost_note->show ();
3527 show_verbose_cursor (_ghost_note->note ());
3531 MidiRegionView::remove_ghost_note ()
3538 MidiRegionView::snap_changed ()
3544 create_ghost_note (_last_ghost_x, _last_ghost_y);
3548 MidiRegionView::drop_down_keys ()
3550 _mouse_state = None;
3554 MidiRegionView::maybe_select_by_position (GdkEventButton* ev, double /*x*/, double y)
3556 double note = midi_stream_view()->y_to_note(y);
3558 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3560 uint16_t chn_mask = mtv->midi_track()->get_playback_channel_mask();
3562 if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
3563 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchGreaterThanOrEqual, (uint8_t) floor (note), chn_mask);
3564 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
3565 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchLessThanOrEqual, (uint8_t) floor (note), chn_mask);
3570 bool add_mrv_selection = false;
3572 if (_selection.empty()) {
3573 add_mrv_selection = true;
3576 for (Events::iterator i = e.begin(); i != e.end(); ++i) {
3577 if (_selection.insert (*i).second) {
3578 (*i)->set_selected (true);
3582 if (add_mrv_selection) {
3583 PublicEditor& editor (trackview.editor());
3584 editor.get_selection().add (this);
3589 MidiRegionView::color_handler ()
3591 RegionView::color_handler ();
3593 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3594 (*i)->set_selected ((*i)->selected()); // will change color
3597 /* XXX probably more to do here */
3601 MidiRegionView::enable_display (bool yn)
3603 RegionView::enable_display (yn);
3610 MidiRegionView::show_step_edit_cursor (Evoral::MusicalTime pos)
3612 if (_step_edit_cursor == 0) {
3613 ArdourCanvas::Item* const group = get_canvas_group();
3615 _step_edit_cursor = new ArdourCanvas::Rectangle (group);
3616 _step_edit_cursor->set_y0 (0);
3617 _step_edit_cursor->set_y1 (midi_stream_view()->contents_height());
3618 _step_edit_cursor->set_fill_color (RGBA_TO_UINT (45,0,0,90));
3619 _step_edit_cursor->set_outline_color (RGBA_TO_UINT (85,0,0,90));
3622 move_step_edit_cursor (pos);
3623 _step_edit_cursor->show ();
3627 MidiRegionView::move_step_edit_cursor (Evoral::MusicalTime pos)
3629 _step_edit_cursor_position = pos;
3631 if (_step_edit_cursor) {
3632 double pixel = trackview.editor().sample_to_pixel (region_beats_to_region_frames (pos));
3633 _step_edit_cursor->set_x0 (pixel);
3634 set_step_edit_cursor_width (_step_edit_cursor_width);
3639 MidiRegionView::hide_step_edit_cursor ()
3641 if (_step_edit_cursor) {
3642 _step_edit_cursor->hide ();
3647 MidiRegionView::set_step_edit_cursor_width (Evoral::MusicalTime beats)
3649 _step_edit_cursor_width = beats;
3651 if (_step_edit_cursor) {
3652 _step_edit_cursor->set_x1 (_step_edit_cursor->x0() + trackview.editor().sample_to_pixel (region_beats_to_region_frames (beats)));
3656 /** Called when a diskstream on our track has received some data. Update the view, if applicable.
3657 * @param w Source that the data will end up in.
3660 MidiRegionView::data_recorded (boost::weak_ptr<MidiSource> w)
3662 if (!_active_notes) {
3663 /* we aren't actively being recorded to */
3667 boost::shared_ptr<MidiSource> src = w.lock ();
3668 if (!src || src != midi_region()->midi_source()) {
3669 /* recorded data was not destined for our source */
3673 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (&trackview);
3675 boost::shared_ptr<MidiBuffer> buf = mtv->midi_track()->get_gui_feed_buffer ();
3677 BeatsFramesConverter converter (trackview.session()->tempo_map(), mtv->midi_track()->get_capture_start_frame (0));
3679 framepos_t back = max_framepos;
3681 for (MidiBuffer::iterator i = buf->begin(); i != buf->end(); ++i) {
3682 Evoral::MIDIEvent<MidiBuffer::TimeType> const ev (*i, false);
3684 if (ev.is_channel_event()) {
3685 if (get_channel_mode() == FilterChannels) {
3686 if (((uint16_t(1) << ev.channel()) & get_selected_channels()) == 0) {
3692 /* ev.time() is in session frames, so (ev.time() - converter.origin_b()) is
3693 frames from the start of the source, and so time_beats is in terms of the
3697 Evoral::MusicalTime const time_beats = converter.from (ev.time () - converter.origin_b ());
3699 if (ev.type() == MIDI_CMD_NOTE_ON) {
3700 boost::shared_ptr<NoteType> note (
3701 new NoteType (ev.channel(), time_beats, 0, ev.note(), ev.velocity()));
3703 add_note (note, true);
3705 /* fix up our note range */
3706 if (ev.note() < _current_range_min) {
3707 midi_stream_view()->apply_note_range (ev.note(), _current_range_max, true);
3708 } else if (ev.note() > _current_range_max) {
3709 midi_stream_view()->apply_note_range (_current_range_min, ev.note(), true);
3712 } else if (ev.type() == MIDI_CMD_NOTE_OFF) {
3713 resolve_note (ev.note (), time_beats);
3719 midi_stream_view()->check_record_layers (region(), back);
3723 MidiRegionView::trim_front_starting ()
3725 /* Reparent the note group to the region view's parent, so that it doesn't change
3726 when the region view is trimmed.
3728 _temporary_note_group = new ArdourCanvas::Container (group->parent ());
3729 _temporary_note_group->move (group->position ());
3730 _note_group->reparent (_temporary_note_group);
3734 MidiRegionView::trim_front_ending ()
3736 _note_group->reparent (group);
3737 delete _temporary_note_group;
3738 _temporary_note_group = 0;
3740 if (_region->start() < 0) {
3741 /* Trim drag made start time -ve; fix this */
3742 midi_region()->fix_negative_start ();
3747 MidiRegionView::edit_patch_change (PatchChange* pc)
3749 PatchChangeDialog d (&_source_relative_time_converter, trackview.session(), *pc->patch (), instrument_info(), Gtk::Stock::APPLY, true);
3751 int response = d.run();
3754 case Gtk::RESPONSE_ACCEPT:
3756 case Gtk::RESPONSE_REJECT:
3757 delete_patch_change (pc);
3763 change_patch_change (pc->patch(), d.patch ());
3767 MidiRegionView::delete_sysex (SysEx* /*sysex*/)
3770 // sysyex object doesn't have a pointer to a sysex event
3771 // MidiModel::SysExDiffCommand* c = _model->new_sysex_diff_command (_("delete sysex"));
3772 // c->remove (sysex->sysex());
3773 // _model->apply_command (*trackview.session(), c);
3775 //_sys_exes.clear ();
3776 // display_sysexes();
3780 MidiRegionView::show_verbose_cursor (boost::shared_ptr<NoteType> n) const
3782 using namespace MIDI::Name;
3786 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3788 boost::shared_ptr<MasterDeviceNames> device_names(mtv->get_device_names());
3790 MIDI::Name::PatchPrimaryKey patch_key;
3791 get_patch_key_at(n->time(), n->channel(), patch_key);
3792 name = device_names->note_name(mtv->gui_property(X_("midnam-custom-device-mode")),
3794 patch_key.bank_number,
3795 patch_key.program_number,
3801 snprintf (buf, sizeof (buf), "%d %s\nCh %d Vel %d",
3803 name.empty() ? Evoral::midi_note_name (n->note()).c_str() : name.c_str(),
3804 (int) n->channel() + 1,
3805 (int) n->velocity());
3807 show_verbose_cursor(buf, 10, 20);
3811 MidiRegionView::show_verbose_cursor (string const & text, double xoffset, double yoffset) const
3813 trackview.editor().verbose_cursor()->set (text);
3814 trackview.editor().verbose_cursor()->show ();
3815 trackview.editor().verbose_cursor()->set_offset (ArdourCanvas::Duple (xoffset, yoffset));
3818 /** @param p A session framepos.
3819 * @param grid_frames Filled in with the number of frames that a grid interval is at p.
3820 * @return p snapped to the grid subdivision underneath it.
3823 MidiRegionView::snap_frame_to_grid_underneath (framepos_t p, framecnt_t& grid_frames) const
3825 PublicEditor& editor = trackview.editor ();
3828 Evoral::MusicalTime grid_beats = editor.get_grid_type_as_beats (success, p);
3834 grid_frames = region_beats_to_region_frames (grid_beats);
3836 /* Hack so that we always snap to the note that we are over, instead of snapping
3837 to the next one if we're more than halfway through the one we're over.
3839 if (editor.snap_mode() == SnapNormal && p >= grid_frames / 2) {
3840 p -= grid_frames / 2;
3843 return snap_frame_to_frame (p);
3846 /** Called when the selection has been cleared in any MidiRegionView.
3847 * @param rv MidiRegionView that the selection was cleared in.
3850 MidiRegionView::selection_cleared (MidiRegionView* rv)
3856 /* Clear our selection in sympathy; but don't signal the fact */
3857 clear_selection (false);
3861 MidiRegionView::note_button_release ()
3863 delete _note_player;
3868 MidiRegionView::get_channel_mode () const
3870 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (&trackview);
3871 return rtav->midi_track()->get_playback_channel_mode();
3875 MidiRegionView::get_selected_channels () const
3877 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (&trackview);
3878 return rtav->midi_track()->get_playback_channel_mask();