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.
27 #include "gtkmm2ext/gtk_ui.h"
29 #include <sigc++/signal.h>
31 #include "pbd/memento_command.h"
32 #include "pbd/stateful_diff_command.h"
34 #include "ardour/midi_region.h"
35 #include "ardour/midi_source.h"
36 #include "ardour/midi_model.h"
37 #include "ardour/midi_patch_manager.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 "automation_region_view.h"
47 #include "automation_time_axis.h"
48 #include "canvas-hit.h"
49 #include "canvas-note.h"
50 #include "canvas_patch_change.h"
51 #include "canvas-sysex.h"
54 #include "editor_drag.h"
55 #include "ghostregion.h"
56 #include "gui_thread.h"
58 #include "midi_channel_dialog.h"
59 #include "midi_cut_buffer.h"
60 #include "midi_list_editor.h"
61 #include "midi_region_view.h"
62 #include "midi_streamview.h"
63 #include "midi_time_axis.h"
64 #include "midi_util.h"
65 #include "midi_velocity_dialog.h"
66 #include "mouse_cursors.h"
67 #include "note_player.h"
68 #include "public_editor.h"
69 #include "rgb_macros.h"
70 #include "selection.h"
71 #include "simpleline.h"
72 #include "streamview.h"
74 #include "patch_change_dialog.h"
75 #include "verbose_cursor.h"
79 using namespace ARDOUR;
81 using namespace Editing;
82 using namespace ArdourCanvas;
83 using Gtkmm2ext::Keyboard;
85 PBD::Signal1<void, MidiRegionView *> MidiRegionView::SelectionCleared;
87 #define MIDI_BP_ZERO ((Config->get_first_midi_bank_is_zero())?0:1)
89 MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &tv,
90 boost::shared_ptr<MidiRegion> r, double spu, Gdk::Color const & basic_color)
91 : RegionView (parent, tv, r, spu, basic_color)
92 , _last_channel_selection(0xFFFF)
93 , _current_range_min(0)
94 , _current_range_max(0)
96 , _note_group(new ArdourCanvas::Group(*group))
97 , _note_diff_command (0)
99 , _step_edit_cursor (0)
100 , _step_edit_cursor_width (1.0)
101 , _step_edit_cursor_position (0.0)
102 , _channel_selection_scoped_note (0)
103 , _temporary_note_group (0)
106 , _sort_needed (true)
107 , _optimization_iterator (_events.end())
109 , _no_sound_notes (false)
112 , pre_enter_cursor (0)
113 , pre_press_cursor (0)
116 _note_group->raise_to_top();
117 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
120 MidiTimeAxisView *time_axis = dynamic_cast<MidiTimeAxisView *>(&tv);
122 _last_channel_mode = time_axis->channel_selector().get_channel_mode();
123 _last_channel_selection = time_axis->channel_selector().get_selected_channels();
126 Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&MidiRegionView::parameter_changed, this, _1), gui_context());
127 connect_to_diskstream ();
129 SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), boost::bind (&MidiRegionView::selection_cleared, this, _1), gui_context ());
132 MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &tv,
133 boost::shared_ptr<MidiRegion> r, double spu, Gdk::Color& basic_color,
134 TimeAxisViewItem::Visibility visibility)
135 : RegionView (parent, tv, r, spu, basic_color, false, visibility)
136 , _last_channel_selection(0xFFFF)
137 , _current_range_min(0)
138 , _current_range_max(0)
140 , _note_group(new ArdourCanvas::Group(*parent))
141 , _note_diff_command (0)
143 , _step_edit_cursor (0)
144 , _step_edit_cursor_width (1.0)
145 , _step_edit_cursor_position (0.0)
146 , _channel_selection_scoped_note (0)
147 , _temporary_note_group (0)
150 , _sort_needed (true)
151 , _optimization_iterator (_events.end())
153 , _no_sound_notes (false)
156 , pre_enter_cursor (0)
157 , pre_press_cursor (0)
160 _note_group->raise_to_top();
161 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
163 MidiTimeAxisView *time_axis = dynamic_cast<MidiTimeAxisView *>(&tv);
165 _last_channel_mode = time_axis->channel_selector().get_channel_mode();
166 _last_channel_selection = time_axis->channel_selector().get_selected_channels();
169 connect_to_diskstream ();
171 SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), boost::bind (&MidiRegionView::selection_cleared, this, _1), gui_context ());
175 MidiRegionView::parameter_changed (std::string const & p)
177 if (p == "diplay-first-midi-bank-as-zero") {
178 if (_enable_display) {
184 MidiRegionView::MidiRegionView (const MidiRegionView& other)
185 : sigc::trackable(other)
187 , _last_channel_selection(0xFFFF)
188 , _current_range_min(0)
189 , _current_range_max(0)
191 , _note_group(new ArdourCanvas::Group(*get_canvas_group()))
192 , _note_diff_command (0)
194 , _step_edit_cursor (0)
195 , _step_edit_cursor_width (1.0)
196 , _step_edit_cursor_position (0.0)
197 , _channel_selection_scoped_note (0)
198 , _temporary_note_group (0)
201 , _sort_needed (true)
202 , _optimization_iterator (_events.end())
204 , _no_sound_notes (false)
207 , pre_enter_cursor (0)
208 , pre_press_cursor (0)
214 UINT_TO_RGBA (other.fill_color, &r, &g, &b, &a);
215 c.set_rgb_p (r/255.0, g/255.0, b/255.0);
220 MidiRegionView::MidiRegionView (const MidiRegionView& other, boost::shared_ptr<MidiRegion> region)
221 : RegionView (other, boost::shared_ptr<Region> (region))
222 , _last_channel_selection(0xFFFF)
223 , _current_range_min(0)
224 , _current_range_max(0)
226 , _note_group(new ArdourCanvas::Group(*get_canvas_group()))
227 , _note_diff_command (0)
229 , _step_edit_cursor (0)
230 , _step_edit_cursor_width (1.0)
231 , _step_edit_cursor_position (0.0)
232 , _channel_selection_scoped_note (0)
233 , _temporary_note_group (0)
236 , _sort_needed (true)
237 , _optimization_iterator (_events.end())
239 , _no_sound_notes (false)
242 , pre_enter_cursor (0)
243 , pre_press_cursor (0)
249 UINT_TO_RGBA (other.fill_color, &r, &g, &b, &a);
250 c.set_rgb_p (r/255.0, g/255.0, b/255.0);
256 MidiRegionView::init (Gdk::Color const & basic_color, bool wfd)
258 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
260 CanvasNoteEvent::CanvasNoteEventDeleted.connect (note_delete_connection, MISSING_INVALIDATOR,
261 boost::bind (&MidiRegionView::maybe_remove_deleted_note_from_selection, this, _1),
265 midi_region()->midi_source(0)->load_model();
268 _model = midi_region()->midi_source(0)->model();
269 _enable_display = false;
271 RegionView::init (basic_color, false);
273 compute_colors (basic_color);
275 set_height (trackview.current_height());
278 region_sync_changed ();
279 region_resized (ARDOUR::bounds_change);
284 _enable_display = true;
287 display_model (_model);
291 reset_width_dependent_items (_pixel_width);
293 group->raise_to_top();
294 group->signal_event().connect (sigc::mem_fun (this, &MidiRegionView::canvas_event), false);
296 midi_view()->signal_channel_mode_changed().connect(
297 sigc::mem_fun(this, &MidiRegionView::midi_channel_mode_changed));
299 instrument_info().Changed.connect (_instrument_changed_connection, invalidator (*this),
300 boost::bind (&MidiRegionView::instrument_settings_changed, this), gui_context());
302 trackview.editor().SnapChanged.connect(snap_changed_connection, invalidator(*this),
303 boost::bind (&MidiRegionView::snap_changed, this),
306 Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&MidiRegionView::parameter_changed, this, _1), gui_context());
307 connect_to_diskstream ();
309 SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), boost::bind (&MidiRegionView::selection_cleared, this, _1), gui_context ());
313 MidiRegionView::instrument_info () const
315 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
316 return route_ui->route()->instrument_info();
319 const boost::shared_ptr<ARDOUR::MidiRegion>
320 MidiRegionView::midi_region() const
322 return boost::dynamic_pointer_cast<ARDOUR::MidiRegion>(_region);
326 MidiRegionView::connect_to_diskstream ()
328 midi_view()->midi_track()->DataRecorded.connect(
329 *this, invalidator(*this),
330 boost::bind (&MidiRegionView::data_recorded, this, _1),
335 MidiRegionView::canvas_event(GdkEvent* ev)
340 case GDK_ENTER_NOTIFY:
341 case GDK_LEAVE_NOTIFY:
342 _last_event_x = ev->crossing.x;
343 _last_event_y = ev->crossing.y;
345 case GDK_MOTION_NOTIFY:
346 _last_event_x = ev->motion.x;
347 _last_event_y = ev->motion.y;
353 if (ev->type == GDK_2BUTTON_PRESS) {
354 // cannot use double-click to exit internal mode if single-click is being used
355 MouseMode m = trackview.editor().current_mouse_mode();
357 if ((m != MouseObject || !Keyboard::modifier_state_contains (ev->button.state, Keyboard::insert_note_modifier())) && (m != MouseDraw)) {
358 return trackview.editor().toggle_internal_editing_from_double_click (ev);
362 if ((!trackview.editor().internal_editing() && trackview.editor().current_mouse_mode() != MouseGain) ||
363 (trackview.editor().current_mouse_mode() == MouseTimeFX) ||
364 (trackview.editor().current_mouse_mode() == MouseZoom)) {
365 // handle non-draw modes elsewhere
371 return scroll (&ev->scroll);
374 return key_press (&ev->key);
376 case GDK_KEY_RELEASE:
377 return key_release (&ev->key);
379 case GDK_BUTTON_PRESS:
380 return button_press (&ev->button);
382 case GDK_BUTTON_RELEASE:
383 r = button_release (&ev->button);
388 case GDK_ENTER_NOTIFY:
389 return enter_notify (&ev->crossing);
391 case GDK_LEAVE_NOTIFY:
392 return leave_notify (&ev->crossing);
394 case GDK_MOTION_NOTIFY:
395 return motion (&ev->motion);
405 MidiRegionView::remove_ghost_note ()
412 MidiRegionView::enter_notify (GdkEventCrossing* ev)
414 trackview.editor().MouseModeChanged.connect (
415 _mouse_mode_connection, invalidator (*this), boost::bind (&MidiRegionView::mouse_mode_changed, this), gui_context ()
418 if (trackview.editor().current_mouse_mode() == MouseDraw && _mouse_state != AddDragging) {
419 create_ghost_note (ev->x, ev->y);
422 if (!trackview.editor().internal_editing()) {
423 Keyboard::magic_widget_drop_focus();
425 Keyboard::magic_widget_grab_focus();
429 // if current operation is non-operational in a midi region, change the cursor to so indicate
430 if (trackview.editor().current_mouse_mode() == MouseGain) {
431 Editor* editor = dynamic_cast<Editor *> (&trackview.editor());
432 pre_enter_cursor = editor->get_canvas_cursor();
433 editor->set_canvas_cursor(editor->cursors()->timebar);
440 MidiRegionView::leave_notify (GdkEventCrossing*)
442 _mouse_mode_connection.disconnect ();
444 trackview.editor().verbose_cursor()->hide ();
445 remove_ghost_note ();
447 if (pre_enter_cursor) {
448 Editor* editor = dynamic_cast<Editor *> (&trackview.editor());
449 editor->set_canvas_cursor(pre_enter_cursor);
456 MidiRegionView::mouse_mode_changed ()
458 if (trackview.editor().current_mouse_mode() == MouseDraw && trackview.editor().internal_editing()) {
459 create_ghost_note (_last_event_x, _last_event_y);
461 remove_ghost_note ();
462 trackview.editor().verbose_cursor()->hide ();
465 if (!trackview.editor().internal_editing()) {
466 Keyboard::magic_widget_drop_focus();
468 Keyboard::magic_widget_grab_focus();
474 MidiRegionView::button_press (GdkEventButton* ev)
476 if (ev->button != 1) {
480 Editor* editor = dynamic_cast<Editor *> (&trackview.editor());
481 MouseMode m = editor->current_mouse_mode();
483 if (m == MouseObject && Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())) {
484 pre_press_cursor = editor->get_canvas_cursor ();
485 editor->set_canvas_cursor (editor->cursors()->midi_pencil);
488 if (_mouse_state != SelectTouchDragging) {
490 _pressed_button = ev->button;
491 _mouse_state = Pressed;
496 _pressed_button = ev->button;
502 MidiRegionView::button_release (GdkEventButton* ev)
504 double event_x, event_y;
506 if (ev->button != 1) {
513 group->w2i(event_x, event_y);
514 group->ungrab(ev->time);
516 PublicEditor& editor = trackview.editor ();
518 if (pre_press_cursor) {
519 dynamic_cast<Editor*>(&editor)->set_canvas_cursor (pre_press_cursor, false);
520 pre_press_cursor = 0;
523 switch (_mouse_state) {
524 case Pressed: // Clicked
526 switch (editor.current_mouse_mode()) {
528 /* no motion occured - simple click */
537 if (Keyboard::is_insert_note_event(ev)) {
539 double event_x, event_y;
543 group->w2i(event_x, event_y);
546 Evoral::MusicalTime beats = editor.get_grid_type_as_beats (success, editor.pixel_to_frame (event_x));
552 /* Shorten the length by 1 tick so that we can add a new note at the next
553 grid snap without it overlapping this one.
555 beats -= 1.0 / Timecode::BBT_Time::ticks_per_beat;
557 create_note_at (editor.pixel_to_frame (event_x), event_y, beats, true);
565 Evoral::MusicalTime beats = editor.get_grid_type_as_beats (success, editor.pixel_to_frame (event_x));
571 /* Shorten the length by 1 tick so that we can add a new note at the next
572 grid snap without it overlapping this one.
574 beats -= 1.0 / Timecode::BBT_Time::ticks_per_beat;
576 create_note_at (editor.pixel_to_frame (event_x), event_y, beats, true);
587 case SelectRectDragging:
589 editor.drags()->end_grab ((GdkEvent *) ev);
591 create_ghost_note (ev->x, ev->y);
603 MidiRegionView::motion (GdkEventMotion* ev)
605 PublicEditor& editor = trackview.editor ();
607 if (!_ghost_note && editor.current_mouse_mode() == MouseObject &&
608 Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier()) &&
609 _mouse_state != AddDragging) {
611 create_ghost_note (ev->x, ev->y);
613 } else if (_ghost_note && editor.current_mouse_mode() == MouseObject &&
614 Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())) {
616 update_ghost_note (ev->x, ev->y);
618 } else if (_ghost_note && editor.current_mouse_mode() == MouseObject) {
620 remove_ghost_note ();
621 editor.verbose_cursor()->hide ();
623 } else if (_ghost_note && editor.current_mouse_mode() == MouseDraw) {
625 update_ghost_note (ev->x, ev->y);
628 /* any motion immediately hides velocity text that may have been visible */
630 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
631 (*i)->hide_velocity ();
634 switch (_mouse_state) {
637 if (_pressed_button == 1) {
639 MouseMode m = editor.current_mouse_mode();
641 if (m == MouseDraw || (m == MouseObject && Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier()))) {
643 editor.drags()->set (new NoteCreateDrag (dynamic_cast<Editor *> (&editor), group, this), (GdkEvent *) ev);
644 _mouse_state = AddDragging;
645 remove_ghost_note ();
646 editor.verbose_cursor()->hide ();
648 } else if (m == MouseObject) {
649 editor.drags()->set (new MidiRubberbandSelectDrag (dynamic_cast<Editor *> (&editor), this), (GdkEvent *) ev);
651 _mouse_state = SelectRectDragging;
653 } else if (m == MouseRange) {
654 editor.drags()->set (new MidiVerticalSelectDrag (dynamic_cast<Editor *> (&editor), this), (GdkEvent *) ev);
655 _mouse_state = SelectVerticalDragging;
662 case SelectRectDragging:
663 case SelectVerticalDragging:
665 editor.drags()->motion_handler ((GdkEvent *) ev, false);
668 case SelectTouchDragging:
680 MidiRegionView::scroll (GdkEventScroll* ev)
682 if (_selection.empty()) {
686 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
687 /* XXX: bit of a hack; allow PrimaryModifier scroll through so that
688 it still works for zoom.
693 trackview.editor().verbose_cursor()->hide ();
695 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
696 bool together = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
698 if (ev->direction == GDK_SCROLL_UP) {
699 change_velocities (true, fine, false, together);
700 } else if (ev->direction == GDK_SCROLL_DOWN) {
701 change_velocities (false, fine, false, together);
703 /* left, right: we don't use them */
711 MidiRegionView::key_press (GdkEventKey* ev)
713 /* since GTK bindings are generally activated on press, and since
714 detectable auto-repeat is the name of the game and only sends
715 repeated presses, carry out key actions at key press, not release.
718 bool unmodified = Keyboard::no_modifier_keys_pressed (ev);
720 if (unmodified && (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R)) {
721 _mouse_state = SelectTouchDragging;
724 } else if (ev->keyval == GDK_Escape && unmodified) {
728 } else if (unmodified && (ev->keyval == GDK_comma || ev->keyval == GDK_period)) {
730 bool start = (ev->keyval == GDK_comma);
731 bool end = (ev->keyval == GDK_period);
732 bool shorter = Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier);
733 bool fine = Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
735 change_note_lengths (fine, shorter, 0.0, start, end);
739 } else if ((ev->keyval == GDK_BackSpace || ev->keyval == GDK_Delete) && unmodified) {
741 if (_selection.empty()) {
748 } else if (ev->keyval == GDK_Tab) {
750 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
751 goto_previous_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
753 goto_next_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
757 } else if (ev->keyval == GDK_ISO_Left_Tab) {
759 /* Shift-TAB generates ISO Left Tab, for some reason */
761 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
762 goto_previous_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
764 goto_next_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
770 } else if (ev->keyval == GDK_Up) {
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 (true, fine, allow_smush, together);
779 transpose (true, fine, allow_smush);
783 } else if (ev->keyval == GDK_Down) {
785 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
786 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
787 bool together = Keyboard::modifier_state_contains (ev->state, Keyboard::Level4Modifier);
789 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
790 change_velocities (false, fine, allow_smush, together);
792 transpose (false, fine, allow_smush);
796 } else if (ev->keyval == GDK_Left && unmodified) {
801 } else if (ev->keyval == GDK_Right && unmodified) {
806 } else if (ev->keyval == GDK_c && unmodified) {
810 } else if (ev->keyval == GDK_v && unmodified) {
819 MidiRegionView::key_release (GdkEventKey* ev)
821 if ((_mouse_state == SelectTouchDragging) && (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R)) {
829 MidiRegionView::channel_edit ()
831 if (_selection.empty()) {
835 /* pick a note somewhat at random (since Selection is a set<>) to
836 * provide the "current" channel for the dialog.
839 uint8_t current_channel = (*_selection.begin())->note()->channel ();
840 MidiChannelDialog channel_dialog (current_channel);
841 int ret = channel_dialog.run ();
844 case Gtk::RESPONSE_OK:
850 uint8_t new_channel = channel_dialog.active_channel ();
852 start_note_diff_command (_("channel edit"));
854 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
855 Selection::iterator next = i;
857 change_note_channel (*i, new_channel);
865 MidiRegionView::velocity_edit ()
867 if (_selection.empty()) {
871 /* pick a note somewhat at random (since Selection is a set<>) to
872 * provide the "current" velocity for the dialog.
875 uint8_t current_velocity = (*_selection.begin())->note()->velocity ();
876 MidiVelocityDialog velocity_dialog (current_velocity);
877 int ret = velocity_dialog.run ();
880 case Gtk::RESPONSE_OK:
886 uint8_t new_velocity = velocity_dialog.velocity ();
888 start_note_diff_command (_("velocity edit"));
890 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
891 Selection::iterator next = i;
893 change_note_velocity (*i, new_velocity, false);
901 MidiRegionView::show_list_editor ()
904 _list_editor = new MidiListEditor (trackview.session(), midi_region(), midi_view()->midi_track());
906 _list_editor->present ();
909 /** Add a note to the model, and the view, at a canvas (click) coordinate.
910 * \param t time in frames relative to the position of the region
911 * \param y vertical position in pixels
912 * \param length duration of the note in beats
913 * \param snap_t true to snap t to the grid, otherwise false.
916 MidiRegionView::create_note_at (framepos_t t, double y, double length, bool snap_t)
918 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
919 MidiStreamView* const view = mtv->midi_view();
921 double note = view->y_to_note(y);
924 assert(note <= 127.0);
926 // Start of note in frames relative to region start
928 framecnt_t grid_frames;
929 t = snap_frame_to_grid_underneath (t, grid_frames);
933 assert (length != 0);
935 const boost::shared_ptr<NoteType> new_note (new NoteType (mtv->get_channel_for_add (),
936 region_frames_to_region_beats(t + _region->start()),
938 (uint8_t)note, 0x40));
940 if (_model->contains (new_note)) {
944 view->update_note_range(new_note->note());
946 MidiModel::NoteDiffCommand* cmd = _model->new_note_diff_command(_("add note"));
948 _model->apply_command(*trackview.session(), cmd);
950 play_midi_note (new_note);
954 MidiRegionView::clear_events (bool with_selection_signal)
956 clear_selection (with_selection_signal);
959 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
960 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
965 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
970 _patch_changes.clear();
972 _optimization_iterator = _events.end();
976 MidiRegionView::display_model(boost::shared_ptr<MidiModel> model)
980 content_connection.disconnect ();
981 _model->ContentsChanged.connect (content_connection, invalidator (*this), boost::bind (&MidiRegionView::redisplay_model, this), gui_context());
985 if (_enable_display) {
991 MidiRegionView::start_note_diff_command (string name)
993 if (!_note_diff_command) {
994 _note_diff_command = _model->new_note_diff_command (name);
999 MidiRegionView::note_diff_add_note (const boost::shared_ptr<NoteType> note, bool selected, bool show_velocity)
1001 if (_note_diff_command) {
1002 _note_diff_command->add (note);
1005 _marked_for_selection.insert(note);
1007 if (show_velocity) {
1008 _marked_for_velocity.insert(note);
1013 MidiRegionView::note_diff_remove_note (ArdourCanvas::CanvasNoteEvent* ev)
1015 if (_note_diff_command && ev->note()) {
1016 _note_diff_command->remove(ev->note());
1021 MidiRegionView::note_diff_add_change (ArdourCanvas::CanvasNoteEvent* ev,
1022 MidiModel::NoteDiffCommand::Property property,
1025 if (_note_diff_command) {
1026 _note_diff_command->change (ev->note(), property, val);
1031 MidiRegionView::note_diff_add_change (ArdourCanvas::CanvasNoteEvent* ev,
1032 MidiModel::NoteDiffCommand::Property property,
1033 Evoral::MusicalTime val)
1035 if (_note_diff_command) {
1036 _note_diff_command->change (ev->note(), property, val);
1041 MidiRegionView::apply_diff (bool as_subcommand)
1045 if (!_note_diff_command) {
1049 if ((add_or_remove = _note_diff_command->adds_or_removes())) {
1050 // Mark all selected notes for selection when model reloads
1051 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1052 _marked_for_selection.insert((*i)->note());
1056 if (as_subcommand) {
1057 _model->apply_command_as_subcommand (*trackview.session(), _note_diff_command);
1059 _model->apply_command (*trackview.session(), _note_diff_command);
1062 _note_diff_command = 0;
1063 midi_view()->midi_track()->playlist_modified();
1065 if (add_or_remove) {
1066 _marked_for_selection.clear();
1069 _marked_for_velocity.clear();
1073 MidiRegionView::abort_command()
1075 delete _note_diff_command;
1076 _note_diff_command = 0;
1081 MidiRegionView::find_canvas_note (boost::shared_ptr<NoteType> note)
1083 if (_optimization_iterator != _events.end()) {
1084 ++_optimization_iterator;
1087 if (_optimization_iterator != _events.end() && (*_optimization_iterator)->note() == note) {
1088 return *_optimization_iterator;
1091 for (_optimization_iterator = _events.begin(); _optimization_iterator != _events.end(); ++_optimization_iterator) {
1092 if ((*_optimization_iterator)->note() == note) {
1093 return *_optimization_iterator;
1101 MidiRegionView::get_events (Events& e, Evoral::Sequence<Evoral::MusicalTime>::NoteOperator op, uint8_t val, int chan_mask)
1103 MidiModel::Notes notes;
1104 _model->get_notes (notes, op, val, chan_mask);
1106 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1107 CanvasNoteEvent* cne = find_canvas_note (*n);
1115 MidiRegionView::redisplay_model()
1117 // Don't redisplay the model if we're currently recording and displaying that
1118 if (_active_notes) {
1126 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1127 (*i)->invalidate ();
1130 MidiModel::ReadLock lock(_model->read_lock());
1132 MidiModel::Notes& notes (_model->notes());
1133 _optimization_iterator = _events.begin();
1135 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1137 boost::shared_ptr<NoteType> note (*n);
1138 CanvasNoteEvent* cne;
1141 if (note_in_region_range (note, visible)) {
1143 if ((cne = find_canvas_note (note)) != 0) {
1150 if ((cn = dynamic_cast<CanvasNote*>(cne)) != 0) {
1152 } else if ((ch = dynamic_cast<CanvasHit*>(cne)) != 0) {
1164 add_note (note, visible);
1169 if ((cne = find_canvas_note (note)) != 0) {
1177 /* remove note items that are no longer valid */
1179 for (Events::iterator i = _events.begin(); i != _events.end(); ) {
1180 if (!(*i)->valid ()) {
1182 for (vector<GhostRegion*>::iterator j = ghosts.begin(); j != ghosts.end(); ++j) {
1183 MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*j);
1185 gr->remove_note (*i);
1190 i = _events.erase (i);
1197 _patch_changes.clear();
1201 display_patch_changes ();
1203 _marked_for_selection.clear ();
1204 _marked_for_velocity.clear ();
1206 /* we may have caused _events to contain things out of order (e.g. if a note
1207 moved earlier or later). we don't generally need them in time order, but
1208 make a note that a sort is required for those cases that require it.
1211 _sort_needed = true;
1215 MidiRegionView::display_patch_changes ()
1217 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1218 uint16_t chn_mask = mtv->channel_selector().get_selected_channels();
1220 for (uint8_t i = 0; i < 16; ++i) {
1221 display_patch_changes_on_channel (i, chn_mask & (1 << i));
1225 /** @param active_channel true to display patch changes fully, false to display
1226 * them `greyed-out' (as on an inactive channel)
1229 MidiRegionView::display_patch_changes_on_channel (uint8_t channel, bool active_channel)
1231 for (MidiModel::PatchChanges::const_iterator i = _model->patch_changes().begin(); i != _model->patch_changes().end(); ++i) {
1233 if ((*i)->channel() != channel) {
1237 string patch_name = instrument_info().get_patch_name ((*i)->bank(), (*i)->program(), channel);
1238 add_canvas_patch_change (*i, patch_name, active_channel);
1243 MidiRegionView::display_sysexes()
1245 bool have_periodic_system_messages = false;
1246 bool display_periodic_messages = true;
1248 if (!Config->get_never_display_periodic_midi()) {
1250 for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
1251 const boost::shared_ptr<const Evoral::MIDIEvent<Evoral::MusicalTime> > mev =
1252 boost::static_pointer_cast<const Evoral::MIDIEvent<Evoral::MusicalTime> > (*i);
1255 if (mev->is_spp() || mev->is_mtc_quarter() || mev->is_mtc_full()) {
1256 have_periodic_system_messages = true;
1262 if (have_periodic_system_messages) {
1263 double zoom = trackview.editor().get_current_zoom (); // frames per pixel
1265 /* get an approximate value for the number of samples per video frame */
1267 double video_frame = trackview.session()->frame_rate() * (1.0/30);
1269 /* if we are zoomed out beyond than the cutoff (i.e. more
1270 * frames per pixel than frames per 4 video frames), don't
1271 * show periodic sysex messages.
1274 if (zoom > (video_frame*4)) {
1275 display_periodic_messages = false;
1279 display_periodic_messages = false;
1282 for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
1284 const boost::shared_ptr<const Evoral::MIDIEvent<Evoral::MusicalTime> > mev =
1285 boost::static_pointer_cast<const Evoral::MIDIEvent<Evoral::MusicalTime> > (*i);
1287 Evoral::MusicalTime time = (*i)->time();
1291 if (mev->is_spp() || mev->is_mtc_quarter() || mev->is_mtc_full()) {
1292 if (!display_periodic_messages) {
1300 for (uint32_t b = 0; b < (*i)->size(); ++b) {
1301 str << int((*i)->buffer()[b]);
1302 if (b != (*i)->size() -1) {
1306 string text = str.str();
1308 const double x = trackview.editor().frame_to_pixel(source_beats_to_region_frames(time));
1310 double height = midi_stream_view()->contents_height();
1312 boost::shared_ptr<CanvasSysEx> sysex = boost::shared_ptr<CanvasSysEx>(
1313 new CanvasSysEx(*this, *_note_group, text, height, x, 1.0, (*i)));
1315 // Show unless message is beyond the region bounds
1316 if (time - _region->start() >= _region->length() || time < _region->start()) {
1322 _sys_exes.push_back(sysex);
1326 MidiRegionView::~MidiRegionView ()
1328 in_destructor = true;
1330 trackview.editor().verbose_cursor()->hide ();
1332 note_delete_connection.disconnect ();
1334 delete _list_editor;
1336 RegionViewGoingAway (this); /* EMIT_SIGNAL */
1338 if (_active_notes) {
1342 _selection_cleared_connection.disconnect ();
1345 clear_events (false);
1348 delete _note_diff_command;
1349 delete _step_edit_cursor;
1350 delete _temporary_note_group;
1354 MidiRegionView::region_resized (const PropertyChange& what_changed)
1356 RegionView::region_resized(what_changed);
1358 if (what_changed.contains (ARDOUR::Properties::position)) {
1359 set_duration(_region->length(), 0);
1360 if (_enable_display) {
1367 MidiRegionView::reset_width_dependent_items (double pixel_width)
1369 RegionView::reset_width_dependent_items(pixel_width);
1370 assert(_pixel_width == pixel_width);
1372 if (_enable_display) {
1376 for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
1377 if ((*x)->width() >= _pixel_width) {
1384 move_step_edit_cursor (_step_edit_cursor_position);
1385 set_step_edit_cursor_width (_step_edit_cursor_width);
1389 MidiRegionView::set_height (double height)
1391 static const double FUDGE = 2.0;
1392 const double old_height = _height;
1393 RegionView::set_height(height);
1394 _height = height - FUDGE;
1396 apply_note_range(midi_stream_view()->lowest_note(),
1397 midi_stream_view()->highest_note(),
1398 height != old_height + FUDGE);
1401 name_pixbuf->raise_to_top();
1404 for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
1405 (*x)->set_height (midi_stream_view()->contents_height());
1408 if (_step_edit_cursor) {
1409 _step_edit_cursor->property_y2() = midi_stream_view()->contents_height();
1414 /** Apply the current note range from the stream view
1415 * by repositioning/hiding notes as necessary
1418 MidiRegionView::apply_note_range (uint8_t min, uint8_t max, bool force)
1420 if (!_enable_display) {
1424 if (!force && _current_range_min == min && _current_range_max == max) {
1428 _current_range_min = min;
1429 _current_range_max = max;
1431 for (Events::const_iterator i = _events.begin(); i != _events.end(); ++i) {
1432 CanvasNoteEvent* event = *i;
1433 boost::shared_ptr<NoteType> note (event->note());
1435 if (note->note() < _current_range_min ||
1436 note->note() > _current_range_max) {
1442 if (CanvasNote* cnote = dynamic_cast<CanvasNote*>(event)) {
1444 const double y1 = midi_stream_view()->note_to_y(note->note());
1445 const double y2 = y1 + floor(midi_stream_view()->note_height());
1447 cnote->property_y1() = y1;
1448 cnote->property_y2() = y2;
1450 } else if (CanvasHit* chit = dynamic_cast<CanvasHit*>(event)) {
1452 const double diamond_size = update_hit (chit);
1454 chit->set_height (diamond_size);
1460 MidiRegionView::add_ghost (TimeAxisView& tv)
1464 double unit_position = _region->position () / samples_per_unit;
1465 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(&tv);
1466 MidiGhostRegion* ghost;
1468 if (mtv && mtv->midi_view()) {
1469 /* if ghost is inserted into midi track, use a dedicated midi ghost canvas group
1470 to allow having midi notes on top of note lines and waveforms.
1472 ghost = new MidiGhostRegion (*mtv->midi_view(), trackview, unit_position);
1474 ghost = new MidiGhostRegion (tv, trackview, unit_position);
1477 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1478 if ((note = dynamic_cast<CanvasNote*>(*i)) != 0) {
1479 ghost->add_note(note);
1483 ghost->set_height ();
1484 ghost->set_duration (_region->length() / samples_per_unit);
1485 ghosts.push_back (ghost);
1487 GhostRegion::CatchDeletion.connect (*this, invalidator (*this), boost::bind (&RegionView::remove_ghost, this, _1), gui_context());
1493 /** Begin tracking note state for successive calls to add_event
1496 MidiRegionView::begin_write()
1498 assert(!_active_notes);
1499 _active_notes = new CanvasNote*[128];
1500 for (unsigned i=0; i < 128; ++i) {
1501 _active_notes[i] = 0;
1506 /** Destroy note state for add_event
1509 MidiRegionView::end_write()
1511 delete[] _active_notes;
1513 _marked_for_selection.clear();
1514 _marked_for_velocity.clear();
1518 /** Resolve an active MIDI note (while recording).
1521 MidiRegionView::resolve_note(uint8_t note, double end_time)
1523 if (midi_view()->note_mode() != Sustained) {
1527 if (_active_notes && _active_notes[note]) {
1529 /* XXX is end_time really region-centric? I think so, because
1530 this is a new region that we're recording, so source zero is
1531 the same as region zero
1533 const framepos_t end_time_frames = region_beats_to_region_frames(end_time);
1535 _active_notes[note]->property_x2() = trackview.editor().frame_to_pixel(end_time_frames);
1536 _active_notes[note]->property_outline_what() = (guint32) 0xF; // all edges
1537 _active_notes[note] = 0;
1542 /** Extend active notes to rightmost edge of region (if length is changed)
1545 MidiRegionView::extend_active_notes()
1547 if (!_active_notes) {
1551 for (unsigned i=0; i < 128; ++i) {
1552 if (_active_notes[i]) {
1553 _active_notes[i]->property_x2() = trackview.editor().frame_to_pixel(_region->length());
1560 MidiRegionView::play_midi_note(boost::shared_ptr<NoteType> note)
1562 if (_no_sound_notes || !Config->get_sound_midi_notes()) {
1566 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1568 if (!route_ui || !route_ui->midi_track()) {
1572 NotePlayer* np = new NotePlayer (route_ui->midi_track ());
1576 /* NotePlayer deletes itself */
1580 MidiRegionView::start_playing_midi_note(boost::shared_ptr<NoteType> note)
1582 if (_no_sound_notes || !Config->get_sound_midi_notes()) {
1586 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1588 if (!route_ui || !route_ui->midi_track()) {
1592 delete _note_player;
1593 _note_player = new NotePlayer (route_ui->midi_track ());
1594 _note_player->add (note);
1595 _note_player->on ();
1599 MidiRegionView::start_playing_midi_chord (vector<boost::shared_ptr<NoteType> > notes)
1601 if (_no_sound_notes || !Config->get_sound_midi_notes()) {
1605 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1607 if (!route_ui || !route_ui->midi_track()) {
1611 delete _note_player;
1612 _note_player = new NotePlayer (route_ui->midi_track());
1614 for (vector<boost::shared_ptr<NoteType> >::iterator n = notes.begin(); n != notes.end(); ++n) {
1615 _note_player->add (*n);
1618 _note_player->on ();
1623 MidiRegionView::note_in_region_range (const boost::shared_ptr<NoteType> note, bool& visible) const
1625 const framepos_t note_start_frames = source_beats_to_region_frames (note->time());
1626 bool outside = (note_start_frames < 0) || (note_start_frames > _region->last_frame());
1628 visible = (note->note() >= midi_stream_view()->lowest_note()) &&
1629 (note->note() <= midi_stream_view()->highest_note());
1634 /** Update a canvas note's size from its model note.
1635 * @param ev Canvas note to update.
1636 * @param update_ghost_regions true to update the note in any ghost regions that we have, otherwise false.
1639 MidiRegionView::update_note (CanvasNote* ev, bool update_ghost_regions)
1641 boost::shared_ptr<NoteType> note = ev->note();
1642 const double x = trackview.editor().frame_to_pixel (source_beats_to_region_frames (note->time()));
1643 const double y1 = midi_stream_view()->note_to_y(note->note());
1645 ev->property_x1() = x;
1646 ev->property_y1() = y1;
1648 /* trim note display to not overlap the end of its region */
1650 if (note->length() > 0) {
1651 const framepos_t note_end_frames = min (source_beats_to_region_frames (note->end_time()), _region->length());
1652 ev->property_x2() = trackview.editor().frame_to_pixel (note_end_frames);
1654 ev->property_x2() = trackview.editor().frame_to_pixel (_region->length());
1657 ev->property_y2() = y1 + floor(midi_stream_view()->note_height());
1659 if (note->length() == 0) {
1660 if (_active_notes) {
1661 assert(note->note() < 128);
1662 // If this note is already active there's a stuck note,
1663 // finish the old note rectangle
1664 if (_active_notes[note->note()]) {
1665 CanvasNote* const old_rect = _active_notes[note->note()];
1666 boost::shared_ptr<NoteType> old_note = old_rect->note();
1667 old_rect->property_x2() = x;
1668 old_rect->property_outline_what() = (guint32) 0xF;
1670 _active_notes[note->note()] = ev;
1672 /* outline all but right edge */
1673 ev->property_outline_what() = (guint32) (0x1 & 0x4 & 0x8);
1675 /* outline all edges */
1676 ev->property_outline_what() = (guint32) 0xF;
1679 if (update_ghost_regions) {
1680 for (std::vector<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
1681 MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*i);
1683 gr->update_note (ev);
1690 MidiRegionView::update_hit (CanvasHit* ev)
1692 boost::shared_ptr<NoteType> note = ev->note();
1694 const framepos_t note_start_frames = source_beats_to_region_frames(note->time());
1695 const double x = trackview.editor().frame_to_pixel(note_start_frames);
1696 const double diamond_size = midi_stream_view()->note_height() / 2.0;
1697 const double y = midi_stream_view()->note_to_y(note->note()) + ((diamond_size-2) / 4.0);
1701 return diamond_size;
1704 /** Add a MIDI note to the view (with length).
1706 * If in sustained mode, notes with length 0 will be considered active
1707 * notes, and resolve_note should be called when the corresponding note off
1708 * event arrives, to properly display the note.
1711 MidiRegionView::add_note(const boost::shared_ptr<NoteType> note, bool visible)
1713 CanvasNoteEvent* event = 0;
1715 assert(note->time() >= 0);
1716 assert(midi_view()->note_mode() == Sustained || midi_view()->note_mode() == Percussive);
1718 //ArdourCanvas::Group* const group = (ArdourCanvas::Group*) get_canvas_group();
1720 if (midi_view()->note_mode() == Sustained) {
1722 CanvasNote* ev_rect = new CanvasNote(*this, *_note_group, note);
1724 update_note (ev_rect);
1728 MidiGhostRegion* gr;
1730 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
1731 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
1732 gr->add_note(ev_rect);
1736 } else if (midi_view()->note_mode() == Percussive) {
1738 const double diamond_size = midi_stream_view()->note_height() / 2.0;
1740 CanvasHit* ev_diamond = new CanvasHit (*this, *_note_group, diamond_size, note);
1742 update_hit (ev_diamond);
1751 if (_marked_for_selection.find(note) != _marked_for_selection.end()) {
1752 note_selected(event, true);
1755 if (_marked_for_velocity.find(note) != _marked_for_velocity.end()) {
1756 event->show_velocity();
1759 event->on_channel_selection_change(_last_channel_selection);
1760 _events.push_back(event);
1769 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1770 MidiStreamView* const view = mtv->midi_view();
1772 view->update_note_range (note->note());
1776 MidiRegionView::step_add_note (uint8_t channel, uint8_t number, uint8_t velocity,
1777 Evoral::MusicalTime pos, Evoral::MusicalTime len)
1779 boost::shared_ptr<NoteType> new_note (new NoteType (channel, pos, len, number, velocity));
1781 /* potentially extend region to hold new note */
1783 framepos_t end_frame = source_beats_to_absolute_frames (new_note->end_time());
1784 framepos_t region_end = _region->last_frame();
1786 if (end_frame > region_end) {
1787 _region->set_length (end_frame - _region->position());
1790 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1791 MidiStreamView* const view = mtv->midi_view();
1793 view->update_note_range(new_note->note());
1795 _marked_for_selection.clear ();
1798 start_note_diff_command (_("step add"));
1799 note_diff_add_note (new_note, true, false);
1802 // last_step_edit_note = new_note;
1806 MidiRegionView::step_sustain (Evoral::MusicalTime beats)
1808 change_note_lengths (false, false, beats, false, true);
1811 /** Add a new patch change flag to the canvas.
1812 * @param patch the patch change to add
1813 * @param the text to display in the flag
1814 * @param active_channel true to display the flag as on an active channel, false to grey it out for an inactive channel.
1817 MidiRegionView::add_canvas_patch_change (MidiModel::PatchChangePtr patch, const string& displaytext, bool active_channel)
1819 assert (patch->time() >= 0);
1821 framecnt_t region_frames = source_beats_to_region_frames (patch->time());
1822 const double x = trackview.editor().frame_to_pixel (region_frames);
1824 double const height = midi_stream_view()->contents_height();
1826 boost::shared_ptr<CanvasPatchChange> patch_change = boost::shared_ptr<CanvasPatchChange>(
1827 new CanvasPatchChange(*this, *group,
1836 if (patch_change->width() < _pixel_width) {
1837 // Show unless patch change is beyond the region bounds
1838 if (region_frames < 0 || region_frames >= _region->length()) {
1839 patch_change->hide();
1841 patch_change->show();
1844 patch_change->hide ();
1847 _patch_changes.push_back (patch_change);
1850 MIDI::Name::PatchPrimaryKey
1851 MidiRegionView::patch_change_to_patch_key (MidiModel::PatchChangePtr p)
1853 return MIDI::Name::PatchPrimaryKey (p->program(), p->bank());
1857 MidiRegionView::get_patch_key_at (double time, uint8_t channel, MIDI::Name::PatchPrimaryKey& key)
1859 MidiModel::PatchChanges::iterator i = _model->patch_change_lower_bound (time);
1860 while (i != _model->patch_changes().end() && (*i)->channel() != channel) {
1864 if (i != _model->patch_changes().end()) {
1865 key.bank_number = (*i)->bank();
1866 key.program_number = (*i)->program ();
1868 key.bank_number = key.program_number = 0;
1871 assert (key.is_sane());
1875 MidiRegionView::change_patch_change (CanvasPatchChange& pc, const MIDI::Name::PatchPrimaryKey& new_patch)
1877 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("alter patch change"));
1879 if (pc.patch()->program() != new_patch.program_number) {
1880 c->change_program (pc.patch (), new_patch.program_number);
1883 int const new_bank = new_patch.bank_number;
1884 if (pc.patch()->bank() != new_bank) {
1885 c->change_bank (pc.patch (), new_bank);
1888 _model->apply_command (*trackview.session(), c);
1890 _patch_changes.clear ();
1891 display_patch_changes ();
1895 MidiRegionView::change_patch_change (MidiModel::PatchChangePtr old_change, const Evoral::PatchChange<Evoral::MusicalTime> & new_change)
1897 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("alter patch change"));
1899 if (old_change->time() != new_change.time()) {
1900 c->change_time (old_change, new_change.time());
1903 if (old_change->channel() != new_change.channel()) {
1904 c->change_channel (old_change, new_change.channel());
1907 if (old_change->program() != new_change.program()) {
1908 c->change_program (old_change, new_change.program());
1911 if (old_change->bank() != new_change.bank()) {
1912 c->change_bank (old_change, new_change.bank());
1915 _model->apply_command (*trackview.session(), c);
1917 _patch_changes.clear ();
1918 display_patch_changes ();
1921 /** Add a patch change to the region.
1922 * @param t Time in frames relative to region position
1923 * @param patch Patch to add; time and channel are ignored (time is converted from t, and channel comes from
1924 * MidiTimeAxisView::get_channel_for_add())
1927 MidiRegionView::add_patch_change (framecnt_t t, Evoral::PatchChange<Evoral::MusicalTime> const & patch)
1929 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1931 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("add patch change"));
1932 c->add (MidiModel::PatchChangePtr (
1933 new Evoral::PatchChange<Evoral::MusicalTime> (
1934 absolute_frames_to_source_beats (_region->position() + t),
1935 mtv->get_channel_for_add(), patch.program(), patch.bank()
1940 _model->apply_command (*trackview.session(), c);
1942 _patch_changes.clear ();
1943 display_patch_changes ();
1947 MidiRegionView::move_patch_change (CanvasPatchChange& pc, Evoral::MusicalTime t)
1949 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("move patch change"));
1950 c->change_time (pc.patch (), t);
1951 _model->apply_command (*trackview.session(), c);
1953 _patch_changes.clear ();
1954 display_patch_changes ();
1958 MidiRegionView::delete_patch_change (CanvasPatchChange* pc)
1960 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("delete patch change"));
1961 c->remove (pc->patch ());
1962 _model->apply_command (*trackview.session(), c);
1964 _patch_changes.clear ();
1965 display_patch_changes ();
1969 MidiRegionView::previous_patch (CanvasPatchChange& patch)
1971 if (patch.patch()->program() < 127) {
1972 MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch());
1973 key.program_number++;
1974 change_patch_change (patch, key);
1979 MidiRegionView::next_patch (CanvasPatchChange& patch)
1981 if (patch.patch()->program() > 0) {
1982 MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch());
1983 key.program_number--;
1984 change_patch_change (patch, key);
1989 MidiRegionView::next_bank (CanvasPatchChange& patch)
1991 if (patch.patch()->program() < 127) {
1992 MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch());
1993 if (key.bank_number > 0) {
1995 change_patch_change (patch, key);
2001 MidiRegionView::previous_bank (CanvasPatchChange& patch)
2003 if (patch.patch()->program() > 0) {
2004 MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch());
2005 if (key.bank_number < 127) {
2007 change_patch_change (patch, key);
2013 MidiRegionView::maybe_remove_deleted_note_from_selection (CanvasNoteEvent* cne)
2015 if (_selection.empty()) {
2019 _selection.erase (cne);
2023 MidiRegionView::delete_selection()
2025 if (_selection.empty()) {
2029 start_note_diff_command (_("delete selection"));
2031 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2032 if ((*i)->selected()) {
2033 _note_diff_command->remove((*i)->note());
2043 MidiRegionView::delete_note (boost::shared_ptr<NoteType> n)
2045 start_note_diff_command (_("delete note"));
2046 _note_diff_command->remove (n);
2049 trackview.editor().verbose_cursor()->hide ();
2053 MidiRegionView::clear_selection_except (ArdourCanvas::CanvasNoteEvent* ev, bool signal)
2055 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2057 Selection::iterator tmp = i;
2060 (*i)->set_selected (false);
2061 (*i)->hide_velocity ();
2062 _selection.erase (i);
2070 /* this does not change the status of this regionview w.r.t the editor
2075 SelectionCleared (this); /* EMIT SIGNAL */
2080 MidiRegionView::unique_select(ArdourCanvas::CanvasNoteEvent* ev)
2082 clear_selection_except (ev);
2084 /* don't bother with checking to see if we should remove this
2085 regionview from the editor selection, since we're about to add
2086 another note, and thus put/keep this regionview in the editor
2090 if (!ev->selected()) {
2091 add_to_selection (ev);
2096 MidiRegionView::select_all_notes ()
2100 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2101 add_to_selection (*i);
2106 MidiRegionView::select_range (framepos_t start, framepos_t end)
2110 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2111 framepos_t t = source_beats_to_absolute_frames((*i)->note()->time());
2112 if (t >= start && t <= end) {
2113 add_to_selection (*i);
2119 MidiRegionView::invert_selection ()
2121 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2122 if ((*i)->selected()) {
2123 remove_from_selection(*i);
2125 add_to_selection (*i);
2131 MidiRegionView::select_matching_notes (uint8_t notenum, uint16_t channel_mask, bool add, bool extend)
2133 uint8_t low_note = 127;
2134 uint8_t high_note = 0;
2135 MidiModel::Notes& notes (_model->notes());
2136 _optimization_iterator = _events.begin();
2142 if (extend && _selection.empty()) {
2148 /* scan existing selection to get note range */
2150 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2151 if ((*i)->note()->note() < low_note) {
2152 low_note = (*i)->note()->note();
2154 if ((*i)->note()->note() > high_note) {
2155 high_note = (*i)->note()->note();
2159 low_note = min (low_note, notenum);
2160 high_note = max (high_note, notenum);
2163 _no_sound_notes = true;
2165 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
2167 boost::shared_ptr<NoteType> note (*n);
2168 CanvasNoteEvent* cne;
2169 bool select = false;
2171 if (((1 << note->channel()) & channel_mask) != 0) {
2173 if ((note->note() >= low_note && note->note() <= high_note)) {
2176 } else if (note->note() == notenum) {
2182 if ((cne = find_canvas_note (note)) != 0) {
2183 // extend is false because we've taken care of it,
2184 // since it extends by time range, not pitch.
2185 note_selected (cne, add, false);
2189 add = true; // we need to add all remaining matching notes, even if the passed in value was false (for "set")
2193 _no_sound_notes = false;
2197 MidiRegionView::toggle_matching_notes (uint8_t notenum, uint16_t channel_mask)
2199 MidiModel::Notes& notes (_model->notes());
2200 _optimization_iterator = _events.begin();
2202 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
2204 boost::shared_ptr<NoteType> note (*n);
2205 CanvasNoteEvent* cne;
2207 if (note->note() == notenum && (((0x0001 << note->channel()) & channel_mask) != 0)) {
2208 if ((cne = find_canvas_note (note)) != 0) {
2209 if (cne->selected()) {
2210 note_deselected (cne);
2212 note_selected (cne, true, false);
2220 MidiRegionView::note_selected (ArdourCanvas::CanvasNoteEvent* ev, bool add, bool extend)
2223 clear_selection_except (ev);
2224 if (!_selection.empty()) {
2225 PublicEditor& editor (trackview.editor());
2226 editor.get_selection().add (this);
2232 if (!ev->selected()) {
2233 add_to_selection (ev);
2237 /* find end of latest note selected, select all between that and the start of "ev" */
2239 Evoral::MusicalTime earliest = Evoral::MaxMusicalTime;
2240 Evoral::MusicalTime latest = 0;
2242 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2243 if ((*i)->note()->end_time() > latest) {
2244 latest = (*i)->note()->end_time();
2246 if ((*i)->note()->time() < earliest) {
2247 earliest = (*i)->note()->time();
2251 if (ev->note()->end_time() > latest) {
2252 latest = ev->note()->end_time();
2255 if (ev->note()->time() < earliest) {
2256 earliest = ev->note()->time();
2259 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2261 /* find notes entirely within OR spanning the earliest..latest range */
2263 if (((*i)->note()->time() >= earliest && (*i)->note()->end_time() <= latest) ||
2264 ((*i)->note()->time() <= earliest && (*i)->note()->end_time() >= latest)) {
2265 add_to_selection (*i);
2273 MidiRegionView::note_deselected(ArdourCanvas::CanvasNoteEvent* ev)
2275 remove_from_selection (ev);
2279 MidiRegionView::update_drag_selection(double x1, double x2, double y1, double y2, bool extend)
2289 // TODO: Make this faster by storing the last updated selection rect, and only
2290 // adjusting things that are in the area that appears/disappeared.
2291 // We probably need a tree to be able to find events in O(log(n)) time.
2293 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2295 /* check if any corner of the note is inside the rect
2298 1) this is computing "touched by", not "contained by" the rect.
2299 2) this does not require that events be sorted in time.
2302 const double ix1 = (*i)->x1();
2303 const double ix2 = (*i)->x2();
2304 const double iy1 = (*i)->y1();
2305 const double iy2 = (*i)->y2();
2307 if ((ix1 >= x1 && ix1 <= x2 && iy1 >= y1 && iy1 <= y2) ||
2308 (ix1 >= x1 && ix1 <= x2 && iy2 >= y1 && iy2 <= y2) ||
2309 (ix2 >= x1 && ix2 <= x2 && iy1 >= y1 && iy1 <= y2) ||
2310 (ix2 >= x1 && ix2 <= x2 && iy2 >= y1 && iy2 <= y2)) {
2313 if (!(*i)->selected()) {
2314 add_to_selection (*i);
2316 } else if ((*i)->selected() && !extend) {
2317 // Not inside rectangle
2318 remove_from_selection (*i);
2324 MidiRegionView::update_vertical_drag_selection (double y1, double y2, bool extend)
2330 // TODO: Make this faster by storing the last updated selection rect, and only
2331 // adjusting things that are in the area that appears/disappeared.
2332 // We probably need a tree to be able to find events in O(log(n)) time.
2334 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2336 /* check if any corner of the note is inside the rect
2339 1) this is computing "touched by", not "contained by" the rect.
2340 2) this does not require that events be sorted in time.
2343 if (((*i)->y1() >= y1 && (*i)->y1() <= y2)) {
2344 // within y- (note-) range
2345 if (!(*i)->selected()) {
2346 add_to_selection (*i);
2348 } else if ((*i)->selected() && !extend) {
2349 // Not inside rectangle
2350 remove_from_selection (*i);
2356 MidiRegionView::remove_from_selection (CanvasNoteEvent* ev)
2358 Selection::iterator i = _selection.find (ev);
2360 if (i != _selection.end()) {
2361 _selection.erase (i);
2364 ev->set_selected (false);
2365 ev->hide_velocity ();
2367 if (_selection.empty()) {
2368 PublicEditor& editor (trackview.editor());
2369 editor.get_selection().remove (this);
2374 MidiRegionView::add_to_selection (CanvasNoteEvent* ev)
2376 bool add_mrv_selection = false;
2378 if (_selection.empty()) {
2379 add_mrv_selection = true;
2382 if (_selection.insert (ev).second) {
2383 ev->set_selected (true);
2384 start_playing_midi_note ((ev)->note());
2387 if (add_mrv_selection) {
2388 PublicEditor& editor (trackview.editor());
2389 editor.get_selection().add (this);
2394 MidiRegionView::move_selection(double dx, double dy, double cumulative_dy)
2396 typedef vector<boost::shared_ptr<NoteType> > PossibleChord;
2397 PossibleChord to_play;
2398 Evoral::MusicalTime earliest = Evoral::MaxMusicalTime;
2400 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2401 if ((*i)->note()->time() < earliest) {
2402 earliest = (*i)->note()->time();
2406 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2407 if (Evoral::musical_time_equal ((*i)->note()->time(), earliest)) {
2408 to_play.push_back ((*i)->note());
2410 (*i)->move_event(dx, dy);
2413 if (dy && !_selection.empty() && !_no_sound_notes && Config->get_sound_midi_notes()) {
2415 if (to_play.size() > 1) {
2417 PossibleChord shifted;
2419 for (PossibleChord::iterator n = to_play.begin(); n != to_play.end(); ++n) {
2420 boost::shared_ptr<NoteType> moved_note (new NoteType (**n));
2421 moved_note->set_note (moved_note->note() + cumulative_dy);
2422 shifted.push_back (moved_note);
2425 start_playing_midi_chord (shifted);
2427 } else if (!to_play.empty()) {
2429 boost::shared_ptr<NoteType> moved_note (new NoteType (*to_play.front()));
2430 moved_note->set_note (moved_note->note() + cumulative_dy);
2431 start_playing_midi_note (moved_note);
2437 MidiRegionView::note_dropped(CanvasNoteEvent *, frameoffset_t dt, int8_t dnote)
2439 assert (!_selection.empty());
2441 uint8_t lowest_note_in_selection = 127;
2442 uint8_t highest_note_in_selection = 0;
2443 uint8_t highest_note_difference = 0;
2445 // find highest and lowest notes first
2447 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2448 uint8_t pitch = (*i)->note()->note();
2449 lowest_note_in_selection = std::min(lowest_note_in_selection, pitch);
2450 highest_note_in_selection = std::max(highest_note_in_selection, pitch);
2454 cerr << "dnote: " << (int) dnote << endl;
2455 cerr << "lowest note (streamview): " << int(midi_stream_view()->lowest_note())
2456 << " highest note (streamview): " << int(midi_stream_view()->highest_note()) << endl;
2457 cerr << "lowest note (selection): " << int(lowest_note_in_selection) << " highest note(selection): "
2458 << int(highest_note_in_selection) << endl;
2459 cerr << "selection size: " << _selection.size() << endl;
2460 cerr << "Highest note in selection: " << (int) highest_note_in_selection << endl;
2463 // Make sure the note pitch does not exceed the MIDI standard range
2464 if (highest_note_in_selection + dnote > 127) {
2465 highest_note_difference = highest_note_in_selection - 127;
2468 start_note_diff_command (_("move notes"));
2470 for (Selection::iterator i = _selection.begin(); i != _selection.end() ; ++i) {
2472 framepos_t new_frames = source_beats_to_absolute_frames ((*i)->note()->time()) + dt;
2473 Evoral::MusicalTime new_time = absolute_frames_to_source_beats (new_frames);
2479 note_diff_add_change (*i, MidiModel::NoteDiffCommand::StartTime, new_time);
2481 uint8_t original_pitch = (*i)->note()->note();
2482 uint8_t new_pitch = original_pitch + dnote - highest_note_difference;
2484 // keep notes in standard midi range
2485 clamp_to_0_127(new_pitch);
2487 lowest_note_in_selection = std::min(lowest_note_in_selection, new_pitch);
2488 highest_note_in_selection = std::max(highest_note_in_selection, new_pitch);
2490 note_diff_add_change (*i, MidiModel::NoteDiffCommand::NoteNumber, new_pitch);
2495 // care about notes being moved beyond the upper/lower bounds on the canvas
2496 if (lowest_note_in_selection < midi_stream_view()->lowest_note() ||
2497 highest_note_in_selection > midi_stream_view()->highest_note()) {
2498 midi_stream_view()->set_note_range(MidiStreamView::ContentsRange);
2502 /** @param x Pixel relative to the region position.
2503 * @return Snapped frame relative to the region position.
2506 MidiRegionView::snap_pixel_to_frame(double x)
2508 PublicEditor& editor (trackview.editor());
2509 return snap_frame_to_frame (editor.pixel_to_frame (x));
2512 /** @param x Pixel relative to the region position.
2513 * @return Snapped pixel relative to the region position.
2516 MidiRegionView::snap_to_pixel(double x)
2518 return (double) trackview.editor().frame_to_pixel(snap_pixel_to_frame(x));
2522 MidiRegionView::get_position_pixels()
2524 framepos_t region_frame = get_position();
2525 return trackview.editor().frame_to_pixel(region_frame);
2529 MidiRegionView::get_end_position_pixels()
2531 framepos_t frame = get_position() + get_duration ();
2532 return trackview.editor().frame_to_pixel(frame);
2536 MidiRegionView::source_beats_to_absolute_frames(double beats) const
2538 /* the time converter will return the frame corresponding to `beats'
2539 relative to the start of the source. The start of the source
2540 is an implied position given by region->position - region->start
2542 const framepos_t source_start = _region->position() - _region->start();
2543 return source_start + _source_relative_time_converter.to (beats);
2547 MidiRegionView::absolute_frames_to_source_beats(framepos_t frames) const
2549 /* the `frames' argument needs to be converted into a frame count
2550 relative to the start of the source before being passed in to the
2553 const framepos_t source_start = _region->position() - _region->start();
2554 return _source_relative_time_converter.from (frames - source_start);
2558 MidiRegionView::region_beats_to_region_frames(double beats) const
2560 return _region_relative_time_converter.to(beats);
2564 MidiRegionView::region_frames_to_region_beats(framepos_t frames) const
2566 return _region_relative_time_converter.from(frames);
2570 MidiRegionView::begin_resizing (bool /*at_front*/)
2572 _resize_data.clear();
2574 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2575 CanvasNote *note = dynamic_cast<CanvasNote *> (*i);
2577 // only insert CanvasNotes into the map
2579 NoteResizeData *resize_data = new NoteResizeData();
2580 resize_data->canvas_note = note;
2582 // create a new SimpleRect from the note which will be the resize preview
2583 SimpleRect *resize_rect = new SimpleRect(
2584 *_note_group, note->x1(), note->y1(), note->x2(), note->y2());
2586 // calculate the colors: get the color settings
2587 uint32_t fill_color = UINT_RGBA_CHANGE_A(
2588 ARDOUR_UI::config()->canvasvar_MidiNoteSelected.get(),
2591 // make the resize preview notes more transparent and bright
2592 fill_color = UINT_INTERPOLATE(fill_color, 0xFFFFFF40, 0.5);
2594 // calculate color based on note velocity
2595 resize_rect->property_fill_color_rgba() = UINT_INTERPOLATE(
2596 CanvasNoteEvent::meter_style_fill_color(note->note()->velocity(), note->selected()),
2600 resize_rect->property_outline_color_rgba() = CanvasNoteEvent::calculate_outline(
2601 ARDOUR_UI::config()->canvasvar_MidiNoteSelected.get());
2603 resize_data->resize_rect = resize_rect;
2604 _resize_data.push_back(resize_data);
2609 /** Update resizing notes while user drags.
2610 * @param primary `primary' note for the drag; ie the one that is used as the reference in non-relative mode.
2611 * @param at_front which end of the note (true == note on, false == note off)
2612 * @param delta_x change in mouse position since the start of the drag
2613 * @param relative true if relative resizing is taking place, false if absolute resizing. This only makes
2614 * a difference when multiple notes are being resized; in relative mode, each note's length is changed by the
2615 * amount of the drag. In non-relative mode, all selected notes are set to have the same start or end point
2616 * as the \a primary note.
2619 MidiRegionView::update_resizing (ArdourCanvas::CanvasNoteEvent* primary, bool at_front, double delta_x, bool relative)
2621 bool cursor_set = false;
2623 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2624 SimpleRect* resize_rect = (*i)->resize_rect;
2625 CanvasNote* canvas_note = (*i)->canvas_note;
2630 current_x = canvas_note->x1() + delta_x;
2632 current_x = primary->x1() + delta_x;
2636 current_x = canvas_note->x2() + delta_x;
2638 current_x = primary->x2() + delta_x;
2643 resize_rect->property_x1() = snap_to_pixel(current_x);
2644 resize_rect->property_x2() = canvas_note->x2();
2646 resize_rect->property_x2() = snap_to_pixel(current_x);
2647 resize_rect->property_x1() = canvas_note->x1();
2653 beats = snap_pixel_to_frame (current_x);
2654 beats = region_frames_to_region_beats (beats);
2659 if (beats < canvas_note->note()->end_time()) {
2660 len = canvas_note->note()->time() - beats;
2661 len += canvas_note->note()->length();
2666 if (beats >= canvas_note->note()->time()) {
2667 len = beats - canvas_note->note()->time();
2674 snprintf (buf, sizeof (buf), "%.3g beats", len);
2675 show_verbose_cursor (buf, 0, 0);
2684 /** Finish resizing notes when the user releases the mouse button.
2685 * Parameters the same as for \a update_resizing().
2688 MidiRegionView::commit_resizing (ArdourCanvas::CanvasNoteEvent* primary, bool at_front, double delta_x, bool relative)
2690 start_note_diff_command (_("resize notes"));
2692 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2693 CanvasNote* canvas_note = (*i)->canvas_note;
2694 SimpleRect* resize_rect = (*i)->resize_rect;
2696 /* Get the new x position for this resize, which is in pixels relative
2697 * to the region position.
2704 current_x = canvas_note->x1() + delta_x;
2706 current_x = primary->x1() + delta_x;
2710 current_x = canvas_note->x2() + delta_x;
2712 current_x = primary->x2() + delta_x;
2716 /* Convert that to a frame within the source */
2717 current_x = snap_pixel_to_frame (current_x) + _region->start ();
2719 /* and then to beats */
2720 current_x = region_frames_to_region_beats (current_x);
2722 if (at_front && current_x < canvas_note->note()->end_time()) {
2723 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::StartTime, current_x);
2725 double len = canvas_note->note()->time() - current_x;
2726 len += canvas_note->note()->length();
2729 /* XXX convert to beats */
2730 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
2735 double len = current_x - canvas_note->note()->time();
2738 /* XXX convert to beats */
2739 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
2747 _resize_data.clear();
2752 MidiRegionView::abort_resizing ()
2754 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2755 delete (*i)->resize_rect;
2759 _resize_data.clear ();
2763 MidiRegionView::change_note_velocity(CanvasNoteEvent* event, int8_t velocity, bool relative)
2765 uint8_t new_velocity;
2768 new_velocity = event->note()->velocity() + velocity;
2769 clamp_to_0_127(new_velocity);
2771 new_velocity = velocity;
2774 event->set_selected (event->selected()); // change color
2776 note_diff_add_change (event, MidiModel::NoteDiffCommand::Velocity, new_velocity);
2780 MidiRegionView::change_note_note (CanvasNoteEvent* event, int8_t note, bool relative)
2785 new_note = event->note()->note() + note;
2790 clamp_to_0_127 (new_note);
2791 note_diff_add_change (event, MidiModel::NoteDiffCommand::NoteNumber, new_note);
2795 MidiRegionView::trim_note (CanvasNoteEvent* event, Evoral::MusicalTime front_delta, Evoral::MusicalTime end_delta)
2797 bool change_start = false;
2798 bool change_length = false;
2799 Evoral::MusicalTime new_start = 0;
2800 Evoral::MusicalTime new_length = 0;
2802 /* NOTE: the semantics of the two delta arguments are slightly subtle:
2804 front_delta: if positive - move the start of the note later in time (shortening it)
2805 if negative - move the start of the note earlier in time (lengthening it)
2807 end_delta: if positive - move the end of the note later in time (lengthening it)
2808 if negative - move the end of the note earlier in time (shortening it)
2812 if (front_delta < 0) {
2814 if (event->note()->time() < -front_delta) {
2817 new_start = event->note()->time() + front_delta; // moves earlier
2820 /* start moved toward zero, so move the end point out to where it used to be.
2821 Note that front_delta is negative, so this increases the length.
2824 new_length = event->note()->length() - front_delta;
2825 change_start = true;
2826 change_length = true;
2830 Evoral::MusicalTime new_pos = event->note()->time() + front_delta;
2832 if (new_pos < event->note()->end_time()) {
2833 new_start = event->note()->time() + front_delta;
2834 /* start moved toward the end, so move the end point back to where it used to be */
2835 new_length = event->note()->length() - front_delta;
2836 change_start = true;
2837 change_length = true;
2844 bool can_change = true;
2845 if (end_delta < 0) {
2846 if (event->note()->length() < -end_delta) {
2852 new_length = event->note()->length() + end_delta;
2853 change_length = true;
2858 note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_start);
2861 if (change_length) {
2862 note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, new_length);
2867 MidiRegionView::change_note_channel (CanvasNoteEvent* event, int8_t chn, bool relative)
2869 uint8_t new_channel;
2873 if (event->note()->channel() < -chn) {
2876 new_channel = event->note()->channel() + chn;
2879 new_channel = event->note()->channel() + chn;
2882 new_channel = (uint8_t) chn;
2885 note_diff_add_change (event, MidiModel::NoteDiffCommand::Channel, new_channel);
2889 MidiRegionView::change_note_time (CanvasNoteEvent* event, Evoral::MusicalTime delta, bool relative)
2891 Evoral::MusicalTime new_time;
2895 if (event->note()->time() < -delta) {
2898 new_time = event->note()->time() + delta;
2901 new_time = event->note()->time() + delta;
2907 note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_time);
2911 MidiRegionView::change_note_length (CanvasNoteEvent* event, Evoral::MusicalTime t)
2913 note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, t);
2917 MidiRegionView::change_velocities (bool up, bool fine, bool allow_smush, bool all_together)
2922 if (_selection.empty()) {
2937 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2938 if ((*i)->note()->velocity() < -delta || (*i)->note()->velocity() + delta > 127) {
2944 start_note_diff_command (_("change velocities"));
2946 for (Selection::iterator i = _selection.begin(); i != _selection.end();) {
2947 Selection::iterator next = i;
2951 if (i == _selection.begin()) {
2952 change_note_velocity (*i, delta, true);
2953 value = (*i)->note()->velocity() + delta;
2955 change_note_velocity (*i, value, false);
2959 change_note_velocity (*i, delta, true);
2968 if (!_selection.empty()) {
2970 snprintf (buf, sizeof (buf), "Vel %d",
2971 (int) (*_selection.begin())->note()->velocity());
2972 show_verbose_cursor (buf, 10, 10);
2978 MidiRegionView::transpose (bool up, bool fine, bool allow_smush)
2980 if (_selection.empty()) {
2997 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2999 if ((int8_t) (*i)->note()->note() + delta <= 0) {
3003 if ((int8_t) (*i)->note()->note() + delta > 127) {
3010 start_note_diff_command (_("transpose"));
3012 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3013 Selection::iterator next = i;
3015 change_note_note (*i, delta, true);
3023 MidiRegionView::change_note_lengths (bool fine, bool shorter, Evoral::MusicalTime delta, bool start, bool end)
3029 /* grab the current grid distance */
3031 delta = trackview.editor().get_grid_type_as_beats (success, _region->position());
3033 /* XXX cannot get grid type as beats ... should always be possible ... FIX ME */
3034 error << string_compose (_("programming error: %1"), "Grid type not available as beats - TO BE FIXED") << endmsg;
3044 start_note_diff_command (_("change note lengths"));
3046 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3047 Selection::iterator next = i;
3050 /* note the negation of the delta for start */
3052 trim_note (*i, (start ? -delta : 0), (end ? delta : 0));
3061 MidiRegionView::nudge_notes (bool forward)
3063 if (_selection.empty()) {
3067 /* pick a note as the point along the timeline to get the nudge distance.
3068 its not necessarily the earliest note, so we may want to pull the notes out
3069 into a vector and sort before using the first one.
3072 framepos_t ref_point = source_beats_to_absolute_frames ((*(_selection.begin()))->note()->time());
3074 framecnt_t distance;
3076 if (trackview.editor().snap_mode() == Editing::SnapOff) {
3078 /* grid is off - use nudge distance */
3080 distance = trackview.editor().get_nudge_distance (ref_point, unused);
3086 framepos_t next_pos = ref_point;
3089 if (max_framepos - 1 < next_pos) {
3093 if (next_pos == 0) {
3099 trackview.editor().snap_to (next_pos, (forward ? 1 : -1), false);
3100 distance = ref_point - next_pos;
3103 if (distance == 0) {
3107 Evoral::MusicalTime delta = region_frames_to_region_beats (fabs (distance));
3113 start_note_diff_command (_("nudge"));
3115 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3116 Selection::iterator next = i;
3118 change_note_time (*i, delta, true);
3126 MidiRegionView::change_channel(uint8_t channel)
3128 start_note_diff_command(_("change channel"));
3129 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3130 note_diff_add_change (*i, MidiModel::NoteDiffCommand::Channel, channel);
3138 MidiRegionView::note_entered(ArdourCanvas::CanvasNoteEvent* ev)
3140 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3142 pre_enter_cursor = editor->get_canvas_cursor ();
3144 if (_mouse_state == SelectTouchDragging) {
3145 note_selected (ev, true);
3148 show_verbose_cursor (ev->note ());
3152 MidiRegionView::note_left (ArdourCanvas::CanvasNoteEvent*)
3154 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3156 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3157 (*i)->hide_velocity ();
3160 editor->verbose_cursor()->hide ();
3162 if (pre_enter_cursor) {
3163 editor->set_canvas_cursor (pre_enter_cursor);
3164 pre_enter_cursor = 0;
3169 MidiRegionView::patch_entered (ArdourCanvas::CanvasPatchChange* p)
3172 /* XXX should get patch name if we can */
3173 s << _("Bank:") << (p->patch()->bank() + MIDI_BP_ZERO) << '\n'
3174 << _("Program:") << ((int) p->patch()->program()) + MIDI_BP_ZERO << '\n'
3175 << _("Channel:") << ((int) p->patch()->channel() + 1);
3176 show_verbose_cursor (s.str(), 10, 20);
3181 MidiRegionView::patch_left (ArdourCanvas::CanvasPatchChange *)
3183 trackview.editor().verbose_cursor()->hide ();
3184 /* focus will transfer back via the enter-notify event sent to this
3190 MidiRegionView::sysex_entered (ArdourCanvas::CanvasSysEx* p)
3194 show_verbose_cursor (s.str(), 10, 20);
3199 MidiRegionView::sysex_left (ArdourCanvas::CanvasSysEx *)
3201 trackview.editor().verbose_cursor()->hide ();
3202 /* focus will transfer back via the enter-notify event sent to this
3208 MidiRegionView::note_mouse_position (float x_fraction, float /*y_fraction*/, bool can_set_cursor)
3210 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3211 Editing::MouseMode mm = editor->current_mouse_mode();
3212 bool trimmable = (mm == MouseObject || mm == MouseTimeFX || mm == MouseDraw);
3214 if (trimmable && x_fraction > 0.0 && x_fraction < 0.2) {
3215 editor->set_canvas_cursor (editor->cursors()->left_side_trim);
3216 } else if (trimmable && x_fraction >= 0.8 && x_fraction < 1.0) {
3217 editor->set_canvas_cursor (editor->cursors()->right_side_trim);
3219 if (pre_enter_cursor && can_set_cursor) {
3220 editor->set_canvas_cursor (pre_enter_cursor);
3226 MidiRegionView::set_frame_color()
3230 TimeAxisViewItem::set_frame_color ();
3237 f = ARDOUR_UI::config()->canvasvar_SelectedFrameBase.get();
3238 } else if (high_enough_for_name) {
3239 f= ARDOUR_UI::config()->canvasvar_MidiFrameBase.get();
3244 if (!rect_visible) {
3245 f = UINT_RGBA_CHANGE_A (f, 0);
3248 frame->property_fill_color_rgba() = f;
3252 MidiRegionView::midi_channel_mode_changed(ChannelMode mode, uint16_t mask)
3254 if (mode == ForceChannel) {
3255 mask = 0xFFFF; // Show all notes as active (below)
3258 // Update notes for selection
3259 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3260 (*i)->on_channel_selection_change(mask);
3263 _last_channel_selection = mask;
3264 _last_channel_mode = mode;
3266 _patch_changes.clear ();
3267 display_patch_changes ();
3271 MidiRegionView::instrument_settings_changed ()
3277 MidiRegionView::cut_copy_clear (Editing::CutCopyOp op)
3279 if (_selection.empty()) {
3283 PublicEditor& editor (trackview.editor());
3287 /* XXX what to do ? */
3291 editor.get_cut_buffer().add (selection_as_cut_buffer());
3299 start_note_diff_command();
3301 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3308 note_diff_remove_note (*i);
3318 MidiRegionView::selection_as_cut_buffer () const
3322 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3323 NoteType* n = (*i)->note().get();
3324 notes.insert (boost::shared_ptr<NoteType> (new NoteType (*n)));
3327 MidiCutBuffer* cb = new MidiCutBuffer (trackview.session());
3333 /** This method handles undo */
3335 MidiRegionView::paste (framepos_t pos, float times, const MidiCutBuffer& mcb)
3341 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("MIDI paste @ %1 times %2\n", pos, times));
3343 trackview.session()->begin_reversible_command (_("paste"));
3345 start_note_diff_command (_("paste"));
3347 Evoral::MusicalTime beat_delta;
3348 Evoral::MusicalTime paste_pos_beats;
3349 Evoral::MusicalTime duration;
3350 Evoral::MusicalTime end_point = 0;
3352 duration = (*mcb.notes().rbegin())->end_time() - (*mcb.notes().begin())->time();
3353 paste_pos_beats = absolute_frames_to_source_beats (pos);
3354 beat_delta = (*mcb.notes().begin())->time() - paste_pos_beats;
3355 paste_pos_beats = 0;
3357 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",
3358 (*mcb.notes().begin())->time(),
3359 (*mcb.notes().rbegin())->end_time(),
3360 duration, pos, _region->position(),
3361 paste_pos_beats, beat_delta));
3365 for (int n = 0; n < (int) times; ++n) {
3367 for (Notes::const_iterator i = mcb.notes().begin(); i != mcb.notes().end(); ++i) {
3369 boost::shared_ptr<NoteType> copied_note (new NoteType (*((*i).get())));
3370 copied_note->set_time (paste_pos_beats + copied_note->time() - beat_delta);
3372 /* make all newly added notes selected */
3374 note_diff_add_note (copied_note, true);
3375 end_point = copied_note->end_time();
3378 paste_pos_beats += duration;
3381 /* if we pasted past the current end of the region, extend the region */
3383 framepos_t end_frame = source_beats_to_absolute_frames (end_point);
3384 framepos_t region_end = _region->position() + _region->length() - 1;
3386 if (end_frame > region_end) {
3388 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste extended region from %1 to %2\n", region_end, end_frame));
3390 _region->clear_changes ();
3391 _region->set_length (end_frame - _region->position());
3392 trackview.session()->add_command (new StatefulDiffCommand (_region));
3397 trackview.session()->commit_reversible_command ();
3400 struct EventNoteTimeEarlyFirstComparator {
3401 bool operator() (CanvasNoteEvent* a, CanvasNoteEvent* b) {
3402 return a->note()->time() < b->note()->time();
3407 MidiRegionView::time_sort_events ()
3409 if (!_sort_needed) {
3413 EventNoteTimeEarlyFirstComparator cmp;
3416 _sort_needed = false;
3420 MidiRegionView::goto_next_note (bool add_to_selection)
3422 bool use_next = false;
3424 if (_events.back()->selected()) {
3428 time_sort_events ();
3430 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3431 uint16_t const channel_mask = mtv->channel_selector().get_selected_channels ();
3433 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3434 if ((*i)->selected()) {
3437 } else if (use_next) {
3438 if (channel_mask & (1 << (*i)->note()->channel())) {
3439 if (!add_to_selection) {
3442 note_selected (*i, true, false);
3449 /* use the first one */
3451 if (!_events.empty() && (channel_mask & (1 << _events.front()->note()->channel ()))) {
3452 unique_select (_events.front());
3457 MidiRegionView::goto_previous_note (bool add_to_selection)
3459 bool use_next = false;
3461 if (_events.front()->selected()) {
3465 time_sort_events ();
3467 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3468 uint16_t const channel_mask = mtv->channel_selector().get_selected_channels ();
3470 for (Events::reverse_iterator i = _events.rbegin(); i != _events.rend(); ++i) {
3471 if ((*i)->selected()) {
3474 } else if (use_next) {
3475 if (channel_mask & (1 << (*i)->note()->channel())) {
3476 if (!add_to_selection) {
3479 note_selected (*i, true, false);
3486 /* use the last one */
3488 if (!_events.empty() && (channel_mask & (1 << (*_events.rbegin())->note()->channel ()))) {
3489 unique_select (*(_events.rbegin()));
3494 MidiRegionView::selection_as_notelist (Notes& selected, bool allow_all_if_none_selected)
3496 bool had_selected = false;
3498 time_sort_events ();
3500 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3501 if ((*i)->selected()) {
3502 selected.insert ((*i)->note());
3503 had_selected = true;
3507 if (allow_all_if_none_selected && !had_selected) {
3508 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3509 selected.insert ((*i)->note());
3515 MidiRegionView::update_ghost_note (double x, double y)
3517 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3522 _note_group->w2i (x, y);
3524 PublicEditor& editor = trackview.editor ();
3526 framepos_t const unsnapped_frame = editor.pixel_to_frame (x);
3527 framecnt_t grid_frames;
3528 framepos_t const f = snap_frame_to_grid_underneath (unsnapped_frame, grid_frames);
3530 /* use region_frames... because we are converting a delta within the region
3534 double length = editor.get_grid_type_as_beats (success, unsnapped_frame);
3540 /* note that this sets the time of the ghost note in beats relative to
3541 the start of the source; that is how all note times are stored.
3543 _ghost_note->note()->set_time (absolute_frames_to_source_beats (f + _region->position ()));
3544 _ghost_note->note()->set_length (length);
3545 _ghost_note->note()->set_note (midi_stream_view()->y_to_note (y));
3546 _ghost_note->note()->set_channel (mtv->get_channel_for_add ());
3548 /* the ghost note does not appear in ghost regions, so pass false in here */
3549 update_note (_ghost_note, false);
3551 show_verbose_cursor (_ghost_note->note ());
3555 MidiRegionView::create_ghost_note (double x, double y)
3557 remove_ghost_note ();
3559 boost::shared_ptr<NoteType> g (new NoteType);
3560 _ghost_note = new NoEventCanvasNote (*this, *_note_group, g);
3561 _ghost_note->property_outline_color_rgba() = 0x000000aa;
3562 update_ghost_note (x, y);
3563 _ghost_note->show ();
3568 show_verbose_cursor (_ghost_note->note ());
3572 MidiRegionView::snap_changed ()
3578 create_ghost_note (_last_ghost_x, _last_ghost_y);
3582 MidiRegionView::drop_down_keys ()
3584 _mouse_state = None;
3588 MidiRegionView::maybe_select_by_position (GdkEventButton* ev, double /*x*/, double y)
3590 double note = midi_stream_view()->y_to_note(y);
3592 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3594 uint16_t chn_mask = mtv->channel_selector().get_selected_channels();
3596 if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
3597 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchGreaterThanOrEqual, (uint8_t) floor (note), chn_mask);
3598 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
3599 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchLessThanOrEqual, (uint8_t) floor (note), chn_mask);
3604 bool add_mrv_selection = false;
3606 if (_selection.empty()) {
3607 add_mrv_selection = true;
3610 for (Events::iterator i = e.begin(); i != e.end(); ++i) {
3611 if (_selection.insert (*i).second) {
3612 (*i)->set_selected (true);
3616 if (add_mrv_selection) {
3617 PublicEditor& editor (trackview.editor());
3618 editor.get_selection().add (this);
3623 MidiRegionView::color_handler ()
3625 RegionView::color_handler ();
3627 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3628 (*i)->set_selected ((*i)->selected()); // will change color
3631 /* XXX probably more to do here */
3635 MidiRegionView::enable_display (bool yn)
3637 RegionView::enable_display (yn);
3644 MidiRegionView::show_step_edit_cursor (Evoral::MusicalTime pos)
3646 if (_step_edit_cursor == 0) {
3647 ArdourCanvas::Group* const group = (ArdourCanvas::Group*)get_canvas_group();
3649 _step_edit_cursor = new ArdourCanvas::SimpleRect (*group);
3650 _step_edit_cursor->property_y1() = 0;
3651 _step_edit_cursor->property_y2() = midi_stream_view()->contents_height();
3652 _step_edit_cursor->property_fill_color_rgba() = RGBA_TO_UINT (45,0,0,90);
3653 _step_edit_cursor->property_outline_color_rgba() = RGBA_TO_UINT (85,0,0,90);
3656 move_step_edit_cursor (pos);
3657 _step_edit_cursor->show ();
3661 MidiRegionView::move_step_edit_cursor (Evoral::MusicalTime pos)
3663 _step_edit_cursor_position = pos;
3665 if (_step_edit_cursor) {
3666 double pixel = trackview.editor().frame_to_pixel (region_beats_to_region_frames (pos));
3667 _step_edit_cursor->property_x1() = pixel;
3668 set_step_edit_cursor_width (_step_edit_cursor_width);
3673 MidiRegionView::hide_step_edit_cursor ()
3675 if (_step_edit_cursor) {
3676 _step_edit_cursor->hide ();
3681 MidiRegionView::set_step_edit_cursor_width (Evoral::MusicalTime beats)
3683 _step_edit_cursor_width = beats;
3685 if (_step_edit_cursor) {
3686 _step_edit_cursor->property_x2() = _step_edit_cursor->property_x1() + trackview.editor().frame_to_pixel (region_beats_to_region_frames (beats));
3690 /** Called when a diskstream on our track has received some data. Update the view, if applicable.
3691 * @param w Source that the data will end up in.
3694 MidiRegionView::data_recorded (boost::weak_ptr<MidiSource> w)
3696 if (!_active_notes) {
3697 /* we aren't actively being recorded to */
3701 boost::shared_ptr<MidiSource> src = w.lock ();
3702 if (!src || src != midi_region()->midi_source()) {
3703 /* recorded data was not destined for our source */
3707 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (&trackview);
3709 boost::shared_ptr<MidiBuffer> buf = mtv->midi_track()->get_gui_feed_buffer ();
3711 BeatsFramesConverter converter (trackview.session()->tempo_map(), mtv->midi_track()->get_capture_start_frame (0));
3713 framepos_t back = max_framepos;
3715 for (MidiBuffer::iterator i = buf->begin(); i != buf->end(); ++i) {
3716 Evoral::MIDIEvent<MidiBuffer::TimeType> const ev (*i, false);
3717 assert (ev.buffer ());
3719 if(ev.is_channel_event()) {
3720 if (_last_channel_mode == FilterChannels) {
3721 if(((uint16_t(1) << ev.channel()) & _last_channel_selection) == 0) {
3727 /* ev.time() is in session frames, so (ev.time() - converter.origin_b()) is
3728 frames from the start of the source, and so time_beats is in terms of the
3732 Evoral::MusicalTime const time_beats = converter.from (ev.time () - converter.origin_b ());
3734 if (ev.type() == MIDI_CMD_NOTE_ON) {
3736 boost::shared_ptr<NoteType> note (
3737 new NoteType (ev.channel(), time_beats, 0, ev.note(), ev.velocity())
3740 add_note (note, true);
3742 /* fix up our note range */
3743 if (ev.note() < _current_range_min) {
3744 midi_stream_view()->apply_note_range (ev.note(), _current_range_max, true);
3745 } else if (ev.note() > _current_range_max) {
3746 midi_stream_view()->apply_note_range (_current_range_min, ev.note(), true);
3749 } else if (ev.type() == MIDI_CMD_NOTE_OFF) {
3750 resolve_note (ev.note (), time_beats);
3756 midi_stream_view()->check_record_layers (region(), back);
3760 MidiRegionView::trim_front_starting ()
3762 /* Reparent the note group to the region view's parent, so that it doesn't change
3763 when the region view is trimmed.
3765 _temporary_note_group = new ArdourCanvas::Group (*group->property_parent ());
3766 _temporary_note_group->move (group->property_x(), group->property_y());
3767 _note_group->reparent (*_temporary_note_group);
3771 MidiRegionView::trim_front_ending ()
3773 _note_group->reparent (*group);
3774 delete _temporary_note_group;
3775 _temporary_note_group = 0;
3777 if (_region->start() < 0) {
3778 /* Trim drag made start time -ve; fix this */
3779 midi_region()->fix_negative_start ();
3784 MidiRegionView::edit_patch_change (ArdourCanvas::CanvasPatchChange* pc)
3786 PatchChangeDialog d (&_source_relative_time_converter, trackview.session(), *pc->patch (), instrument_info(), Gtk::Stock::APPLY, true);
3788 d.set_position (Gtk::WIN_POS_MOUSE);
3790 int response = d.run();
3793 case Gtk::RESPONSE_ACCEPT:
3795 case Gtk::RESPONSE_REJECT:
3796 delete_patch_change (pc);
3802 change_patch_change (pc->patch(), d.patch ());
3806 MidiRegionView::delete_sysex (CanvasSysEx* sysex)
3808 MidiModel::SysExDiffCommand* c = _model->new_sysex_diff_command (_("delete sysex"));
3809 c->remove (sysex->sysex());
3810 _model->apply_command (*trackview.session(), c);
3817 MidiRegionView::show_verbose_cursor (boost::shared_ptr<NoteType> n) const
3820 snprintf (buf, sizeof (buf), "%s (%d) Chn %d\nVel %d",
3821 Evoral::midi_note_name (n->note()).c_str(),
3823 (int) n->channel() + 1,
3824 (int) n->velocity());
3826 show_verbose_cursor (buf, 10, 20);
3830 MidiRegionView::show_verbose_cursor (string const & text, double xoffset, double yoffset) const
3834 trackview.editor().get_pointer_position (wx, wy);
3839 /* Flip the cursor above the mouse pointer if it would overlap the bottom of the canvas */
3841 double x1, y1, x2, y2;
3842 trackview.editor().verbose_cursor()->canvas_item()->get_bounds (x1, y1, x2, y2);
3844 if ((wy + y2 - y1) > trackview.editor().canvas_height()) {
3845 wy -= (y2 - y1) + 2 * yoffset;
3848 trackview.editor().verbose_cursor()->set (text, wx, wy);
3849 trackview.editor().verbose_cursor()->show ();
3852 /** @param p A session framepos.
3853 * @param grid_frames Filled in with the number of frames that a grid interval is at p.
3854 * @return p snapped to the grid subdivision underneath it.
3857 MidiRegionView::snap_frame_to_grid_underneath (framepos_t p, framecnt_t& grid_frames) const
3859 PublicEditor& editor = trackview.editor ();
3862 Evoral::MusicalTime grid_beats = editor.get_grid_type_as_beats (success, p);
3868 grid_frames = region_beats_to_region_frames (grid_beats);
3870 /* Hack so that we always snap to the note that we are over, instead of snapping
3871 to the next one if we're more than halfway through the one we're over.
3873 if (editor.snap_mode() == SnapNormal && p >= grid_frames / 2) {
3874 p -= grid_frames / 2;
3877 return snap_frame_to_frame (p);
3880 /** Called when the selection has been cleared in any MidiRegionView.
3881 * @param rv MidiRegionView that the selection was cleared in.
3884 MidiRegionView::selection_cleared (MidiRegionView* rv)
3890 /* Clear our selection in sympathy; but don't signal the fact */
3891 clear_selection (false);
3895 MidiRegionView::note_button_release ()
3897 delete _note_player;