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/pixbuf.h"
48 #include "automation_region_view.h"
49 #include "automation_time_axis.h"
52 #include "editor_drag.h"
53 #include "ghostregion.h"
54 #include "gui_thread.h"
56 #include "midi_channel_dialog.h"
57 #include "midi_cut_buffer.h"
58 #include "midi_list_editor.h"
59 #include "midi_region_view.h"
60 #include "midi_streamview.h"
61 #include "midi_time_axis.h"
62 #include "midi_util.h"
63 #include "midi_velocity_dialog.h"
64 #include "mouse_cursors.h"
65 #include "note_player.h"
66 #include "public_editor.h"
67 #include "route_time_axis.h"
68 #include "rgb_macros.h"
69 #include "selection.h"
70 #include "streamview.h"
72 #include "patch_change_dialog.h"
73 #include "verbose_cursor.h"
74 #include "ardour_ui.h"
77 #include "patch_change.h"
82 using namespace ARDOUR;
84 using namespace Editing;
85 using Gtkmm2ext::Keyboard;
87 PBD::Signal1<void, MidiRegionView *> MidiRegionView::SelectionCleared;
89 #define MIDI_BP_ZERO ((Config->get_first_midi_bank_is_zero())?0:1)
91 MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &tv,
92 boost::shared_ptr<MidiRegion> r, double spu, Gdk::Color const & basic_color)
93 : RegionView (parent, tv, r, spu, basic_color)
94 , _current_range_min(0)
95 , _current_range_max(0)
97 , _note_group (new ArdourCanvas::Group (group))
98 , _note_diff_command (0)
100 , _step_edit_cursor (0)
101 , _step_edit_cursor_width (1.0)
102 , _step_edit_cursor_position (0.0)
103 , _channel_selection_scoped_note (0)
104 , _temporary_note_group (0)
107 , _sort_needed (true)
108 , _optimization_iterator (_events.end())
110 , _no_sound_notes (false)
113 , pre_enter_cursor (0)
114 , pre_press_cursor (0)
117 _note_group->raise_to_top();
118 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
120 Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&MidiRegionView::parameter_changed, this, _1), gui_context());
121 connect_to_diskstream ();
123 SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), boost::bind (&MidiRegionView::selection_cleared, this, _1), gui_context ());
126 MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &tv,
127 boost::shared_ptr<MidiRegion> r, double spu, Gdk::Color& basic_color,
128 TimeAxisViewItem::Visibility visibility)
129 : RegionView (parent, tv, r, spu, basic_color, false, visibility)
130 , _current_range_min(0)
131 , _current_range_max(0)
133 , _note_group (new ArdourCanvas::Group (parent))
134 , _note_diff_command (0)
136 , _step_edit_cursor (0)
137 , _step_edit_cursor_width (1.0)
138 , _step_edit_cursor_position (0.0)
139 , _channel_selection_scoped_note (0)
140 , _temporary_note_group (0)
143 , _sort_needed (true)
144 , _optimization_iterator (_events.end())
146 , _no_sound_notes (false)
149 , pre_enter_cursor (0)
150 , pre_press_cursor (0)
153 _note_group->raise_to_top();
154 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
156 connect_to_diskstream ();
158 SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), boost::bind (&MidiRegionView::selection_cleared, this, _1), gui_context ());
162 MidiRegionView::parameter_changed (std::string const & p)
164 if (p == "diplay-first-midi-bank-as-zero") {
165 if (_enable_display) {
171 MidiRegionView::MidiRegionView (const MidiRegionView& other)
172 : sigc::trackable(other)
174 , _current_range_min(0)
175 , _current_range_max(0)
177 , _note_group (new ArdourCanvas::Group (get_canvas_group()))
178 , _note_diff_command (0)
180 , _step_edit_cursor (0)
181 , _step_edit_cursor_width (1.0)
182 , _step_edit_cursor_position (0.0)
183 , _channel_selection_scoped_note (0)
184 , _temporary_note_group (0)
187 , _sort_needed (true)
188 , _optimization_iterator (_events.end())
190 , _no_sound_notes (false)
193 , pre_enter_cursor (0)
194 , pre_press_cursor (0)
200 UINT_TO_RGBA (other.fill_color, &r, &g, &b, &a);
201 c.set_rgb_p (r/255.0, g/255.0, b/255.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::Group (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)
234 UINT_TO_RGBA (other.fill_color, &r, &g, &b, &a);
235 c.set_rgb_p (r/255.0, g/255.0, b/255.0);
241 MidiRegionView::init (Gdk::Color const & basic_color, bool wfd)
243 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
245 NoteBase::NoteBaseDeleted.connect (note_delete_connection, MISSING_INVALIDATOR,
246 boost::bind (&MidiRegionView::maybe_remove_deleted_note_from_selection, this, _1),
250 midi_region()->midi_source(0)->load_model();
253 _model = midi_region()->midi_source(0)->model();
254 _enable_display = false;
256 RegionView::init (basic_color, false);
258 compute_colors (basic_color);
260 set_height (trackview.current_height());
263 region_sync_changed ();
264 region_resized (ARDOUR::bounds_change);
269 _enable_display = true;
272 display_model (_model);
276 reset_width_dependent_items (_pixel_width);
278 group->raise_to_top();
279 group->Event.connect (sigc::mem_fun (this, &MidiRegionView::canvas_event));
282 midi_view()->midi_track()->PlaybackChannelModeChanged.connect (_channel_mode_changed_connection, invalidator (*this),
283 boost::bind (&MidiRegionView::midi_channel_mode_changed, this),
286 instrument_info().Changed.connect (_instrument_changed_connection, invalidator (*this),
287 boost::bind (&MidiRegionView::instrument_settings_changed, this), gui_context());
289 trackview.editor().SnapChanged.connect(snap_changed_connection, invalidator(*this),
290 boost::bind (&MidiRegionView::snap_changed, this),
293 Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&MidiRegionView::parameter_changed, this, _1), gui_context());
294 connect_to_diskstream ();
296 SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), boost::bind (&MidiRegionView::selection_cleared, this, _1), gui_context ());
300 MidiRegionView::instrument_info () const
302 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
303 return route_ui->route()->instrument_info();
306 const boost::shared_ptr<ARDOUR::MidiRegion>
307 MidiRegionView::midi_region() const
309 return boost::dynamic_pointer_cast<ARDOUR::MidiRegion>(_region);
313 MidiRegionView::connect_to_diskstream ()
315 midi_view()->midi_track()->DataRecorded.connect(
316 *this, invalidator(*this),
317 boost::bind (&MidiRegionView::data_recorded, this, _1),
322 MidiRegionView::canvas_event(GdkEvent* ev)
327 case GDK_ENTER_NOTIFY:
328 case GDK_LEAVE_NOTIFY:
329 _last_event_x = ev->crossing.x;
330 _last_event_y = ev->crossing.y;
332 case GDK_MOTION_NOTIFY:
333 _last_event_x = ev->motion.x;
334 _last_event_y = ev->motion.y;
340 if (ev->type == GDK_2BUTTON_PRESS) {
341 // cannot use double-click to exit internal mode if single-click is being used
342 MouseMode m = trackview.editor().current_mouse_mode();
344 if ((m != MouseObject || !Keyboard::modifier_state_contains (ev->button.state, Keyboard::insert_note_modifier())) && (m != MouseDraw)) {
345 return trackview.editor().toggle_internal_editing_from_double_click (ev);
349 if ((!trackview.editor().internal_editing() && trackview.editor().current_mouse_mode() != MouseGain) ||
350 (trackview.editor().current_mouse_mode() == MouseTimeFX) ||
351 (trackview.editor().current_mouse_mode() == MouseZoom)) {
352 // handle non-draw modes elsewhere
358 return scroll (&ev->scroll);
361 return key_press (&ev->key);
363 case GDK_KEY_RELEASE:
364 return key_release (&ev->key);
366 case GDK_BUTTON_PRESS:
367 return button_press (&ev->button);
369 case GDK_BUTTON_RELEASE:
370 r = button_release (&ev->button);
375 case GDK_ENTER_NOTIFY:
376 return enter_notify (&ev->crossing);
378 case GDK_LEAVE_NOTIFY:
379 return leave_notify (&ev->crossing);
381 case GDK_MOTION_NOTIFY:
382 return motion (&ev->motion);
392 MidiRegionView::remove_ghost_note ()
399 MidiRegionView::enter_notify (GdkEventCrossing* ev)
401 trackview.editor().MouseModeChanged.connect (
402 _mouse_mode_connection, invalidator (*this), boost::bind (&MidiRegionView::mouse_mode_changed, this), gui_context ()
405 if (trackview.editor().current_mouse_mode() == MouseDraw && _mouse_state != AddDragging) {
406 create_ghost_note (ev->x, ev->y);
409 if (!trackview.editor().internal_editing()) {
410 Keyboard::magic_widget_drop_focus();
412 Keyboard::magic_widget_grab_focus();
416 // if current operation is non-operational in a midi region, change the cursor to so indicate
417 if (trackview.editor().current_mouse_mode() == MouseGain) {
418 Editor* editor = dynamic_cast<Editor *> (&trackview.editor());
419 pre_enter_cursor = editor->get_canvas_cursor();
420 editor->set_canvas_cursor(editor->cursors()->timebar);
427 MidiRegionView::leave_notify (GdkEventCrossing*)
429 _mouse_mode_connection.disconnect ();
431 trackview.editor().verbose_cursor()->hide ();
432 remove_ghost_note ();
434 if (pre_enter_cursor) {
435 Editor* editor = dynamic_cast<Editor *> (&trackview.editor());
436 editor->set_canvas_cursor(pre_enter_cursor);
443 MidiRegionView::mouse_mode_changed ()
445 if (trackview.editor().current_mouse_mode() == MouseDraw && trackview.editor().internal_editing()) {
446 create_ghost_note (_last_event_x, _last_event_y);
448 remove_ghost_note ();
449 trackview.editor().verbose_cursor()->hide ();
452 if (!trackview.editor().internal_editing()) {
453 Keyboard::magic_widget_drop_focus();
455 Keyboard::magic_widget_grab_focus();
461 MidiRegionView::button_press (GdkEventButton* ev)
463 if (ev->button != 1) {
467 Editor* editor = dynamic_cast<Editor *> (&trackview.editor());
468 MouseMode m = editor->current_mouse_mode();
470 if (m == MouseObject && Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())) {
471 pre_press_cursor = editor->get_canvas_cursor ();
472 editor->set_canvas_cursor (editor->cursors()->midi_pencil);
475 if (_mouse_state != SelectTouchDragging) {
477 _pressed_button = ev->button;
478 _mouse_state = Pressed;
483 _pressed_button = ev->button;
489 MidiRegionView::button_release (GdkEventButton* ev)
491 double event_x, event_y;
493 if (ev->button != 1) {
500 group->canvas_to_item (event_x, event_y);
503 PublicEditor& editor = trackview.editor ();
505 if (pre_press_cursor) {
506 dynamic_cast<Editor*>(&editor)->set_canvas_cursor (pre_press_cursor, false);
507 pre_press_cursor = 0;
510 switch (_mouse_state) {
511 case Pressed: // Clicked
513 switch (editor.current_mouse_mode()) {
515 /* no motion occured - simple click */
524 if (Keyboard::is_insert_note_event(ev)) {
526 double event_x, event_y;
530 group->canvas_to_item (event_x, event_y);
533 Evoral::MusicalTime beats = editor.get_grid_type_as_beats (success, editor.pixel_to_frame (event_x));
539 /* Shorten the length by 1 tick so that we can add a new note at the next
540 grid snap without it overlapping this one.
542 beats -= 1.0 / Timecode::BBT_Time::ticks_per_beat;
544 create_note_at (editor.pixel_to_frame (event_x), event_y, beats, true);
552 Evoral::MusicalTime beats = editor.get_grid_type_as_beats (success, editor.pixel_to_frame (event_x));
558 /* Shorten the length by 1 tick so that we can add a new note at the next
559 grid snap without it overlapping this one.
561 beats -= 1.0 / Timecode::BBT_Time::ticks_per_beat;
563 create_note_at (editor.pixel_to_frame (event_x), event_y, beats, true);
574 case SelectRectDragging:
576 editor.drags()->end_grab ((GdkEvent *) ev);
578 create_ghost_note (ev->x, ev->y);
590 MidiRegionView::motion (GdkEventMotion* ev)
592 PublicEditor& editor = trackview.editor ();
594 if (!_ghost_note && editor.current_mouse_mode() == MouseObject &&
595 Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier()) &&
596 _mouse_state != AddDragging) {
598 create_ghost_note (ev->x, ev->y);
600 } else if (_ghost_note && editor.current_mouse_mode() == MouseObject &&
601 Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())) {
603 update_ghost_note (ev->x, ev->y);
605 } else if (_ghost_note && editor.current_mouse_mode() == MouseObject) {
607 remove_ghost_note ();
608 editor.verbose_cursor()->hide ();
610 } else if (_ghost_note && editor.current_mouse_mode() == MouseDraw) {
612 update_ghost_note (ev->x, ev->y);
615 /* any motion immediately hides velocity text that may have been visible */
617 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
618 (*i)->hide_velocity ();
621 switch (_mouse_state) {
624 if (_pressed_button == 1) {
626 MouseMode m = editor.current_mouse_mode();
628 if (m == MouseDraw || (m == MouseObject && Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier()))) {
630 editor.drags()->set (new NoteCreateDrag (dynamic_cast<Editor *> (&editor), group, this), (GdkEvent *) ev);
631 _mouse_state = AddDragging;
632 remove_ghost_note ();
633 editor.verbose_cursor()->hide ();
635 } else if (m == MouseObject) {
636 editor.drags()->set (new MidiRubberbandSelectDrag (dynamic_cast<Editor *> (&editor), this), (GdkEvent *) ev);
638 _mouse_state = SelectRectDragging;
640 } else if (m == MouseRange) {
641 editor.drags()->set (new MidiVerticalSelectDrag (dynamic_cast<Editor *> (&editor), this), (GdkEvent *) ev);
642 _mouse_state = SelectVerticalDragging;
649 case SelectRectDragging:
650 case SelectVerticalDragging:
652 editor.drags()->motion_handler ((GdkEvent *) ev, false);
655 case SelectTouchDragging:
667 MidiRegionView::scroll (GdkEventScroll* ev)
669 if (_selection.empty()) {
673 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
674 /* XXX: bit of a hack; allow PrimaryModifier scroll through so that
675 it still works for zoom.
680 trackview.editor().verbose_cursor()->hide ();
682 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
683 bool together = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
685 if (ev->direction == GDK_SCROLL_UP) {
686 change_velocities (true, fine, false, together);
687 } else if (ev->direction == GDK_SCROLL_DOWN) {
688 change_velocities (false, fine, false, together);
690 /* left, right: we don't use them */
698 MidiRegionView::key_press (GdkEventKey* ev)
700 /* since GTK bindings are generally activated on press, and since
701 detectable auto-repeat is the name of the game and only sends
702 repeated presses, carry out key actions at key press, not release.
705 bool unmodified = Keyboard::no_modifier_keys_pressed (ev);
707 if (unmodified && (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R)) {
708 _mouse_state = SelectTouchDragging;
711 } else if (ev->keyval == GDK_Escape && unmodified) {
715 } else if (unmodified && (ev->keyval == GDK_comma || ev->keyval == GDK_period)) {
717 bool start = (ev->keyval == GDK_comma);
718 bool end = (ev->keyval == GDK_period);
719 bool shorter = Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier);
720 bool fine = Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
722 change_note_lengths (fine, shorter, 0.0, start, end);
726 } else if ((ev->keyval == GDK_BackSpace || ev->keyval == GDK_Delete) && unmodified) {
728 if (_selection.empty()) {
735 } else if (ev->keyval == GDK_Tab) {
737 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
738 goto_previous_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
740 goto_next_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
744 } else if (ev->keyval == GDK_ISO_Left_Tab) {
746 /* Shift-TAB generates ISO Left Tab, for some reason */
748 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
749 goto_previous_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
751 goto_next_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
757 } else if (ev->keyval == GDK_Up) {
759 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
760 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
761 bool together = Keyboard::modifier_state_contains (ev->state, Keyboard::Level4Modifier);
763 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
764 change_velocities (true, fine, allow_smush, together);
766 transpose (true, fine, allow_smush);
770 } else if (ev->keyval == GDK_Down) {
772 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
773 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
774 bool together = Keyboard::modifier_state_contains (ev->state, Keyboard::Level4Modifier);
776 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
777 change_velocities (false, fine, allow_smush, together);
779 transpose (false, fine, allow_smush);
783 } else if (ev->keyval == GDK_Left && unmodified) {
788 } else if (ev->keyval == GDK_Right && unmodified) {
793 } else if (ev->keyval == GDK_c && unmodified) {
797 } else if (ev->keyval == GDK_v && unmodified) {
806 MidiRegionView::key_release (GdkEventKey* ev)
808 if ((_mouse_state == SelectTouchDragging) && (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R)) {
816 MidiRegionView::channel_edit ()
818 if (_selection.empty()) {
822 /* pick a note somewhat at random (since Selection is a set<>) to
823 * provide the "current" channel for the dialog.
826 uint8_t current_channel = (*_selection.begin())->note()->channel ();
827 MidiChannelDialog channel_dialog (current_channel);
828 int ret = channel_dialog.run ();
831 case Gtk::RESPONSE_OK:
837 uint8_t new_channel = channel_dialog.active_channel ();
839 start_note_diff_command (_("channel edit"));
841 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
842 Selection::iterator next = i;
844 change_note_channel (*i, new_channel);
852 MidiRegionView::velocity_edit ()
854 if (_selection.empty()) {
858 /* pick a note somewhat at random (since Selection is a set<>) to
859 * provide the "current" velocity for the dialog.
862 uint8_t current_velocity = (*_selection.begin())->note()->velocity ();
863 MidiVelocityDialog velocity_dialog (current_velocity);
864 int ret = velocity_dialog.run ();
867 case Gtk::RESPONSE_OK:
873 uint8_t new_velocity = velocity_dialog.velocity ();
875 start_note_diff_command (_("velocity edit"));
877 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
878 Selection::iterator next = i;
880 change_note_velocity (*i, new_velocity, false);
888 MidiRegionView::show_list_editor ()
891 _list_editor = new MidiListEditor (trackview.session(), midi_region(), midi_view()->midi_track());
893 _list_editor->present ();
896 /** Add a note to the model, and the view, at a canvas (click) coordinate.
897 * \param t time in frames relative to the position of the region
898 * \param y vertical position in pixels
899 * \param length duration of the note in beats
900 * \param snap_t true to snap t to the grid, otherwise false.
903 MidiRegionView::create_note_at (framepos_t t, double y, double length, bool snap_t)
905 if (length < 2 * DBL_EPSILON) {
909 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
910 MidiStreamView* const view = mtv->midi_view();
912 const double note = view->y_to_note(y);
914 // Start of note in frames relative to region start
916 framecnt_t grid_frames;
917 t = snap_frame_to_grid_underneath (t, grid_frames);
920 const boost::shared_ptr<NoteType> new_note (
921 new NoteType (mtv->get_channel_for_add (),
922 region_frames_to_region_beats(t + _region->start()),
924 (uint8_t)note, 0x40));
926 if (_model->contains (new_note)) {
930 view->update_note_range(new_note->note());
932 MidiModel::NoteDiffCommand* cmd = _model->new_note_diff_command(_("add note"));
934 _model->apply_command(*trackview.session(), cmd);
936 play_midi_note (new_note);
940 MidiRegionView::clear_events (bool with_selection_signal)
942 clear_selection (with_selection_signal);
945 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
946 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
951 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
956 _patch_changes.clear();
958 _optimization_iterator = _events.end();
962 MidiRegionView::display_model(boost::shared_ptr<MidiModel> model)
966 content_connection.disconnect ();
967 _model->ContentsChanged.connect (content_connection, invalidator (*this), boost::bind (&MidiRegionView::redisplay_model, this), gui_context());
971 if (_enable_display) {
977 MidiRegionView::start_note_diff_command (string name)
979 if (!_note_diff_command) {
980 _note_diff_command = _model->new_note_diff_command (name);
985 MidiRegionView::note_diff_add_note (const boost::shared_ptr<NoteType> note, bool selected, bool show_velocity)
987 if (_note_diff_command) {
988 _note_diff_command->add (note);
991 _marked_for_selection.insert(note);
994 _marked_for_velocity.insert(note);
999 MidiRegionView::note_diff_remove_note (NoteBase* ev)
1001 if (_note_diff_command && ev->note()) {
1002 _note_diff_command->remove(ev->note());
1007 MidiRegionView::note_diff_add_change (NoteBase* ev,
1008 MidiModel::NoteDiffCommand::Property property,
1011 if (_note_diff_command) {
1012 _note_diff_command->change (ev->note(), property, val);
1017 MidiRegionView::note_diff_add_change (NoteBase* ev,
1018 MidiModel::NoteDiffCommand::Property property,
1019 Evoral::MusicalTime val)
1021 if (_note_diff_command) {
1022 _note_diff_command->change (ev->note(), property, val);
1027 MidiRegionView::apply_diff (bool as_subcommand)
1031 if (!_note_diff_command) {
1035 if ((add_or_remove = _note_diff_command->adds_or_removes())) {
1036 // Mark all selected notes for selection when model reloads
1037 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1038 _marked_for_selection.insert((*i)->note());
1042 if (as_subcommand) {
1043 _model->apply_command_as_subcommand (*trackview.session(), _note_diff_command);
1045 _model->apply_command (*trackview.session(), _note_diff_command);
1048 _note_diff_command = 0;
1049 midi_view()->midi_track()->playlist_modified();
1051 if (add_or_remove) {
1052 _marked_for_selection.clear();
1055 _marked_for_velocity.clear();
1059 MidiRegionView::abort_command()
1061 delete _note_diff_command;
1062 _note_diff_command = 0;
1067 MidiRegionView::find_canvas_note (boost::shared_ptr<NoteType> note)
1069 if (_optimization_iterator != _events.end()) {
1070 ++_optimization_iterator;
1073 if (_optimization_iterator != _events.end() && (*_optimization_iterator)->note() == note) {
1074 return *_optimization_iterator;
1077 for (_optimization_iterator = _events.begin(); _optimization_iterator != _events.end(); ++_optimization_iterator) {
1078 if ((*_optimization_iterator)->note() == note) {
1079 return *_optimization_iterator;
1087 MidiRegionView::get_events (Events& e, Evoral::Sequence<Evoral::MusicalTime>::NoteOperator op, uint8_t val, int chan_mask)
1089 MidiModel::Notes notes;
1090 _model->get_notes (notes, op, val, chan_mask);
1092 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1093 NoteBase* cne = find_canvas_note (*n);
1101 MidiRegionView::redisplay_model()
1103 // Don't redisplay the model if we're currently recording and displaying that
1104 if (_active_notes) {
1112 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1113 (*i)->invalidate ();
1116 MidiModel::ReadLock lock(_model->read_lock());
1118 MidiModel::Notes& notes (_model->notes());
1119 _optimization_iterator = _events.begin();
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 ((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 ((cne = find_canvas_note (note)) != 0) {
1163 /* remove note items that are no longer valid */
1165 for (Events::iterator i = _events.begin(); i != _events.end(); ) {
1166 if (!(*i)->valid ()) {
1168 for (vector<GhostRegion*>::iterator j = ghosts.begin(); j != ghosts.end(); ++j) {
1169 MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*j);
1171 gr->remove_note (*i);
1176 i = _events.erase (i);
1183 _patch_changes.clear();
1187 display_patch_changes ();
1189 _marked_for_selection.clear ();
1190 _marked_for_velocity.clear ();
1192 /* we may have caused _events to contain things out of order (e.g. if a note
1193 moved earlier or later). we don't generally need them in time order, but
1194 make a note that a sort is required for those cases that require it.
1197 _sort_needed = true;
1201 MidiRegionView::display_patch_changes ()
1203 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1204 uint16_t chn_mask = mtv->midi_track()->get_playback_channel_mask();
1206 for (uint8_t i = 0; i < 16; ++i) {
1207 display_patch_changes_on_channel (i, chn_mask & (1 << i));
1211 /** @param active_channel true to display patch changes fully, false to display
1212 * them `greyed-out' (as on an inactive channel)
1215 MidiRegionView::display_patch_changes_on_channel (uint8_t channel, bool active_channel)
1217 for (MidiModel::PatchChanges::const_iterator i = _model->patch_changes().begin(); i != _model->patch_changes().end(); ++i) {
1219 if ((*i)->channel() != channel) {
1223 const string patch_name = instrument_info().get_patch_name ((*i)->bank(), (*i)->program(), channel);
1224 add_canvas_patch_change (*i, patch_name, active_channel);
1229 MidiRegionView::display_sysexes()
1231 bool have_periodic_system_messages = false;
1232 bool display_periodic_messages = true;
1234 if (!Config->get_never_display_periodic_midi()) {
1236 for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
1237 const boost::shared_ptr<const Evoral::MIDIEvent<Evoral::MusicalTime> > mev =
1238 boost::static_pointer_cast<const Evoral::MIDIEvent<Evoral::MusicalTime> > (*i);
1241 if (mev->is_spp() || mev->is_mtc_quarter() || mev->is_mtc_full()) {
1242 have_periodic_system_messages = true;
1248 if (have_periodic_system_messages) {
1249 double zoom = trackview.editor().get_current_zoom (); // frames per pixel
1251 /* get an approximate value for the number of samples per video frame */
1253 double video_frame = trackview.session()->frame_rate() * (1.0/30);
1255 /* if we are zoomed out beyond than the cutoff (i.e. more
1256 * frames per pixel than frames per 4 video frames), don't
1257 * show periodic sysex messages.
1260 if (zoom > (video_frame*4)) {
1261 display_periodic_messages = false;
1265 display_periodic_messages = false;
1268 for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
1270 const boost::shared_ptr<const Evoral::MIDIEvent<Evoral::MusicalTime> > mev =
1271 boost::static_pointer_cast<const Evoral::MIDIEvent<Evoral::MusicalTime> > (*i);
1273 Evoral::MusicalTime time = (*i)->time();
1276 if (mev->is_spp() || mev->is_mtc_quarter() || mev->is_mtc_full()) {
1277 if (!display_periodic_messages) {
1285 for (uint32_t b = 0; b < (*i)->size(); ++b) {
1286 str << int((*i)->buffer()[b]);
1287 if (b != (*i)->size() -1) {
1291 string text = str.str();
1293 const double x = trackview.editor().frame_to_pixel(source_beats_to_region_frames(time));
1295 double height = midi_stream_view()->contents_height();
1297 // CAIROCANVAS: no longer passing *i (the sysex event) to the
1298 // SysEx canvas object!!!
1300 boost::shared_ptr<SysEx> sysex = boost::shared_ptr<SysEx>(
1301 new SysEx (*this, _note_group, text, height, x, 1.0));
1303 // Show unless message is beyond the region bounds
1304 if (time - _region->start() >= _region->length() || time < _region->start()) {
1310 _sys_exes.push_back(sysex);
1314 MidiRegionView::~MidiRegionView ()
1316 in_destructor = true;
1318 trackview.editor().verbose_cursor()->hide ();
1320 note_delete_connection.disconnect ();
1322 delete _list_editor;
1324 RegionViewGoingAway (this); /* EMIT_SIGNAL */
1326 if (_active_notes) {
1330 _selection_cleared_connection.disconnect ();
1333 clear_events (false);
1336 delete _note_diff_command;
1337 delete _step_edit_cursor;
1338 delete _temporary_note_group;
1342 MidiRegionView::region_resized (const PropertyChange& what_changed)
1344 RegionView::region_resized(what_changed);
1346 if (what_changed.contains (ARDOUR::Properties::position)) {
1347 set_duration(_region->length(), 0);
1348 if (_enable_display) {
1355 MidiRegionView::reset_width_dependent_items (double pixel_width)
1357 RegionView::reset_width_dependent_items(pixel_width);
1359 if (_enable_display) {
1365 for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
1366 if ((*x)->width() >= _pixel_width) {
1374 move_step_edit_cursor (_step_edit_cursor_position);
1375 set_step_edit_cursor_width (_step_edit_cursor_width);
1379 MidiRegionView::set_height (double height)
1381 static const double FUDGE = 2.0;
1382 const double old_height = _height;
1383 RegionView::set_height(height);
1384 _height = height - FUDGE;
1386 apply_note_range(midi_stream_view()->lowest_note(),
1387 midi_stream_view()->highest_note(),
1388 height != old_height + FUDGE);
1391 name_pixbuf->raise_to_top();
1394 for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
1395 (*x)->set_height (midi_stream_view()->contents_height());
1398 if (_step_edit_cursor) {
1399 _step_edit_cursor->set_y1 (midi_stream_view()->contents_height());
1404 /** Apply the current note range from the stream view
1405 * by repositioning/hiding notes as necessary
1408 MidiRegionView::apply_note_range (uint8_t min, uint8_t max, bool force)
1410 if (!_enable_display) {
1414 if (!force && _current_range_min == min && _current_range_max == max) {
1418 _current_range_min = min;
1419 _current_range_max = max;
1421 for (Events::const_iterator i = _events.begin(); i != _events.end(); ++i) {
1422 NoteBase* event = *i;
1423 boost::shared_ptr<NoteType> note (event->note());
1425 if (note->note() < _current_range_min ||
1426 note->note() > _current_range_max) {
1432 if (Note* cnote = dynamic_cast<Note*>(event)) {
1434 const double y0 = midi_stream_view()->note_to_y(note->note());
1435 const double y1 = y0 + floor(midi_stream_view()->note_height());
1440 } else if (Hit* chit = dynamic_cast<Hit*>(event)) {
1442 const double diamond_size = update_hit (chit);
1444 chit->set_height (diamond_size);
1450 MidiRegionView::add_ghost (TimeAxisView& tv)
1454 double unit_position = _region->position () / frames_per_pixel;
1455 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(&tv);
1456 MidiGhostRegion* ghost;
1458 if (mtv && mtv->midi_view()) {
1459 /* if ghost is inserted into midi track, use a dedicated midi ghost canvas group
1460 to allow having midi notes on top of note lines and waveforms.
1462 ghost = new MidiGhostRegion (*mtv->midi_view(), trackview, unit_position);
1464 ghost = new MidiGhostRegion (tv, trackview, unit_position);
1467 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1468 if ((note = dynamic_cast<Note*>(*i)) != 0) {
1469 ghost->add_note(note);
1473 ghost->set_height ();
1474 ghost->set_duration (_region->length() / frames_per_pixel);
1475 ghosts.push_back (ghost);
1477 GhostRegion::CatchDeletion.connect (*this, invalidator (*this), boost::bind (&RegionView::remove_ghost, this, _1), gui_context());
1483 /** Begin tracking note state for successive calls to add_event
1486 MidiRegionView::begin_write()
1488 if (_active_notes) {
1489 delete[] _active_notes;
1491 _active_notes = new Note*[128];
1492 for (unsigned i = 0; i < 128; ++i) {
1493 _active_notes[i] = 0;
1498 /** Destroy note state for add_event
1501 MidiRegionView::end_write()
1503 delete[] _active_notes;
1505 _marked_for_selection.clear();
1506 _marked_for_velocity.clear();
1510 /** Resolve an active MIDI note (while recording).
1513 MidiRegionView::resolve_note(uint8_t note, double end_time)
1515 if (midi_view()->note_mode() != Sustained) {
1519 if (_active_notes && _active_notes[note]) {
1521 /* XXX is end_time really region-centric? I think so, because
1522 this is a new region that we're recording, so source zero is
1523 the same as region zero
1525 const framepos_t end_time_frames = region_beats_to_region_frames(end_time);
1527 _active_notes[note]->set_x1 (trackview.editor().frame_to_pixel(end_time_frames));
1528 _active_notes[note]->set_outline_what (0xf);
1529 _active_notes[note] = 0;
1534 /** Extend active notes to rightmost edge of region (if length is changed)
1537 MidiRegionView::extend_active_notes()
1539 if (!_active_notes) {
1543 for (unsigned i=0; i < 128; ++i) {
1544 if (_active_notes[i]) {
1545 _active_notes[i]->set_x1 (trackview.editor().frame_to_pixel(_region->length()));
1552 MidiRegionView::play_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 NotePlayer* np = new NotePlayer (route_ui->midi_track ());
1568 /* NotePlayer deletes itself */
1572 MidiRegionView::start_playing_midi_note(boost::shared_ptr<NoteType> note)
1574 if (_no_sound_notes || !Config->get_sound_midi_notes()) {
1578 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1580 if (!route_ui || !route_ui->midi_track()) {
1584 delete _note_player;
1585 _note_player = new NotePlayer (route_ui->midi_track ());
1586 _note_player->add (note);
1587 _note_player->on ();
1591 MidiRegionView::start_playing_midi_chord (vector<boost::shared_ptr<NoteType> > notes)
1593 if (_no_sound_notes || !Config->get_sound_midi_notes()) {
1597 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1599 if (!route_ui || !route_ui->midi_track()) {
1603 delete _note_player;
1604 _note_player = new NotePlayer (route_ui->midi_track());
1606 for (vector<boost::shared_ptr<NoteType> >::iterator n = notes.begin(); n != notes.end(); ++n) {
1607 _note_player->add (*n);
1610 _note_player->on ();
1615 MidiRegionView::note_in_region_range (const boost::shared_ptr<NoteType> note, bool& visible) const
1617 const framepos_t note_start_frames = source_beats_to_region_frames (note->time());
1618 bool outside = (note_start_frames < 0) || (note_start_frames > _region->last_frame());
1620 visible = (note->note() >= midi_stream_view()->lowest_note()) &&
1621 (note->note() <= midi_stream_view()->highest_note());
1626 /** Update a canvas note's size from its model note.
1627 * @param ev Canvas note to update.
1628 * @param update_ghost_regions true to update the note in any ghost regions that we have, otherwise false.
1631 MidiRegionView::update_note (Note* ev, bool update_ghost_regions)
1633 boost::shared_ptr<NoteType> note = ev->note();
1634 const double x = trackview.editor().frame_to_pixel (source_beats_to_region_frames (note->time()));
1635 const double y0 = midi_stream_view()->note_to_y(note->note());
1640 /* trim note display to not overlap the end of its region */
1642 if (note->length() > 0) {
1643 const framepos_t note_end_frames = min (source_beats_to_region_frames (note->end_time()), _region->length());
1644 ev->set_x1 (trackview.editor().frame_to_pixel (note_end_frames));
1646 ev->set_x1 (trackview.editor().frame_to_pixel (_region->length()));
1649 ev->set_y1 (y0 + floor(midi_stream_view()->note_height()));
1651 if (note->length() == 0) {
1652 if (_active_notes && note->note() < 128) {
1653 // If this note is already active there's a stuck note,
1654 // finish the old note rectangle
1655 if (_active_notes[note->note()]) {
1656 Note* const old_rect = _active_notes[note->note()];
1657 boost::shared_ptr<NoteType> old_note = old_rect->note();
1658 old_rect->set_x1 (x);
1659 old_rect->set_outline_what (0xF);
1661 _active_notes[note->note()] = ev;
1663 /* outline all but right edge */
1664 ev->set_outline_what (0x1 & 0x4 & 0x8);
1666 /* outline all edges */
1667 ev->set_outline_what (0xF);
1670 if (update_ghost_regions) {
1671 for (std::vector<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
1672 MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*i);
1674 gr->update_note (ev);
1681 MidiRegionView::update_hit (Hit* ev)
1683 boost::shared_ptr<NoteType> note = ev->note();
1685 const framepos_t note_start_frames = source_beats_to_region_frames(note->time());
1686 const double x = trackview.editor().frame_to_pixel(note_start_frames);
1687 const double diamond_size = midi_stream_view()->note_height() / 2.0;
1688 const double y = midi_stream_view()->note_to_y(note->note()) + ((diamond_size-2) / 4.0);
1690 ev->set_position (ArdourCanvas::Duple (x, y));
1692 return diamond_size;
1695 /** Add a MIDI note to the view (with length).
1697 * If in sustained mode, notes with length 0 will be considered active
1698 * notes, and resolve_note should be called when the corresponding note off
1699 * event arrives, to properly display the note.
1702 MidiRegionView::add_note(const boost::shared_ptr<NoteType> note, bool visible)
1704 NoteBase* event = 0;
1706 //ArdourCanvas::Group* const group = (ArdourCanvas::Group*) get_canvas_group();
1708 if (midi_view()->note_mode() == Sustained) {
1710 Note* ev_rect = new Note (*this, _note_group, note);
1712 update_note (ev_rect);
1716 MidiGhostRegion* gr;
1718 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
1719 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
1720 gr->add_note(ev_rect);
1724 } else if (midi_view()->note_mode() == Percussive) {
1726 const double diamond_size = midi_stream_view()->note_height() / 2.0;
1728 Hit* ev_diamond = new Hit (*this, _note_group, diamond_size, note);
1730 update_hit (ev_diamond);
1739 if (_marked_for_selection.find(note) != _marked_for_selection.end()) {
1740 note_selected(event, true);
1743 if (_marked_for_velocity.find(note) != _marked_for_velocity.end()) {
1744 event->show_velocity();
1747 event->on_channel_selection_change (get_selected_channels());
1748 _events.push_back(event);
1757 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1758 MidiStreamView* const view = mtv->midi_view();
1760 view->update_note_range (note->note());
1764 MidiRegionView::step_add_note (uint8_t channel, uint8_t number, uint8_t velocity,
1765 Evoral::MusicalTime pos, Evoral::MusicalTime len)
1767 boost::shared_ptr<NoteType> new_note (new NoteType (channel, pos, len, number, velocity));
1769 /* potentially extend region to hold new note */
1771 framepos_t end_frame = source_beats_to_absolute_frames (new_note->end_time());
1772 framepos_t region_end = _region->last_frame();
1774 if (end_frame > region_end) {
1775 _region->set_length (end_frame - _region->position());
1778 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1779 MidiStreamView* const view = mtv->midi_view();
1781 view->update_note_range(new_note->note());
1783 _marked_for_selection.clear ();
1786 start_note_diff_command (_("step add"));
1787 note_diff_add_note (new_note, true, false);
1790 // last_step_edit_note = new_note;
1794 MidiRegionView::step_sustain (Evoral::MusicalTime beats)
1796 change_note_lengths (false, false, beats, false, true);
1799 /** Add a new patch change flag to the canvas.
1800 * @param patch the patch change to add
1801 * @param the text to display in the flag
1802 * @param active_channel true to display the flag as on an active channel, false to grey it out for an inactive channel.
1805 MidiRegionView::add_canvas_patch_change (MidiModel::PatchChangePtr patch, const string& displaytext, bool /*active_channel*/)
1807 framecnt_t region_frames = source_beats_to_region_frames (patch->time());
1808 const double x = trackview.editor().frame_to_pixel (region_frames);
1810 double const height = midi_stream_view()->contents_height();
1812 // CAIROCANVAS: active_channel info removed from PatcChange constructor
1813 // so we need to do something more sophisticated to keep its color
1814 // appearance (MidiPatchChangeFill/MidiPatchChangeInactiveChannelFill)
1817 boost::shared_ptr<PatchChange> patch_change = boost::shared_ptr<PatchChange>(
1818 new PatchChange(*this, group,
1825 if (patch_change->item().width() < _pixel_width) {
1826 // Show unless patch change is beyond the region bounds
1827 if (region_frames < 0 || region_frames >= _region->length()) {
1828 patch_change->hide();
1830 patch_change->show();
1833 patch_change->hide ();
1836 _patch_changes.push_back (patch_change);
1839 MIDI::Name::PatchPrimaryKey
1840 MidiRegionView::patch_change_to_patch_key (MidiModel::PatchChangePtr p)
1842 return MIDI::Name::PatchPrimaryKey (p->program(), p->bank());
1845 /// Return true iff @p pc applies to the given time on the given channel.
1847 patch_applies (const ARDOUR::MidiModel::constPatchChangePtr pc, double time, uint8_t channel)
1849 return pc->time() <= time && pc->channel() == channel;
1853 MidiRegionView::get_patch_key_at (double time, uint8_t channel, MIDI::Name::PatchPrimaryKey& key) const
1855 // The earliest event not before time
1856 MidiModel::PatchChanges::iterator i = _model->patch_change_lower_bound (time);
1858 // Go backwards until we find the latest PC for this channel, or the start
1859 while (i != _model->patch_changes().begin() &&
1860 (i == _model->patch_changes().end() ||
1861 !patch_applies(*i, time, channel))) {
1865 if (i != _model->patch_changes().end() && patch_applies(*i, time, channel)) {
1866 key.bank_number = (*i)->bank();
1867 key.program_number = (*i)->program ();
1869 key.bank_number = key.program_number = 0;
1872 if (!key.is_sane()) {
1873 error << string_compose(_("insane MIDI patch key %1:%2"),
1874 key.bank_number, key.program_number) << endmsg;
1879 MidiRegionView::change_patch_change (PatchChange& pc, const MIDI::Name::PatchPrimaryKey& new_patch)
1881 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("alter patch change"));
1883 if (pc.patch()->program() != new_patch.program_number) {
1884 c->change_program (pc.patch (), new_patch.program_number);
1887 int const new_bank = new_patch.bank_number;
1888 if (pc.patch()->bank() != new_bank) {
1889 c->change_bank (pc.patch (), new_bank);
1892 _model->apply_command (*trackview.session(), c);
1894 _patch_changes.clear ();
1895 display_patch_changes ();
1899 MidiRegionView::change_patch_change (MidiModel::PatchChangePtr old_change, const Evoral::PatchChange<Evoral::MusicalTime> & new_change)
1901 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("alter patch change"));
1903 if (old_change->time() != new_change.time()) {
1904 c->change_time (old_change, new_change.time());
1907 if (old_change->channel() != new_change.channel()) {
1908 c->change_channel (old_change, new_change.channel());
1911 if (old_change->program() != new_change.program()) {
1912 c->change_program (old_change, new_change.program());
1915 if (old_change->bank() != new_change.bank()) {
1916 c->change_bank (old_change, new_change.bank());
1919 _model->apply_command (*trackview.session(), c);
1921 _patch_changes.clear ();
1922 display_patch_changes ();
1925 /** Add a patch change to the region.
1926 * @param t Time in frames relative to region position
1927 * @param patch Patch to add; time and channel are ignored (time is converted from t, and channel comes from
1928 * MidiTimeAxisView::get_channel_for_add())
1931 MidiRegionView::add_patch_change (framecnt_t t, Evoral::PatchChange<Evoral::MusicalTime> const & patch)
1933 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1935 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("add patch change"));
1936 c->add (MidiModel::PatchChangePtr (
1937 new Evoral::PatchChange<Evoral::MusicalTime> (
1938 absolute_frames_to_source_beats (_region->position() + t),
1939 mtv->get_channel_for_add(), patch.program(), patch.bank()
1944 _model->apply_command (*trackview.session(), c);
1946 _patch_changes.clear ();
1947 display_patch_changes ();
1951 MidiRegionView::move_patch_change (PatchChange& pc, Evoral::MusicalTime t)
1953 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("move patch change"));
1954 c->change_time (pc.patch (), t);
1955 _model->apply_command (*trackview.session(), c);
1957 _patch_changes.clear ();
1958 display_patch_changes ();
1962 MidiRegionView::delete_patch_change (PatchChange* pc)
1964 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("delete patch change"));
1965 c->remove (pc->patch ());
1966 _model->apply_command (*trackview.session(), c);
1968 _patch_changes.clear ();
1969 display_patch_changes ();
1973 MidiRegionView::previous_patch (PatchChange& patch)
1975 if (patch.patch()->program() < 127) {
1976 MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch());
1977 key.program_number++;
1978 change_patch_change (patch, key);
1983 MidiRegionView::next_patch (PatchChange& patch)
1985 if (patch.patch()->program() > 0) {
1986 MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch());
1987 key.program_number--;
1988 change_patch_change (patch, key);
1993 MidiRegionView::next_bank (PatchChange& patch)
1995 if (patch.patch()->program() < 127) {
1996 MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch());
1997 if (key.bank_number > 0) {
1999 change_patch_change (patch, key);
2005 MidiRegionView::previous_bank (PatchChange& patch)
2007 if (patch.patch()->program() > 0) {
2008 MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch());
2009 if (key.bank_number < 127) {
2011 change_patch_change (patch, key);
2017 MidiRegionView::maybe_remove_deleted_note_from_selection (NoteBase* cne)
2019 if (_selection.empty()) {
2023 _selection.erase (cne);
2027 MidiRegionView::delete_selection()
2029 if (_selection.empty()) {
2033 start_note_diff_command (_("delete selection"));
2035 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2036 if ((*i)->selected()) {
2037 _note_diff_command->remove((*i)->note());
2047 MidiRegionView::delete_note (boost::shared_ptr<NoteType> n)
2049 start_note_diff_command (_("delete note"));
2050 _note_diff_command->remove (n);
2053 trackview.editor().verbose_cursor()->hide ();
2057 MidiRegionView::clear_selection_except (NoteBase* ev, bool signal)
2059 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2061 Selection::iterator tmp = i;
2064 (*i)->set_selected (false);
2065 (*i)->hide_velocity ();
2066 _selection.erase (i);
2074 /* this does not change the status of this regionview w.r.t the editor
2079 SelectionCleared (this); /* EMIT SIGNAL */
2084 MidiRegionView::unique_select(NoteBase* ev)
2086 clear_selection_except (ev);
2088 /* don't bother with checking to see if we should remove this
2089 regionview from the editor selection, since we're about to add
2090 another note, and thus put/keep this regionview in the editor
2094 if (!ev->selected()) {
2095 add_to_selection (ev);
2100 MidiRegionView::select_all_notes ()
2104 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2105 add_to_selection (*i);
2110 MidiRegionView::select_range (framepos_t start, framepos_t end)
2114 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2115 framepos_t t = source_beats_to_absolute_frames((*i)->note()->time());
2116 if (t >= start && t <= end) {
2117 add_to_selection (*i);
2123 MidiRegionView::invert_selection ()
2125 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2126 if ((*i)->selected()) {
2127 remove_from_selection(*i);
2129 add_to_selection (*i);
2135 MidiRegionView::select_matching_notes (uint8_t notenum, uint16_t channel_mask, bool add, bool extend)
2137 uint8_t low_note = 127;
2138 uint8_t high_note = 0;
2139 MidiModel::Notes& notes (_model->notes());
2140 _optimization_iterator = _events.begin();
2146 if (extend && _selection.empty()) {
2152 /* scan existing selection to get note range */
2154 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2155 if ((*i)->note()->note() < low_note) {
2156 low_note = (*i)->note()->note();
2158 if ((*i)->note()->note() > high_note) {
2159 high_note = (*i)->note()->note();
2163 low_note = min (low_note, notenum);
2164 high_note = max (high_note, notenum);
2167 _no_sound_notes = true;
2169 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
2171 boost::shared_ptr<NoteType> note (*n);
2173 bool select = false;
2175 if (((1 << note->channel()) & channel_mask) != 0) {
2177 if ((note->note() >= low_note && note->note() <= high_note)) {
2180 } else if (note->note() == notenum) {
2186 if ((cne = find_canvas_note (note)) != 0) {
2187 // extend is false because we've taken care of it,
2188 // since it extends by time range, not pitch.
2189 note_selected (cne, add, false);
2193 add = true; // we need to add all remaining matching notes, even if the passed in value was false (for "set")
2197 _no_sound_notes = false;
2201 MidiRegionView::toggle_matching_notes (uint8_t notenum, uint16_t channel_mask)
2203 MidiModel::Notes& notes (_model->notes());
2204 _optimization_iterator = _events.begin();
2206 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
2208 boost::shared_ptr<NoteType> note (*n);
2211 if (note->note() == notenum && (((0x0001 << note->channel()) & channel_mask) != 0)) {
2212 if ((cne = find_canvas_note (note)) != 0) {
2213 if (cne->selected()) {
2214 note_deselected (cne);
2216 note_selected (cne, true, false);
2224 MidiRegionView::note_selected (NoteBase* ev, bool add, bool extend)
2227 clear_selection_except (ev);
2228 if (!_selection.empty()) {
2229 PublicEditor& editor (trackview.editor());
2230 editor.get_selection().add (this);
2236 if (!ev->selected()) {
2237 add_to_selection (ev);
2241 /* find end of latest note selected, select all between that and the start of "ev" */
2243 Evoral::MusicalTime earliest = Evoral::MaxMusicalTime;
2244 Evoral::MusicalTime latest = 0;
2246 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2247 if ((*i)->note()->end_time() > latest) {
2248 latest = (*i)->note()->end_time();
2250 if ((*i)->note()->time() < earliest) {
2251 earliest = (*i)->note()->time();
2255 if (ev->note()->end_time() > latest) {
2256 latest = ev->note()->end_time();
2259 if (ev->note()->time() < earliest) {
2260 earliest = ev->note()->time();
2263 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2265 /* find notes entirely within OR spanning the earliest..latest range */
2267 if (((*i)->note()->time() >= earliest && (*i)->note()->end_time() <= latest) ||
2268 ((*i)->note()->time() <= earliest && (*i)->note()->end_time() >= latest)) {
2269 add_to_selection (*i);
2277 MidiRegionView::note_deselected(NoteBase* ev)
2279 remove_from_selection (ev);
2283 MidiRegionView::update_drag_selection(double x0, double x1, double y0, double y1, bool extend)
2285 // TODO: Make this faster by storing the last updated selection rect, and only
2286 // adjusting things that are in the area that appears/disappeared.
2287 // We probably need a tree to be able to find events in O(log(n)) time.
2289 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2290 if ((*i)->x0() < x1 && (*i)->x1() > x0 && (*i)->y0() < y1 && (*i)->y1() > y0) {
2291 // Rectangles intersect
2292 if (!(*i)->selected()) {
2293 add_to_selection (*i);
2295 } else if ((*i)->selected() && !extend) {
2296 // Rectangles do not intersect
2297 remove_from_selection (*i);
2303 MidiRegionView::update_vertical_drag_selection (double y1, double y2, bool extend)
2309 // TODO: Make this faster by storing the last updated selection rect, and only
2310 // adjusting things that are in the area that appears/disappeared.
2311 // We probably need a tree to be able to find events in O(log(n)) time.
2313 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2314 if (((*i)->y1() >= y1 && (*i)->y1() <= y2)) {
2315 // within y- (note-) range
2316 if (!(*i)->selected()) {
2317 add_to_selection (*i);
2319 } else if ((*i)->selected() && !extend) {
2320 remove_from_selection (*i);
2326 MidiRegionView::remove_from_selection (NoteBase* ev)
2328 Selection::iterator i = _selection.find (ev);
2330 if (i != _selection.end()) {
2331 _selection.erase (i);
2334 ev->set_selected (false);
2335 ev->hide_velocity ();
2337 if (_selection.empty()) {
2338 PublicEditor& editor (trackview.editor());
2339 editor.get_selection().remove (this);
2344 MidiRegionView::add_to_selection (NoteBase* ev)
2346 bool add_mrv_selection = false;
2348 if (_selection.empty()) {
2349 add_mrv_selection = true;
2352 if (_selection.insert (ev).second) {
2353 ev->set_selected (true);
2354 start_playing_midi_note ((ev)->note());
2357 if (add_mrv_selection) {
2358 PublicEditor& editor (trackview.editor());
2359 editor.get_selection().add (this);
2364 MidiRegionView::move_selection(double dx, double dy, double cumulative_dy)
2366 typedef vector<boost::shared_ptr<NoteType> > PossibleChord;
2367 PossibleChord to_play;
2368 Evoral::MusicalTime earliest = Evoral::MaxMusicalTime;
2370 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2371 if ((*i)->note()->time() < earliest) {
2372 earliest = (*i)->note()->time();
2376 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2377 if (Evoral::musical_time_equal ((*i)->note()->time(), earliest)) {
2378 to_play.push_back ((*i)->note());
2380 (*i)->move_event(dx, dy);
2383 if (dy && !_selection.empty() && !_no_sound_notes && Config->get_sound_midi_notes()) {
2385 if (to_play.size() > 1) {
2387 PossibleChord shifted;
2389 for (PossibleChord::iterator n = to_play.begin(); n != to_play.end(); ++n) {
2390 boost::shared_ptr<NoteType> moved_note (new NoteType (**n));
2391 moved_note->set_note (moved_note->note() + cumulative_dy);
2392 shifted.push_back (moved_note);
2395 start_playing_midi_chord (shifted);
2397 } else if (!to_play.empty()) {
2399 boost::shared_ptr<NoteType> moved_note (new NoteType (*to_play.front()));
2400 moved_note->set_note (moved_note->note() + cumulative_dy);
2401 start_playing_midi_note (moved_note);
2407 MidiRegionView::note_dropped(NoteBase *, frameoffset_t dt, int8_t dnote)
2409 uint8_t lowest_note_in_selection = 127;
2410 uint8_t highest_note_in_selection = 0;
2411 uint8_t highest_note_difference = 0;
2413 // find highest and lowest notes first
2415 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2416 uint8_t pitch = (*i)->note()->note();
2417 lowest_note_in_selection = std::min(lowest_note_in_selection, pitch);
2418 highest_note_in_selection = std::max(highest_note_in_selection, pitch);
2422 cerr << "dnote: " << (int) dnote << endl;
2423 cerr << "lowest note (streamview): " << int(midi_stream_view()->lowest_note())
2424 << " highest note (streamview): " << int(midi_stream_view()->highest_note()) << endl;
2425 cerr << "lowest note (selection): " << int(lowest_note_in_selection) << " highest note(selection): "
2426 << int(highest_note_in_selection) << endl;
2427 cerr << "selection size: " << _selection.size() << endl;
2428 cerr << "Highest note in selection: " << (int) highest_note_in_selection << endl;
2431 // Make sure the note pitch does not exceed the MIDI standard range
2432 if (highest_note_in_selection + dnote > 127) {
2433 highest_note_difference = highest_note_in_selection - 127;
2436 start_note_diff_command (_("move notes"));
2438 for (Selection::iterator i = _selection.begin(); i != _selection.end() ; ++i) {
2440 framepos_t new_frames = source_beats_to_absolute_frames ((*i)->note()->time()) + dt;
2441 Evoral::MusicalTime new_time = absolute_frames_to_source_beats (new_frames);
2447 note_diff_add_change (*i, MidiModel::NoteDiffCommand::StartTime, new_time);
2449 uint8_t original_pitch = (*i)->note()->note();
2450 uint8_t new_pitch = original_pitch + dnote - highest_note_difference;
2452 // keep notes in standard midi range
2453 clamp_to_0_127(new_pitch);
2455 lowest_note_in_selection = std::min(lowest_note_in_selection, new_pitch);
2456 highest_note_in_selection = std::max(highest_note_in_selection, new_pitch);
2458 note_diff_add_change (*i, MidiModel::NoteDiffCommand::NoteNumber, new_pitch);
2463 // care about notes being moved beyond the upper/lower bounds on the canvas
2464 if (lowest_note_in_selection < midi_stream_view()->lowest_note() ||
2465 highest_note_in_selection > midi_stream_view()->highest_note()) {
2466 midi_stream_view()->set_note_range(MidiStreamView::ContentsRange);
2470 /** @param x Pixel relative to the region position.
2471 * @return Snapped frame relative to the region position.
2474 MidiRegionView::snap_pixel_to_frame(double x)
2476 PublicEditor& editor (trackview.editor());
2477 return snap_frame_to_frame (editor.pixel_to_frame (x));
2480 /** @param x Pixel relative to the region position.
2481 * @return Snapped pixel relative to the region position.
2484 MidiRegionView::snap_to_pixel(double x)
2486 return (double) trackview.editor().frame_to_pixel(snap_pixel_to_frame(x));
2490 MidiRegionView::get_position_pixels()
2492 framepos_t region_frame = get_position();
2493 return trackview.editor().frame_to_pixel(region_frame);
2497 MidiRegionView::get_end_position_pixels()
2499 framepos_t frame = get_position() + get_duration ();
2500 return trackview.editor().frame_to_pixel(frame);
2504 MidiRegionView::source_beats_to_absolute_frames(double beats) const
2506 /* the time converter will return the frame corresponding to `beats'
2507 relative to the start of the source. The start of the source
2508 is an implied position given by region->position - region->start
2510 const framepos_t source_start = _region->position() - _region->start();
2511 return source_start + _source_relative_time_converter.to (beats);
2515 MidiRegionView::absolute_frames_to_source_beats(framepos_t frames) const
2517 /* the `frames' argument needs to be converted into a frame count
2518 relative to the start of the source before being passed in to the
2521 const framepos_t source_start = _region->position() - _region->start();
2522 return _source_relative_time_converter.from (frames - source_start);
2526 MidiRegionView::region_beats_to_region_frames(double beats) const
2528 return _region_relative_time_converter.to(beats);
2532 MidiRegionView::region_frames_to_region_beats(framepos_t frames) const
2534 return _region_relative_time_converter.from(frames);
2538 MidiRegionView::begin_resizing (bool /*at_front*/)
2540 _resize_data.clear();
2542 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2543 Note *note = dynamic_cast<Note*> (*i);
2545 // only insert CanvasNotes into the map
2547 NoteResizeData *resize_data = new NoteResizeData();
2548 resize_data->note = note;
2550 // create a new SimpleRect from the note which will be the resize preview
2551 ArdourCanvas::Rectangle *resize_rect = new ArdourCanvas::Rectangle (_note_group,
2552 ArdourCanvas::Rect (note->x0(), note->y0(), note->x0(), note->y1()));
2554 // calculate the colors: get the color settings
2555 uint32_t fill_color = UINT_RGBA_CHANGE_A(
2556 ARDOUR_UI::config()->canvasvar_MidiNoteSelected.get(),
2559 // make the resize preview notes more transparent and bright
2560 fill_color = UINT_INTERPOLATE(fill_color, 0xFFFFFF40, 0.5);
2562 // calculate color based on note velocity
2563 resize_rect->set_fill_color (UINT_INTERPOLATE(
2564 NoteBase::meter_style_fill_color(note->note()->velocity(), note->selected()),
2568 resize_rect->set_outline_color (NoteBase::calculate_outline (
2569 ARDOUR_UI::config()->canvasvar_MidiNoteSelected.get()));
2571 resize_data->resize_rect = resize_rect;
2572 _resize_data.push_back(resize_data);
2577 /** Update resizing notes while user drags.
2578 * @param primary `primary' note for the drag; ie the one that is used as the reference in non-relative mode.
2579 * @param at_front which end of the note (true == note on, false == note off)
2580 * @param delta_x change in mouse position since the start of the drag
2581 * @param relative true if relative resizing is taking place, false if absolute resizing. This only makes
2582 * a difference when multiple notes are being resized; in relative mode, each note's length is changed by the
2583 * amount of the drag. In non-relative mode, all selected notes are set to have the same start or end point
2584 * as the \a primary note.
2587 MidiRegionView::update_resizing (NoteBase* primary, bool at_front, double delta_x, bool relative)
2589 bool cursor_set = false;
2591 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2592 ArdourCanvas::Rectangle* resize_rect = (*i)->resize_rect;
2593 Note* canvas_note = (*i)->note;
2598 current_x = canvas_note->x0() + delta_x;
2600 current_x = primary->x0() + delta_x;
2604 current_x = canvas_note->x1() + delta_x;
2606 current_x = primary->x1() + delta_x;
2611 resize_rect->set_x0 (snap_to_pixel(current_x));
2612 resize_rect->set_x1 (canvas_note->x1());
2614 resize_rect->set_x1 (snap_to_pixel(current_x));
2615 resize_rect->set_x0 (canvas_note->x0());
2621 beats = snap_pixel_to_frame (current_x);
2622 beats = region_frames_to_region_beats (beats);
2627 if (beats < canvas_note->note()->end_time()) {
2628 len = canvas_note->note()->time() - beats;
2629 len += canvas_note->note()->length();
2634 if (beats >= canvas_note->note()->time()) {
2635 len = beats - canvas_note->note()->time();
2642 snprintf (buf, sizeof (buf), "%.3g beats", len);
2643 show_verbose_cursor (buf, 0, 0);
2652 /** Finish resizing notes when the user releases the mouse button.
2653 * Parameters the same as for \a update_resizing().
2656 MidiRegionView::commit_resizing (NoteBase* primary, bool at_front, double delta_x, bool relative)
2658 start_note_diff_command (_("resize notes"));
2660 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2661 Note* canvas_note = (*i)->note;
2662 ArdourCanvas::Rectangle* resize_rect = (*i)->resize_rect;
2664 /* Get the new x position for this resize, which is in pixels relative
2665 * to the region position.
2672 current_x = canvas_note->x0() + delta_x;
2674 current_x = primary->x0() + delta_x;
2678 current_x = canvas_note->x1() + delta_x;
2680 current_x = primary->x1() + delta_x;
2684 /* Convert that to a frame within the source */
2685 current_x = snap_pixel_to_frame (current_x) + _region->start ();
2687 /* and then to beats */
2688 current_x = region_frames_to_region_beats (current_x);
2690 if (at_front && current_x < canvas_note->note()->end_time()) {
2691 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::StartTime, current_x);
2693 double len = canvas_note->note()->time() - current_x;
2694 len += canvas_note->note()->length();
2697 /* XXX convert to beats */
2698 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
2703 double len = current_x - canvas_note->note()->time();
2706 /* XXX convert to beats */
2707 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
2715 _resize_data.clear();
2720 MidiRegionView::abort_resizing ()
2722 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2723 delete (*i)->resize_rect;
2727 _resize_data.clear ();
2731 MidiRegionView::change_note_velocity(NoteBase* event, int8_t velocity, bool relative)
2733 uint8_t new_velocity;
2736 new_velocity = event->note()->velocity() + velocity;
2737 clamp_to_0_127(new_velocity);
2739 new_velocity = velocity;
2742 event->set_selected (event->selected()); // change color
2744 note_diff_add_change (event, MidiModel::NoteDiffCommand::Velocity, new_velocity);
2748 MidiRegionView::change_note_note (NoteBase* event, int8_t note, bool relative)
2753 new_note = event->note()->note() + note;
2758 clamp_to_0_127 (new_note);
2759 note_diff_add_change (event, MidiModel::NoteDiffCommand::NoteNumber, new_note);
2763 MidiRegionView::trim_note (NoteBase* event, Evoral::MusicalTime front_delta, Evoral::MusicalTime end_delta)
2765 bool change_start = false;
2766 bool change_length = false;
2767 Evoral::MusicalTime new_start = 0;
2768 Evoral::MusicalTime new_length = 0;
2770 /* NOTE: the semantics of the two delta arguments are slightly subtle:
2772 front_delta: if positive - move the start of the note later in time (shortening it)
2773 if negative - move the start of the note earlier in time (lengthening it)
2775 end_delta: if positive - move the end of the note later in time (lengthening it)
2776 if negative - move the end of the note earlier in time (shortening it)
2780 if (front_delta < 0) {
2782 if (event->note()->time() < -front_delta) {
2785 new_start = event->note()->time() + front_delta; // moves earlier
2788 /* start moved toward zero, so move the end point out to where it used to be.
2789 Note that front_delta is negative, so this increases the length.
2792 new_length = event->note()->length() - front_delta;
2793 change_start = true;
2794 change_length = true;
2798 Evoral::MusicalTime new_pos = event->note()->time() + front_delta;
2800 if (new_pos < event->note()->end_time()) {
2801 new_start = event->note()->time() + front_delta;
2802 /* start moved toward the end, so move the end point back to where it used to be */
2803 new_length = event->note()->length() - front_delta;
2804 change_start = true;
2805 change_length = true;
2812 bool can_change = true;
2813 if (end_delta < 0) {
2814 if (event->note()->length() < -end_delta) {
2820 new_length = event->note()->length() + end_delta;
2821 change_length = true;
2826 note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_start);
2829 if (change_length) {
2830 note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, new_length);
2835 MidiRegionView::change_note_channel (NoteBase* event, int8_t chn, bool relative)
2837 uint8_t new_channel;
2841 if (event->note()->channel() < -chn) {
2844 new_channel = event->note()->channel() + chn;
2847 new_channel = event->note()->channel() + chn;
2850 new_channel = (uint8_t) chn;
2853 note_diff_add_change (event, MidiModel::NoteDiffCommand::Channel, new_channel);
2857 MidiRegionView::change_note_time (NoteBase* event, Evoral::MusicalTime delta, bool relative)
2859 Evoral::MusicalTime new_time;
2863 if (event->note()->time() < -delta) {
2866 new_time = event->note()->time() + delta;
2869 new_time = event->note()->time() + delta;
2875 note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_time);
2879 MidiRegionView::change_note_length (NoteBase* event, Evoral::MusicalTime t)
2881 note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, t);
2885 MidiRegionView::change_velocities (bool up, bool fine, bool allow_smush, bool all_together)
2890 if (_selection.empty()) {
2905 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2906 if ((*i)->note()->velocity() < -delta || (*i)->note()->velocity() + delta > 127) {
2912 start_note_diff_command (_("change velocities"));
2914 for (Selection::iterator i = _selection.begin(); i != _selection.end();) {
2915 Selection::iterator next = i;
2919 if (i == _selection.begin()) {
2920 change_note_velocity (*i, delta, true);
2921 value = (*i)->note()->velocity() + delta;
2923 change_note_velocity (*i, value, false);
2927 change_note_velocity (*i, delta, true);
2936 if (!_selection.empty()) {
2938 snprintf (buf, sizeof (buf), "Vel %d",
2939 (int) (*_selection.begin())->note()->velocity());
2940 show_verbose_cursor (buf, 10, 10);
2946 MidiRegionView::transpose (bool up, bool fine, bool allow_smush)
2948 if (_selection.empty()) {
2965 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2967 if ((int8_t) (*i)->note()->note() + delta <= 0) {
2971 if ((int8_t) (*i)->note()->note() + delta > 127) {
2978 start_note_diff_command (_("transpose"));
2980 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2981 Selection::iterator next = i;
2983 change_note_note (*i, delta, true);
2991 MidiRegionView::change_note_lengths (bool fine, bool shorter, Evoral::MusicalTime delta, bool start, bool end)
2997 /* grab the current grid distance */
2999 delta = trackview.editor().get_grid_type_as_beats (success, _region->position());
3001 /* XXX cannot get grid type as beats ... should always be possible ... FIX ME */
3002 error << string_compose (_("programming error: %1"), "Grid type not available as beats - TO BE FIXED") << endmsg;
3012 start_note_diff_command (_("change note lengths"));
3014 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3015 Selection::iterator next = i;
3018 /* note the negation of the delta for start */
3020 trim_note (*i, (start ? -delta : 0), (end ? delta : 0));
3029 MidiRegionView::nudge_notes (bool forward)
3031 if (_selection.empty()) {
3035 /* pick a note as the point along the timeline to get the nudge distance.
3036 its not necessarily the earliest note, so we may want to pull the notes out
3037 into a vector and sort before using the first one.
3040 framepos_t ref_point = source_beats_to_absolute_frames ((*(_selection.begin()))->note()->time());
3042 framecnt_t distance;
3044 if (trackview.editor().snap_mode() == Editing::SnapOff) {
3046 /* grid is off - use nudge distance */
3048 distance = trackview.editor().get_nudge_distance (ref_point, unused);
3054 framepos_t next_pos = ref_point;
3057 if (max_framepos - 1 < next_pos) {
3061 if (next_pos == 0) {
3067 trackview.editor().snap_to (next_pos, (forward ? 1 : -1), false);
3068 distance = ref_point - next_pos;
3071 if (distance == 0) {
3075 Evoral::MusicalTime delta = region_frames_to_region_beats (fabs (distance));
3081 start_note_diff_command (_("nudge"));
3083 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3084 Selection::iterator next = i;
3086 change_note_time (*i, delta, true);
3094 MidiRegionView::change_channel(uint8_t channel)
3096 start_note_diff_command(_("change channel"));
3097 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3098 note_diff_add_change (*i, MidiModel::NoteDiffCommand::Channel, channel);
3106 MidiRegionView::note_entered(NoteBase* ev)
3108 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3110 pre_enter_cursor = editor->get_canvas_cursor ();
3112 if (_mouse_state == SelectTouchDragging) {
3113 note_selected (ev, true);
3116 show_verbose_cursor (ev->note ());
3120 MidiRegionView::note_left (NoteBase*)
3122 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3124 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3125 (*i)->hide_velocity ();
3128 editor->verbose_cursor()->hide ();
3130 if (pre_enter_cursor) {
3131 editor->set_canvas_cursor (pre_enter_cursor);
3132 pre_enter_cursor = 0;
3137 MidiRegionView::patch_entered (PatchChange* p)
3140 /* XXX should get patch name if we can */
3141 s << _("Bank ") << (p->patch()->bank() + MIDI_BP_ZERO) << '\n'
3142 << _("Program ") << ((int) p->patch()->program()) + MIDI_BP_ZERO << '\n'
3143 << _("Channel ") << ((int) p->patch()->channel() + 1);
3144 show_verbose_cursor (s.str(), 10, 20);
3145 p->item().grab_focus();
3149 MidiRegionView::patch_left (PatchChange *)
3151 trackview.editor().verbose_cursor()->hide ();
3152 /* focus will transfer back via the enter-notify event sent to this
3158 MidiRegionView::sysex_entered (SysEx* p)
3162 // need a way to extract text from p->_flag->_text
3164 // show_verbose_cursor (s.str(), 10, 20);
3165 p->item().grab_focus();
3169 MidiRegionView::sysex_left (SysEx *)
3171 trackview.editor().verbose_cursor()->hide ();
3172 /* focus will transfer back via the enter-notify event sent to this
3178 MidiRegionView::note_mouse_position (float x_fraction, float /*y_fraction*/, bool can_set_cursor)
3180 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3181 Editing::MouseMode mm = editor->current_mouse_mode();
3182 bool trimmable = (mm == MouseObject || mm == MouseTimeFX || mm == MouseDraw);
3184 if (trimmable && x_fraction > 0.0 && x_fraction < 0.2) {
3185 editor->set_canvas_cursor (editor->cursors()->left_side_trim);
3186 } else if (trimmable && x_fraction >= 0.8 && x_fraction < 1.0) {
3187 editor->set_canvas_cursor (editor->cursors()->right_side_trim);
3189 if (pre_enter_cursor && can_set_cursor) {
3190 editor->set_canvas_cursor (pre_enter_cursor);
3196 MidiRegionView::set_frame_color()
3200 TimeAxisViewItem::set_frame_color ();
3207 f = ARDOUR_UI::config()->canvasvar_SelectedFrameBase.get();
3208 } else if (high_enough_for_name) {
3209 f= ARDOUR_UI::config()->canvasvar_MidiFrameBase.get();
3214 if (!rect_visible) {
3215 f = UINT_RGBA_CHANGE_A (f, 0);
3218 frame->set_fill_color (f);
3222 MidiRegionView::midi_channel_mode_changed ()
3224 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3225 uint16_t mask = mtv->midi_track()->get_playback_channel_mask();
3226 ChannelMode mode = mtv->midi_track()->get_playback_channel_mode ();
3228 if (mode == ForceChannel) {
3229 mask = 0xFFFF; // Show all notes as active (below)
3232 // Update notes for selection
3233 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3234 (*i)->on_channel_selection_change (mask);
3237 _patch_changes.clear ();
3238 display_patch_changes ();
3242 MidiRegionView::instrument_settings_changed ()
3248 MidiRegionView::cut_copy_clear (Editing::CutCopyOp op)
3250 if (_selection.empty()) {
3254 PublicEditor& editor (trackview.editor());
3258 /* XXX what to do ? */
3262 editor.get_cut_buffer().add (selection_as_cut_buffer());
3270 start_note_diff_command();
3272 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3279 note_diff_remove_note (*i);
3289 MidiRegionView::selection_as_cut_buffer () const
3293 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3294 NoteType* n = (*i)->note().get();
3295 notes.insert (boost::shared_ptr<NoteType> (new NoteType (*n)));
3298 MidiCutBuffer* cb = new MidiCutBuffer (trackview.session());
3304 /** This method handles undo */
3306 MidiRegionView::paste (framepos_t pos, float times, const MidiCutBuffer& mcb)
3312 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("MIDI paste @ %1 times %2\n", pos, times));
3314 trackview.session()->begin_reversible_command (_("paste"));
3316 start_note_diff_command (_("paste"));
3318 Evoral::MusicalTime beat_delta;
3319 Evoral::MusicalTime paste_pos_beats;
3320 Evoral::MusicalTime duration;
3321 Evoral::MusicalTime end_point = 0;
3323 duration = (*mcb.notes().rbegin())->end_time() - (*mcb.notes().begin())->time();
3324 paste_pos_beats = absolute_frames_to_source_beats (pos);
3325 beat_delta = (*mcb.notes().begin())->time() - paste_pos_beats;
3326 paste_pos_beats = 0;
3328 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",
3329 (*mcb.notes().begin())->time(),
3330 (*mcb.notes().rbegin())->end_time(),
3331 duration, pos, _region->position(),
3332 paste_pos_beats, beat_delta));
3336 for (int n = 0; n < (int) times; ++n) {
3338 for (Notes::const_iterator i = mcb.notes().begin(); i != mcb.notes().end(); ++i) {
3340 boost::shared_ptr<NoteType> copied_note (new NoteType (*((*i).get())));
3341 copied_note->set_time (paste_pos_beats + copied_note->time() - beat_delta);
3343 /* make all newly added notes selected */
3345 note_diff_add_note (copied_note, true);
3346 end_point = copied_note->end_time();
3349 paste_pos_beats += duration;
3352 /* if we pasted past the current end of the region, extend the region */
3354 framepos_t end_frame = source_beats_to_absolute_frames (end_point);
3355 framepos_t region_end = _region->position() + _region->length() - 1;
3357 if (end_frame > region_end) {
3359 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste extended region from %1 to %2\n", region_end, end_frame));
3361 _region->clear_changes ();
3362 _region->set_length (end_frame - _region->position());
3363 trackview.session()->add_command (new StatefulDiffCommand (_region));
3368 trackview.session()->commit_reversible_command ();
3371 struct EventNoteTimeEarlyFirstComparator {
3372 bool operator() (NoteBase* a, NoteBase* b) {
3373 return a->note()->time() < b->note()->time();
3378 MidiRegionView::time_sort_events ()
3380 if (!_sort_needed) {
3384 EventNoteTimeEarlyFirstComparator cmp;
3387 _sort_needed = false;
3391 MidiRegionView::goto_next_note (bool add_to_selection)
3393 bool use_next = false;
3395 if (_events.back()->selected()) {
3399 time_sort_events ();
3401 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3402 uint16_t const channel_mask = mtv->midi_track()->get_playback_channel_mask();
3404 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3405 if ((*i)->selected()) {
3408 } else if (use_next) {
3409 if (channel_mask & (1 << (*i)->note()->channel())) {
3410 if (!add_to_selection) {
3413 note_selected (*i, true, false);
3420 /* use the first one */
3422 if (!_events.empty() && (channel_mask & (1 << _events.front()->note()->channel ()))) {
3423 unique_select (_events.front());
3428 MidiRegionView::goto_previous_note (bool add_to_selection)
3430 bool use_next = false;
3432 if (_events.front()->selected()) {
3436 time_sort_events ();
3438 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3439 uint16_t const channel_mask = mtv->midi_track()->get_playback_channel_mask ();
3441 for (Events::reverse_iterator i = _events.rbegin(); i != _events.rend(); ++i) {
3442 if ((*i)->selected()) {
3445 } else if (use_next) {
3446 if (channel_mask & (1 << (*i)->note()->channel())) {
3447 if (!add_to_selection) {
3450 note_selected (*i, true, false);
3457 /* use the last one */
3459 if (!_events.empty() && (channel_mask & (1 << (*_events.rbegin())->note()->channel ()))) {
3460 unique_select (*(_events.rbegin()));
3465 MidiRegionView::selection_as_notelist (Notes& selected, bool allow_all_if_none_selected)
3467 bool had_selected = false;
3469 time_sort_events ();
3471 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3472 if ((*i)->selected()) {
3473 selected.insert ((*i)->note());
3474 had_selected = true;
3478 if (allow_all_if_none_selected && !had_selected) {
3479 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3480 selected.insert ((*i)->note());
3486 MidiRegionView::update_ghost_note (double x, double y)
3488 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3493 _note_group->canvas_to_item (x, y);
3495 PublicEditor& editor = trackview.editor ();
3497 framepos_t const unsnapped_frame = editor.pixel_to_frame (x);
3498 framecnt_t grid_frames;
3499 framepos_t const f = snap_frame_to_grid_underneath (unsnapped_frame, grid_frames);
3501 /* use region_frames... because we are converting a delta within the region
3505 double length = editor.get_grid_type_as_beats (success, unsnapped_frame);
3511 /* note that this sets the time of the ghost note in beats relative to
3512 the start of the source; that is how all note times are stored.
3514 _ghost_note->note()->set_time (absolute_frames_to_source_beats (f + _region->position ()));
3515 _ghost_note->note()->set_length (length);
3516 _ghost_note->note()->set_note (midi_stream_view()->y_to_note (y));
3517 _ghost_note->note()->set_channel (mtv->get_channel_for_add ());
3519 /* the ghost note does not appear in ghost regions, so pass false in here */
3520 update_note (_ghost_note, false);
3522 show_verbose_cursor (_ghost_note->note ());
3526 MidiRegionView::create_ghost_note (double x, double y)
3528 remove_ghost_note ();
3530 boost::shared_ptr<NoteType> g (new NoteType);
3531 _ghost_note = new Note (*this, _note_group, g);
3532 _ghost_note->set_ignore_events (true);
3533 _ghost_note->set_outline_color (0x000000aa);
3534 update_ghost_note (x, y);
3535 _ghost_note->show ();
3540 show_verbose_cursor (_ghost_note->note ());
3544 MidiRegionView::snap_changed ()
3550 create_ghost_note (_last_ghost_x, _last_ghost_y);
3554 MidiRegionView::drop_down_keys ()
3556 _mouse_state = None;
3560 MidiRegionView::maybe_select_by_position (GdkEventButton* ev, double /*x*/, double y)
3562 double note = midi_stream_view()->y_to_note(y);
3564 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3566 uint16_t chn_mask = mtv->midi_track()->get_playback_channel_mask();
3568 if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
3569 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchGreaterThanOrEqual, (uint8_t) floor (note), chn_mask);
3570 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
3571 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchLessThanOrEqual, (uint8_t) floor (note), chn_mask);
3576 bool add_mrv_selection = false;
3578 if (_selection.empty()) {
3579 add_mrv_selection = true;
3582 for (Events::iterator i = e.begin(); i != e.end(); ++i) {
3583 if (_selection.insert (*i).second) {
3584 (*i)->set_selected (true);
3588 if (add_mrv_selection) {
3589 PublicEditor& editor (trackview.editor());
3590 editor.get_selection().add (this);
3595 MidiRegionView::color_handler ()
3597 RegionView::color_handler ();
3599 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3600 (*i)->set_selected ((*i)->selected()); // will change color
3603 /* XXX probably more to do here */
3607 MidiRegionView::enable_display (bool yn)
3609 RegionView::enable_display (yn);
3616 MidiRegionView::show_step_edit_cursor (Evoral::MusicalTime pos)
3618 if (_step_edit_cursor == 0) {
3619 ArdourCanvas::Group* const group = (ArdourCanvas::Group*)get_canvas_group();
3621 _step_edit_cursor = new ArdourCanvas::Rectangle (group);
3622 _step_edit_cursor->set_y0 (0);
3623 _step_edit_cursor->set_y1 (midi_stream_view()->contents_height());
3624 _step_edit_cursor->set_fill_color (RGBA_TO_UINT (45,0,0,90));
3625 _step_edit_cursor->set_outline_color (RGBA_TO_UINT (85,0,0,90));
3628 move_step_edit_cursor (pos);
3629 _step_edit_cursor->show ();
3633 MidiRegionView::move_step_edit_cursor (Evoral::MusicalTime pos)
3635 _step_edit_cursor_position = pos;
3637 if (_step_edit_cursor) {
3638 double pixel = trackview.editor().frame_to_pixel (region_beats_to_region_frames (pos));
3639 _step_edit_cursor->set_x0 (pixel);
3640 set_step_edit_cursor_width (_step_edit_cursor_width);
3645 MidiRegionView::hide_step_edit_cursor ()
3647 if (_step_edit_cursor) {
3648 _step_edit_cursor->hide ();
3653 MidiRegionView::set_step_edit_cursor_width (Evoral::MusicalTime beats)
3655 _step_edit_cursor_width = beats;
3657 if (_step_edit_cursor) {
3658 _step_edit_cursor->set_x1 (_step_edit_cursor->x0() + trackview.editor().frame_to_pixel (region_beats_to_region_frames (beats)));
3662 /** Called when a diskstream on our track has received some data. Update the view, if applicable.
3663 * @param w Source that the data will end up in.
3666 MidiRegionView::data_recorded (boost::weak_ptr<MidiSource> w)
3668 if (!_active_notes) {
3669 /* we aren't actively being recorded to */
3673 boost::shared_ptr<MidiSource> src = w.lock ();
3674 if (!src || src != midi_region()->midi_source()) {
3675 /* recorded data was not destined for our source */
3679 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (&trackview);
3681 boost::shared_ptr<MidiBuffer> buf = mtv->midi_track()->get_gui_feed_buffer ();
3683 BeatsFramesConverter converter (trackview.session()->tempo_map(), mtv->midi_track()->get_capture_start_frame (0));
3685 framepos_t back = max_framepos;
3687 for (MidiBuffer::iterator i = buf->begin(); i != buf->end(); ++i) {
3688 Evoral::MIDIEvent<MidiBuffer::TimeType> const ev (*i, false);
3690 if (ev.is_channel_event()) {
3691 if (get_channel_mode() == FilterChannels) {
3692 if (((uint16_t(1) << ev.channel()) & get_selected_channels()) == 0) {
3698 /* ev.time() is in session frames, so (ev.time() - converter.origin_b()) is
3699 frames from the start of the source, and so time_beats is in terms of the
3703 Evoral::MusicalTime const time_beats = converter.from (ev.time () - converter.origin_b ());
3705 if (ev.type() == MIDI_CMD_NOTE_ON) {
3706 boost::shared_ptr<NoteType> note (
3707 new NoteType (ev.channel(), time_beats, 0, ev.note(), ev.velocity()));
3709 add_note (note, true);
3711 /* fix up our note range */
3712 if (ev.note() < _current_range_min) {
3713 midi_stream_view()->apply_note_range (ev.note(), _current_range_max, true);
3714 } else if (ev.note() > _current_range_max) {
3715 midi_stream_view()->apply_note_range (_current_range_min, ev.note(), true);
3718 } else if (ev.type() == MIDI_CMD_NOTE_OFF) {
3719 resolve_note (ev.note (), time_beats);
3725 midi_stream_view()->check_record_layers (region(), back);
3729 MidiRegionView::trim_front_starting ()
3731 /* Reparent the note group to the region view's parent, so that it doesn't change
3732 when the region view is trimmed.
3734 _temporary_note_group = new ArdourCanvas::Group (group->parent ());
3735 _temporary_note_group->move (group->position ());
3736 _note_group->reparent (_temporary_note_group);
3740 MidiRegionView::trim_front_ending ()
3742 _note_group->reparent (group);
3743 delete _temporary_note_group;
3744 _temporary_note_group = 0;
3746 if (_region->start() < 0) {
3747 /* Trim drag made start time -ve; fix this */
3748 midi_region()->fix_negative_start ();
3753 MidiRegionView::edit_patch_change (PatchChange* pc)
3755 PatchChangeDialog d (&_source_relative_time_converter, trackview.session(), *pc->patch (), instrument_info(), Gtk::Stock::APPLY, true);
3757 d.set_position (Gtk::WIN_POS_MOUSE);
3759 int response = d.run();
3762 case Gtk::RESPONSE_ACCEPT:
3764 case Gtk::RESPONSE_REJECT:
3765 delete_patch_change (pc);
3771 change_patch_change (pc->patch(), d.patch ());
3775 MidiRegionView::delete_sysex (SysEx* /*sysex*/)
3778 // sysyex object doesn't have a pointer to a sysex event
3779 // MidiModel::SysExDiffCommand* c = _model->new_sysex_diff_command (_("delete sysex"));
3780 // c->remove (sysex->sysex());
3781 // _model->apply_command (*trackview.session(), c);
3783 //_sys_exes.clear ();
3784 // display_sysexes();
3788 MidiRegionView::show_verbose_cursor (boost::shared_ptr<NoteType> n) const
3790 using namespace MIDI::Name;
3794 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3796 boost::shared_ptr<MasterDeviceNames> device_names(mtv->get_device_names());
3798 MIDI::Name::PatchPrimaryKey patch_key;
3799 get_patch_key_at(n->time(), n->channel(), patch_key);
3800 name = device_names->note_name(mtv->gui_property(X_("midnam-custom-device-mode")),
3802 patch_key.bank_number,
3803 patch_key.program_number,
3809 snprintf (buf, sizeof (buf), "%d %s\nCh %d Vel %d",
3811 name.empty() ? Evoral::midi_note_name (n->note()).c_str() : name.c_str(),
3812 (int) n->channel() + 1,
3813 (int) n->velocity());
3815 show_verbose_cursor(buf, 10, 20);
3819 MidiRegionView::show_verbose_cursor (string const & text, double xoffset, double yoffset) const
3823 trackview.editor().verbose_cursor()->set_text (text);
3824 trackview.editor().get_pointer_position (wx, wy);
3829 /* Flip the cursor above the mouse pointer if it would overlap the bottom of the canvas */
3831 boost::optional<ArdourCanvas::Rect> bbo = trackview.editor().verbose_cursor()->item().bounding_box();
3835 ArdourCanvas::Rect bb = bbo.get();
3837 if ((wy + bb.y1 - bb.y0) > trackview.editor().visible_canvas_height()) {
3838 wy -= (bb.y1 - bb.y0) + 2 * yoffset;
3841 trackview.editor().verbose_cursor()->set_position (wx, wy);
3842 trackview.editor().verbose_cursor()->show ();
3845 /** @param p A session framepos.
3846 * @param grid_frames Filled in with the number of frames that a grid interval is at p.
3847 * @return p snapped to the grid subdivision underneath it.
3850 MidiRegionView::snap_frame_to_grid_underneath (framepos_t p, framecnt_t& grid_frames) const
3852 PublicEditor& editor = trackview.editor ();
3855 Evoral::MusicalTime grid_beats = editor.get_grid_type_as_beats (success, p);
3861 grid_frames = region_beats_to_region_frames (grid_beats);
3863 /* Hack so that we always snap to the note that we are over, instead of snapping
3864 to the next one if we're more than halfway through the one we're over.
3866 if (editor.snap_mode() == SnapNormal && p >= grid_frames / 2) {
3867 p -= grid_frames / 2;
3870 return snap_frame_to_frame (p);
3873 /** Called when the selection has been cleared in any MidiRegionView.
3874 * @param rv MidiRegionView that the selection was cleared in.
3877 MidiRegionView::selection_cleared (MidiRegionView* rv)
3883 /* Clear our selection in sympathy; but don't signal the fact */
3884 clear_selection (false);
3888 MidiRegionView::note_button_release ()
3890 delete _note_player;
3895 MidiRegionView::get_channel_mode () const
3897 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (&trackview);
3898 return rtav->midi_track()->get_playback_channel_mode();
3902 MidiRegionView::get_selected_channels () const
3904 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (&trackview);
3905 return rtav->midi_track()->get_playback_channel_mask();