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 "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 if (length < 2 * DBL_EPSILON) {
922 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
923 MidiStreamView* const view = mtv->midi_view();
925 const double note = view->y_to_note(y);
927 // Start of note in frames relative to region start
929 framecnt_t grid_frames;
930 t = snap_frame_to_grid_underneath (t, grid_frames);
933 const boost::shared_ptr<NoteType> new_note (
934 new NoteType (mtv->get_channel_for_add (),
935 region_frames_to_region_beats(t + _region->start()),
937 (uint8_t)note, 0x40));
939 if (_model->contains (new_note)) {
943 view->update_note_range(new_note->note());
945 MidiModel::NoteDiffCommand* cmd = _model->new_note_diff_command(_("add note"));
947 _model->apply_command(*trackview.session(), cmd);
949 play_midi_note (new_note);
953 MidiRegionView::clear_events (bool with_selection_signal)
955 clear_selection (with_selection_signal);
958 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
959 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
964 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
969 _patch_changes.clear();
971 _optimization_iterator = _events.end();
975 MidiRegionView::display_model(boost::shared_ptr<MidiModel> model)
979 content_connection.disconnect ();
980 _model->ContentsChanged.connect (content_connection, invalidator (*this), boost::bind (&MidiRegionView::redisplay_model, this), gui_context());
984 if (_enable_display) {
990 MidiRegionView::start_note_diff_command (string name)
992 if (!_note_diff_command) {
993 _note_diff_command = _model->new_note_diff_command (name);
998 MidiRegionView::note_diff_add_note (const boost::shared_ptr<NoteType> note, bool selected, bool show_velocity)
1000 if (_note_diff_command) {
1001 _note_diff_command->add (note);
1004 _marked_for_selection.insert(note);
1006 if (show_velocity) {
1007 _marked_for_velocity.insert(note);
1012 MidiRegionView::note_diff_remove_note (ArdourCanvas::CanvasNoteEvent* ev)
1014 if (_note_diff_command && ev->note()) {
1015 _note_diff_command->remove(ev->note());
1020 MidiRegionView::note_diff_add_change (ArdourCanvas::CanvasNoteEvent* ev,
1021 MidiModel::NoteDiffCommand::Property property,
1024 if (_note_diff_command) {
1025 _note_diff_command->change (ev->note(), property, val);
1030 MidiRegionView::note_diff_add_change (ArdourCanvas::CanvasNoteEvent* ev,
1031 MidiModel::NoteDiffCommand::Property property,
1032 Evoral::MusicalTime val)
1034 if (_note_diff_command) {
1035 _note_diff_command->change (ev->note(), property, val);
1040 MidiRegionView::apply_diff (bool as_subcommand)
1044 if (!_note_diff_command) {
1048 if ((add_or_remove = _note_diff_command->adds_or_removes())) {
1049 // Mark all selected notes for selection when model reloads
1050 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1051 _marked_for_selection.insert((*i)->note());
1055 if (as_subcommand) {
1056 _model->apply_command_as_subcommand (*trackview.session(), _note_diff_command);
1058 _model->apply_command (*trackview.session(), _note_diff_command);
1061 _note_diff_command = 0;
1062 midi_view()->midi_track()->playlist_modified();
1064 if (add_or_remove) {
1065 _marked_for_selection.clear();
1068 _marked_for_velocity.clear();
1072 MidiRegionView::abort_command()
1074 delete _note_diff_command;
1075 _note_diff_command = 0;
1080 MidiRegionView::find_canvas_note (boost::shared_ptr<NoteType> note)
1082 if (_optimization_iterator != _events.end()) {
1083 ++_optimization_iterator;
1086 if (_optimization_iterator != _events.end() && (*_optimization_iterator)->note() == note) {
1087 return *_optimization_iterator;
1090 for (_optimization_iterator = _events.begin(); _optimization_iterator != _events.end(); ++_optimization_iterator) {
1091 if ((*_optimization_iterator)->note() == note) {
1092 return *_optimization_iterator;
1100 MidiRegionView::get_events (Events& e, Evoral::Sequence<Evoral::MusicalTime>::NoteOperator op, uint8_t val, int chan_mask)
1102 MidiModel::Notes notes;
1103 _model->get_notes (notes, op, val, chan_mask);
1105 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1106 CanvasNoteEvent* cne = find_canvas_note (*n);
1114 MidiRegionView::redisplay_model()
1116 // Don't redisplay the model if we're currently recording and displaying that
1117 if (_active_notes) {
1125 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1126 (*i)->invalidate ();
1129 MidiModel::ReadLock lock(_model->read_lock());
1131 MidiModel::Notes& notes (_model->notes());
1132 _optimization_iterator = _events.begin();
1134 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1136 boost::shared_ptr<NoteType> note (*n);
1137 CanvasNoteEvent* cne;
1140 if (note_in_region_range (note, visible)) {
1142 if ((cne = find_canvas_note (note)) != 0) {
1149 if ((cn = dynamic_cast<CanvasNote*>(cne)) != 0) {
1151 } else if ((ch = dynamic_cast<CanvasHit*>(cne)) != 0) {
1163 add_note (note, visible);
1168 if ((cne = find_canvas_note (note)) != 0) {
1176 /* remove note items that are no longer valid */
1178 for (Events::iterator i = _events.begin(); i != _events.end(); ) {
1179 if (!(*i)->valid ()) {
1181 for (vector<GhostRegion*>::iterator j = ghosts.begin(); j != ghosts.end(); ++j) {
1182 MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*j);
1184 gr->remove_note (*i);
1189 i = _events.erase (i);
1196 _patch_changes.clear();
1200 display_patch_changes ();
1202 _marked_for_selection.clear ();
1203 _marked_for_velocity.clear ();
1205 /* we may have caused _events to contain things out of order (e.g. if a note
1206 moved earlier or later). we don't generally need them in time order, but
1207 make a note that a sort is required for those cases that require it.
1210 _sort_needed = true;
1214 MidiRegionView::display_patch_changes ()
1216 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1217 uint16_t chn_mask = mtv->channel_selector().get_selected_channels();
1219 for (uint8_t i = 0; i < 16; ++i) {
1220 display_patch_changes_on_channel (i, chn_mask & (1 << i));
1224 /** @param active_channel true to display patch changes fully, false to display
1225 * them `greyed-out' (as on an inactive channel)
1228 MidiRegionView::display_patch_changes_on_channel (uint8_t channel, bool active_channel)
1230 for (MidiModel::PatchChanges::const_iterator i = _model->patch_changes().begin(); i != _model->patch_changes().end(); ++i) {
1232 if ((*i)->channel() != channel) {
1236 const string patch_name = instrument_info().get_patch_name ((*i)->bank(), (*i)->program(), channel);
1237 add_canvas_patch_change (*i, patch_name, active_channel);
1242 MidiRegionView::display_sysexes()
1244 bool have_periodic_system_messages = false;
1245 bool display_periodic_messages = true;
1247 if (!Config->get_never_display_periodic_midi()) {
1249 for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
1250 const boost::shared_ptr<const Evoral::MIDIEvent<Evoral::MusicalTime> > mev =
1251 boost::static_pointer_cast<const Evoral::MIDIEvent<Evoral::MusicalTime> > (*i);
1254 if (mev->is_spp() || mev->is_mtc_quarter() || mev->is_mtc_full()) {
1255 have_periodic_system_messages = true;
1261 if (have_periodic_system_messages) {
1262 double zoom = trackview.editor().get_current_zoom (); // frames per pixel
1264 /* get an approximate value for the number of samples per video frame */
1266 double video_frame = trackview.session()->frame_rate() * (1.0/30);
1268 /* if we are zoomed out beyond than the cutoff (i.e. more
1269 * frames per pixel than frames per 4 video frames), don't
1270 * show periodic sysex messages.
1273 if (zoom > (video_frame*4)) {
1274 display_periodic_messages = false;
1278 display_periodic_messages = false;
1281 for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
1283 const boost::shared_ptr<const Evoral::MIDIEvent<Evoral::MusicalTime> > mev =
1284 boost::static_pointer_cast<const Evoral::MIDIEvent<Evoral::MusicalTime> > (*i);
1286 Evoral::MusicalTime time = (*i)->time();
1289 if (mev->is_spp() || mev->is_mtc_quarter() || mev->is_mtc_full()) {
1290 if (!display_periodic_messages) {
1298 for (uint32_t b = 0; b < (*i)->size(); ++b) {
1299 str << int((*i)->buffer()[b]);
1300 if (b != (*i)->size() -1) {
1304 string text = str.str();
1306 const double x = trackview.editor().frame_to_pixel(source_beats_to_region_frames(time));
1308 double height = midi_stream_view()->contents_height();
1310 boost::shared_ptr<CanvasSysEx> sysex = boost::shared_ptr<CanvasSysEx>(
1311 new CanvasSysEx(*this, *_note_group, text, height, x, 1.0, (*i)));
1313 // Show unless message is beyond the region bounds
1314 if (time - _region->start() >= _region->length() || time < _region->start()) {
1320 _sys_exes.push_back(sysex);
1324 MidiRegionView::~MidiRegionView ()
1326 in_destructor = true;
1328 trackview.editor().verbose_cursor()->hide ();
1330 note_delete_connection.disconnect ();
1332 delete _list_editor;
1334 RegionViewGoingAway (this); /* EMIT_SIGNAL */
1336 if (_active_notes) {
1340 _selection_cleared_connection.disconnect ();
1343 clear_events (false);
1346 delete _note_diff_command;
1347 delete _step_edit_cursor;
1348 delete _temporary_note_group;
1352 MidiRegionView::region_resized (const PropertyChange& what_changed)
1354 RegionView::region_resized(what_changed);
1356 if (what_changed.contains (ARDOUR::Properties::position)) {
1357 set_duration(_region->length(), 0);
1358 if (_enable_display) {
1365 MidiRegionView::reset_width_dependent_items (double pixel_width)
1367 RegionView::reset_width_dependent_items(pixel_width);
1369 if (_enable_display) {
1373 for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
1374 if ((*x)->width() >= _pixel_width) {
1381 move_step_edit_cursor (_step_edit_cursor_position);
1382 set_step_edit_cursor_width (_step_edit_cursor_width);
1386 MidiRegionView::set_height (double height)
1388 static const double FUDGE = 2.0;
1389 const double old_height = _height;
1390 RegionView::set_height(height);
1391 _height = height - FUDGE;
1393 apply_note_range(midi_stream_view()->lowest_note(),
1394 midi_stream_view()->highest_note(),
1395 height != old_height + FUDGE);
1398 name_pixbuf->raise_to_top();
1401 for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
1402 (*x)->set_height (midi_stream_view()->contents_height());
1405 if (_step_edit_cursor) {
1406 _step_edit_cursor->property_y2() = midi_stream_view()->contents_height();
1411 /** Apply the current note range from the stream view
1412 * by repositioning/hiding notes as necessary
1415 MidiRegionView::apply_note_range (uint8_t min, uint8_t max, bool force)
1417 if (!_enable_display) {
1421 if (!force && _current_range_min == min && _current_range_max == max) {
1425 _current_range_min = min;
1426 _current_range_max = max;
1428 for (Events::const_iterator i = _events.begin(); i != _events.end(); ++i) {
1429 CanvasNoteEvent* event = *i;
1430 boost::shared_ptr<NoteType> note (event->note());
1432 if (note->note() < _current_range_min ||
1433 note->note() > _current_range_max) {
1439 if (CanvasNote* cnote = dynamic_cast<CanvasNote*>(event)) {
1441 const double y1 = midi_stream_view()->note_to_y(note->note());
1442 const double y2 = y1 + floor(midi_stream_view()->note_height());
1444 cnote->property_y1() = y1;
1445 cnote->property_y2() = y2;
1447 } else if (CanvasHit* chit = dynamic_cast<CanvasHit*>(event)) {
1449 const double diamond_size = update_hit (chit);
1451 chit->set_height (diamond_size);
1457 MidiRegionView::add_ghost (TimeAxisView& tv)
1461 double unit_position = _region->position () / samples_per_unit;
1462 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(&tv);
1463 MidiGhostRegion* ghost;
1465 if (mtv && mtv->midi_view()) {
1466 /* if ghost is inserted into midi track, use a dedicated midi ghost canvas group
1467 to allow having midi notes on top of note lines and waveforms.
1469 ghost = new MidiGhostRegion (*mtv->midi_view(), trackview, unit_position);
1471 ghost = new MidiGhostRegion (tv, trackview, unit_position);
1474 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1475 if ((note = dynamic_cast<CanvasNote*>(*i)) != 0) {
1476 ghost->add_note(note);
1480 ghost->set_height ();
1481 ghost->set_duration (_region->length() / samples_per_unit);
1482 ghosts.push_back (ghost);
1484 GhostRegion::CatchDeletion.connect (*this, invalidator (*this), boost::bind (&RegionView::remove_ghost, this, _1), gui_context());
1490 /** Begin tracking note state for successive calls to add_event
1493 MidiRegionView::begin_write()
1495 if (_active_notes) {
1496 delete[] _active_notes;
1498 _active_notes = new CanvasNote*[128];
1499 for (unsigned i = 0; i < 128; ++i) {
1500 _active_notes[i] = 0;
1505 /** Destroy note state for add_event
1508 MidiRegionView::end_write()
1510 delete[] _active_notes;
1512 _marked_for_selection.clear();
1513 _marked_for_velocity.clear();
1517 /** Resolve an active MIDI note (while recording).
1520 MidiRegionView::resolve_note(uint8_t note, double end_time)
1522 if (midi_view()->note_mode() != Sustained) {
1526 if (_active_notes && _active_notes[note]) {
1528 /* XXX is end_time really region-centric? I think so, because
1529 this is a new region that we're recording, so source zero is
1530 the same as region zero
1532 const framepos_t end_time_frames = region_beats_to_region_frames(end_time);
1534 _active_notes[note]->property_x2() = trackview.editor().frame_to_pixel(end_time_frames);
1535 _active_notes[note]->property_outline_what() = (guint32) 0xF; // all edges
1536 _active_notes[note] = 0;
1541 /** Extend active notes to rightmost edge of region (if length is changed)
1544 MidiRegionView::extend_active_notes()
1546 if (!_active_notes) {
1550 for (unsigned i=0; i < 128; ++i) {
1551 if (_active_notes[i]) {
1552 _active_notes[i]->property_x2() = trackview.editor().frame_to_pixel(_region->length());
1559 MidiRegionView::play_midi_note(boost::shared_ptr<NoteType> note)
1561 if (_no_sound_notes || !Config->get_sound_midi_notes()) {
1565 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1567 if (!route_ui || !route_ui->midi_track()) {
1571 NotePlayer* np = new NotePlayer (route_ui->midi_track ());
1575 /* NotePlayer deletes itself */
1579 MidiRegionView::start_playing_midi_note(boost::shared_ptr<NoteType> note)
1581 if (_no_sound_notes || !Config->get_sound_midi_notes()) {
1585 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1587 if (!route_ui || !route_ui->midi_track()) {
1591 delete _note_player;
1592 _note_player = new NotePlayer (route_ui->midi_track ());
1593 _note_player->add (note);
1594 _note_player->on ();
1598 MidiRegionView::start_playing_midi_chord (vector<boost::shared_ptr<NoteType> > notes)
1600 if (_no_sound_notes || !Config->get_sound_midi_notes()) {
1604 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1606 if (!route_ui || !route_ui->midi_track()) {
1610 delete _note_player;
1611 _note_player = new NotePlayer (route_ui->midi_track());
1613 for (vector<boost::shared_ptr<NoteType> >::iterator n = notes.begin(); n != notes.end(); ++n) {
1614 _note_player->add (*n);
1617 _note_player->on ();
1622 MidiRegionView::note_in_region_range (const boost::shared_ptr<NoteType> note, bool& visible) const
1624 const framepos_t note_start_frames = source_beats_to_region_frames (note->time());
1625 bool outside = (note_start_frames < 0) || (note_start_frames > _region->last_frame());
1627 visible = (note->note() >= midi_stream_view()->lowest_note()) &&
1628 (note->note() <= midi_stream_view()->highest_note());
1633 /** Update a canvas note's size from its model note.
1634 * @param ev Canvas note to update.
1635 * @param update_ghost_regions true to update the note in any ghost regions that we have, otherwise false.
1638 MidiRegionView::update_note (CanvasNote* ev, bool update_ghost_regions)
1640 boost::shared_ptr<NoteType> note = ev->note();
1641 const double x = trackview.editor().frame_to_pixel (source_beats_to_region_frames (note->time()));
1642 const double y1 = midi_stream_view()->note_to_y(note->note());
1644 ev->property_x1() = x;
1645 ev->property_y1() = y1;
1647 /* trim note display to not overlap the end of its region */
1649 if (note->length() > 0) {
1650 const framepos_t note_end_frames = min (source_beats_to_region_frames (note->end_time()), _region->length());
1651 ev->property_x2() = trackview.editor().frame_to_pixel (note_end_frames);
1653 ev->property_x2() = trackview.editor().frame_to_pixel (_region->length());
1656 ev->property_y2() = y1 + floor(midi_stream_view()->note_height());
1658 if (note->length() == 0) {
1659 if (_active_notes && note->note() < 128) {
1660 // If this note is already active there's a stuck note,
1661 // finish the old note rectangle
1662 if (_active_notes[note->note()]) {
1663 CanvasNote* const old_rect = _active_notes[note->note()];
1664 boost::shared_ptr<NoteType> old_note = old_rect->note();
1665 old_rect->property_x2() = x;
1666 old_rect->property_outline_what() = (guint32) 0xF;
1668 _active_notes[note->note()] = ev;
1670 /* outline all but right edge */
1671 ev->property_outline_what() = (guint32) (0x1 & 0x4 & 0x8);
1673 /* outline all edges */
1674 ev->property_outline_what() = (guint32) 0xF;
1677 if (update_ghost_regions) {
1678 for (std::vector<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
1679 MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*i);
1681 gr->update_note (ev);
1688 MidiRegionView::update_hit (CanvasHit* ev)
1690 boost::shared_ptr<NoteType> note = ev->note();
1692 const framepos_t note_start_frames = source_beats_to_region_frames(note->time());
1693 const double x = trackview.editor().frame_to_pixel(note_start_frames);
1694 const double diamond_size = midi_stream_view()->note_height() / 2.0;
1695 const double y = midi_stream_view()->note_to_y(note->note()) + ((diamond_size-2) / 4.0);
1699 return diamond_size;
1702 /** Add a MIDI note to the view (with length).
1704 * If in sustained mode, notes with length 0 will be considered active
1705 * notes, and resolve_note should be called when the corresponding note off
1706 * event arrives, to properly display the note.
1709 MidiRegionView::add_note(const boost::shared_ptr<NoteType> note, bool visible)
1711 CanvasNoteEvent* event = 0;
1713 //ArdourCanvas::Group* const group = (ArdourCanvas::Group*) get_canvas_group();
1715 if (midi_view()->note_mode() == Sustained) {
1717 CanvasNote* ev_rect = new CanvasNote(*this, *_note_group, note);
1719 update_note (ev_rect);
1723 MidiGhostRegion* gr;
1725 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
1726 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
1727 gr->add_note(ev_rect);
1731 } else if (midi_view()->note_mode() == Percussive) {
1733 const double diamond_size = midi_stream_view()->note_height() / 2.0;
1735 CanvasHit* ev_diamond = new CanvasHit (*this, *_note_group, diamond_size, note);
1737 update_hit (ev_diamond);
1746 if (_marked_for_selection.find(note) != _marked_for_selection.end()) {
1747 note_selected(event, true);
1750 if (_marked_for_velocity.find(note) != _marked_for_velocity.end()) {
1751 event->show_velocity();
1754 event->on_channel_selection_change(_last_channel_selection);
1755 _events.push_back(event);
1764 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1765 MidiStreamView* const view = mtv->midi_view();
1767 view->update_note_range (note->note());
1771 MidiRegionView::step_add_note (uint8_t channel, uint8_t number, uint8_t velocity,
1772 Evoral::MusicalTime pos, Evoral::MusicalTime len)
1774 boost::shared_ptr<NoteType> new_note (new NoteType (channel, pos, len, number, velocity));
1776 /* potentially extend region to hold new note */
1778 framepos_t end_frame = source_beats_to_absolute_frames (new_note->end_time());
1779 framepos_t region_end = _region->last_frame();
1781 if (end_frame > region_end) {
1782 _region->set_length (end_frame - _region->position());
1785 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1786 MidiStreamView* const view = mtv->midi_view();
1788 view->update_note_range(new_note->note());
1790 _marked_for_selection.clear ();
1793 start_note_diff_command (_("step add"));
1794 note_diff_add_note (new_note, true, false);
1797 // last_step_edit_note = new_note;
1801 MidiRegionView::step_sustain (Evoral::MusicalTime beats)
1803 change_note_lengths (false, false, beats, false, true);
1806 /** Add a new patch change flag to the canvas.
1807 * @param patch the patch change to add
1808 * @param the text to display in the flag
1809 * @param active_channel true to display the flag as on an active channel, false to grey it out for an inactive channel.
1812 MidiRegionView::add_canvas_patch_change (MidiModel::PatchChangePtr patch, const string& displaytext, bool active_channel)
1814 framecnt_t region_frames = source_beats_to_region_frames (patch->time());
1815 const double x = trackview.editor().frame_to_pixel (region_frames);
1817 double const height = midi_stream_view()->contents_height();
1819 boost::shared_ptr<CanvasPatchChange> patch_change = boost::shared_ptr<CanvasPatchChange>(
1820 new CanvasPatchChange(*this, *group,
1828 if (patch_change->width() < _pixel_width) {
1829 // Show unless patch change is beyond the region bounds
1830 if (region_frames < 0 || region_frames >= _region->length()) {
1831 patch_change->hide();
1833 patch_change->show();
1836 patch_change->hide ();
1839 _patch_changes.push_back (patch_change);
1842 MIDI::Name::PatchPrimaryKey
1843 MidiRegionView::patch_change_to_patch_key (MidiModel::PatchChangePtr p)
1845 return MIDI::Name::PatchPrimaryKey (p->program(), p->bank());
1848 /// Return true iff @p pc applies to the given time on the given channel.
1850 patch_applies (const ARDOUR::MidiModel::constPatchChangePtr pc, double time, uint8_t channel)
1852 return pc->time() <= time && pc->channel() == channel;
1856 MidiRegionView::get_patch_key_at (double time, uint8_t channel, MIDI::Name::PatchPrimaryKey& key) const
1858 // The earliest event not before time
1859 MidiModel::PatchChanges::iterator i = _model->patch_change_lower_bound (time);
1861 // Go backwards until we find the latest PC for this channel, or the start
1862 while (i != _model->patch_changes().begin() &&
1863 (i == _model->patch_changes().end() ||
1864 !patch_applies(*i, time, channel))) {
1868 if (i != _model->patch_changes().end() && patch_applies(*i, time, channel)) {
1869 key.bank_number = (*i)->bank();
1870 key.program_number = (*i)->program ();
1872 key.bank_number = key.program_number = 0;
1875 if (!key.is_sane()) {
1876 error << string_compose(_("insane MIDI patch key %1:%2"),
1877 key.bank_number, key.program_number) << endmsg;
1882 MidiRegionView::change_patch_change (CanvasPatchChange& pc, const MIDI::Name::PatchPrimaryKey& new_patch)
1884 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("alter patch change"));
1886 if (pc.patch()->program() != new_patch.program_number) {
1887 c->change_program (pc.patch (), new_patch.program_number);
1890 int const new_bank = new_patch.bank_number;
1891 if (pc.patch()->bank() != new_bank) {
1892 c->change_bank (pc.patch (), new_bank);
1895 _model->apply_command (*trackview.session(), c);
1897 _patch_changes.clear ();
1898 display_patch_changes ();
1902 MidiRegionView::change_patch_change (MidiModel::PatchChangePtr old_change, const Evoral::PatchChange<Evoral::MusicalTime> & new_change)
1904 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("alter patch change"));
1906 if (old_change->time() != new_change.time()) {
1907 c->change_time (old_change, new_change.time());
1910 if (old_change->channel() != new_change.channel()) {
1911 c->change_channel (old_change, new_change.channel());
1914 if (old_change->program() != new_change.program()) {
1915 c->change_program (old_change, new_change.program());
1918 if (old_change->bank() != new_change.bank()) {
1919 c->change_bank (old_change, new_change.bank());
1922 _model->apply_command (*trackview.session(), c);
1924 _patch_changes.clear ();
1925 display_patch_changes ();
1928 /** Add a patch change to the region.
1929 * @param t Time in frames relative to region position
1930 * @param patch Patch to add; time and channel are ignored (time is converted from t, and channel comes from
1931 * MidiTimeAxisView::get_channel_for_add())
1934 MidiRegionView::add_patch_change (framecnt_t t, Evoral::PatchChange<Evoral::MusicalTime> const & patch)
1936 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1938 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("add patch change"));
1939 c->add (MidiModel::PatchChangePtr (
1940 new Evoral::PatchChange<Evoral::MusicalTime> (
1941 absolute_frames_to_source_beats (_region->position() + t),
1942 mtv->get_channel_for_add(), patch.program(), patch.bank()
1947 _model->apply_command (*trackview.session(), c);
1949 _patch_changes.clear ();
1950 display_patch_changes ();
1954 MidiRegionView::move_patch_change (CanvasPatchChange& pc, Evoral::MusicalTime t)
1956 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("move patch change"));
1957 c->change_time (pc.patch (), t);
1958 _model->apply_command (*trackview.session(), c);
1960 _patch_changes.clear ();
1961 display_patch_changes ();
1965 MidiRegionView::delete_patch_change (CanvasPatchChange* pc)
1967 MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("delete patch change"));
1968 c->remove (pc->patch ());
1969 _model->apply_command (*trackview.session(), c);
1971 _patch_changes.clear ();
1972 display_patch_changes ();
1976 MidiRegionView::previous_patch (CanvasPatchChange& patch)
1978 if (patch.patch()->program() < 127) {
1979 MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch());
1980 key.program_number++;
1981 change_patch_change (patch, key);
1986 MidiRegionView::next_patch (CanvasPatchChange& patch)
1988 if (patch.patch()->program() > 0) {
1989 MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch());
1990 key.program_number--;
1991 change_patch_change (patch, key);
1996 MidiRegionView::next_bank (CanvasPatchChange& patch)
1998 if (patch.patch()->program() < 127) {
1999 MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch());
2000 if (key.bank_number > 0) {
2002 change_patch_change (patch, key);
2008 MidiRegionView::previous_bank (CanvasPatchChange& patch)
2010 if (patch.patch()->program() > 0) {
2011 MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch());
2012 if (key.bank_number < 127) {
2014 change_patch_change (patch, key);
2020 MidiRegionView::maybe_remove_deleted_note_from_selection (CanvasNoteEvent* cne)
2022 if (_selection.empty()) {
2026 _selection.erase (cne);
2030 MidiRegionView::delete_selection()
2032 if (_selection.empty()) {
2036 start_note_diff_command (_("delete selection"));
2038 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2039 if ((*i)->selected()) {
2040 _note_diff_command->remove((*i)->note());
2050 MidiRegionView::delete_note (boost::shared_ptr<NoteType> n)
2052 start_note_diff_command (_("delete note"));
2053 _note_diff_command->remove (n);
2056 trackview.editor().verbose_cursor()->hide ();
2060 MidiRegionView::clear_selection_except (ArdourCanvas::CanvasNoteEvent* ev, bool signal)
2062 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2064 Selection::iterator tmp = i;
2067 (*i)->set_selected (false);
2068 (*i)->hide_velocity ();
2069 _selection.erase (i);
2077 /* this does not change the status of this regionview w.r.t the editor
2082 SelectionCleared (this); /* EMIT SIGNAL */
2087 MidiRegionView::unique_select(ArdourCanvas::CanvasNoteEvent* ev)
2089 clear_selection_except (ev);
2091 /* don't bother with checking to see if we should remove this
2092 regionview from the editor selection, since we're about to add
2093 another note, and thus put/keep this regionview in the editor
2097 if (!ev->selected()) {
2098 add_to_selection (ev);
2103 MidiRegionView::select_all_notes ()
2107 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2108 add_to_selection (*i);
2113 MidiRegionView::select_range (framepos_t start, framepos_t end)
2117 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2118 framepos_t t = source_beats_to_absolute_frames((*i)->note()->time());
2119 if (t >= start && t <= end) {
2120 add_to_selection (*i);
2126 MidiRegionView::invert_selection ()
2128 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2129 if ((*i)->selected()) {
2130 remove_from_selection(*i);
2132 add_to_selection (*i);
2138 MidiRegionView::select_matching_notes (uint8_t notenum, uint16_t channel_mask, bool add, bool extend)
2140 uint8_t low_note = 127;
2141 uint8_t high_note = 0;
2142 MidiModel::Notes& notes (_model->notes());
2143 _optimization_iterator = _events.begin();
2149 if (extend && _selection.empty()) {
2155 /* scan existing selection to get note range */
2157 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2158 if ((*i)->note()->note() < low_note) {
2159 low_note = (*i)->note()->note();
2161 if ((*i)->note()->note() > high_note) {
2162 high_note = (*i)->note()->note();
2166 low_note = min (low_note, notenum);
2167 high_note = max (high_note, notenum);
2170 _no_sound_notes = true;
2172 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
2174 boost::shared_ptr<NoteType> note (*n);
2175 CanvasNoteEvent* cne;
2176 bool select = false;
2178 if (((1 << note->channel()) & channel_mask) != 0) {
2180 if ((note->note() >= low_note && note->note() <= high_note)) {
2183 } else if (note->note() == notenum) {
2189 if ((cne = find_canvas_note (note)) != 0) {
2190 // extend is false because we've taken care of it,
2191 // since it extends by time range, not pitch.
2192 note_selected (cne, add, false);
2196 add = true; // we need to add all remaining matching notes, even if the passed in value was false (for "set")
2200 _no_sound_notes = false;
2204 MidiRegionView::toggle_matching_notes (uint8_t notenum, uint16_t channel_mask)
2206 MidiModel::Notes& notes (_model->notes());
2207 _optimization_iterator = _events.begin();
2209 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
2211 boost::shared_ptr<NoteType> note (*n);
2212 CanvasNoteEvent* cne;
2214 if (note->note() == notenum && (((0x0001 << note->channel()) & channel_mask) != 0)) {
2215 if ((cne = find_canvas_note (note)) != 0) {
2216 if (cne->selected()) {
2217 note_deselected (cne);
2219 note_selected (cne, true, false);
2227 MidiRegionView::note_selected (ArdourCanvas::CanvasNoteEvent* ev, bool add, bool extend)
2230 clear_selection_except (ev);
2231 if (!_selection.empty()) {
2232 PublicEditor& editor (trackview.editor());
2233 editor.get_selection().add (this);
2239 if (!ev->selected()) {
2240 add_to_selection (ev);
2244 /* find end of latest note selected, select all between that and the start of "ev" */
2246 Evoral::MusicalTime earliest = Evoral::MaxMusicalTime;
2247 Evoral::MusicalTime latest = 0;
2249 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2250 if ((*i)->note()->end_time() > latest) {
2251 latest = (*i)->note()->end_time();
2253 if ((*i)->note()->time() < earliest) {
2254 earliest = (*i)->note()->time();
2258 if (ev->note()->end_time() > latest) {
2259 latest = ev->note()->end_time();
2262 if (ev->note()->time() < earliest) {
2263 earliest = ev->note()->time();
2266 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2268 /* find notes entirely within OR spanning the earliest..latest range */
2270 if (((*i)->note()->time() >= earliest && (*i)->note()->end_time() <= latest) ||
2271 ((*i)->note()->time() <= earliest && (*i)->note()->end_time() >= latest)) {
2272 add_to_selection (*i);
2280 MidiRegionView::note_deselected(ArdourCanvas::CanvasNoteEvent* ev)
2282 remove_from_selection (ev);
2286 MidiRegionView::update_drag_selection(double x1, double x2, double y1, double y2, bool extend)
2288 // TODO: Make this faster by storing the last updated selection rect, and only
2289 // adjusting things that are in the area that appears/disappeared.
2290 // We probably need a tree to be able to find events in O(log(n)) time.
2292 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2293 if ((*i)->x1() < x2 && (*i)->x2() > x1 && (*i)->y1() < y2 && (*i)->y2() > y1) {
2294 // Rectangles intersect
2295 if (!(*i)->selected()) {
2296 add_to_selection (*i);
2298 } else if ((*i)->selected() && !extend) {
2299 // Rectangles do not intersect
2300 remove_from_selection (*i);
2306 MidiRegionView::update_vertical_drag_selection (double y1, double y2, bool extend)
2312 // TODO: Make this faster by storing the last updated selection rect, and only
2313 // adjusting things that are in the area that appears/disappeared.
2314 // We probably need a tree to be able to find events in O(log(n)) time.
2316 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2317 if (((*i)->y1() >= y1 && (*i)->y1() <= y2)) {
2318 // within y- (note-) range
2319 if (!(*i)->selected()) {
2320 add_to_selection (*i);
2322 } else if ((*i)->selected() && !extend) {
2323 remove_from_selection (*i);
2329 MidiRegionView::remove_from_selection (CanvasNoteEvent* ev)
2331 Selection::iterator i = _selection.find (ev);
2333 if (i != _selection.end()) {
2334 _selection.erase (i);
2337 ev->set_selected (false);
2338 ev->hide_velocity ();
2340 if (_selection.empty()) {
2341 PublicEditor& editor (trackview.editor());
2342 editor.get_selection().remove (this);
2347 MidiRegionView::add_to_selection (CanvasNoteEvent* ev)
2349 bool add_mrv_selection = false;
2351 if (_selection.empty()) {
2352 add_mrv_selection = true;
2355 if (_selection.insert (ev).second) {
2356 ev->set_selected (true);
2357 start_playing_midi_note ((ev)->note());
2360 if (add_mrv_selection) {
2361 PublicEditor& editor (trackview.editor());
2362 editor.get_selection().add (this);
2367 MidiRegionView::move_selection(double dx, double dy, double cumulative_dy)
2369 typedef vector<boost::shared_ptr<NoteType> > PossibleChord;
2370 PossibleChord to_play;
2371 Evoral::MusicalTime earliest = Evoral::MaxMusicalTime;
2373 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2374 if ((*i)->note()->time() < earliest) {
2375 earliest = (*i)->note()->time();
2379 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2380 if (Evoral::musical_time_equal ((*i)->note()->time(), earliest)) {
2381 to_play.push_back ((*i)->note());
2383 (*i)->move_event(dx, dy);
2386 if (dy && !_selection.empty() && !_no_sound_notes && Config->get_sound_midi_notes()) {
2388 if (to_play.size() > 1) {
2390 PossibleChord shifted;
2392 for (PossibleChord::iterator n = to_play.begin(); n != to_play.end(); ++n) {
2393 boost::shared_ptr<NoteType> moved_note (new NoteType (**n));
2394 moved_note->set_note (moved_note->note() + cumulative_dy);
2395 shifted.push_back (moved_note);
2398 start_playing_midi_chord (shifted);
2400 } else if (!to_play.empty()) {
2402 boost::shared_ptr<NoteType> moved_note (new NoteType (*to_play.front()));
2403 moved_note->set_note (moved_note->note() + cumulative_dy);
2404 start_playing_midi_note (moved_note);
2410 MidiRegionView::note_dropped(CanvasNoteEvent *, frameoffset_t dt, int8_t dnote)
2412 uint8_t lowest_note_in_selection = 127;
2413 uint8_t highest_note_in_selection = 0;
2414 uint8_t highest_note_difference = 0;
2416 // find highest and lowest notes first
2418 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2419 uint8_t pitch = (*i)->note()->note();
2420 lowest_note_in_selection = std::min(lowest_note_in_selection, pitch);
2421 highest_note_in_selection = std::max(highest_note_in_selection, pitch);
2425 cerr << "dnote: " << (int) dnote << endl;
2426 cerr << "lowest note (streamview): " << int(midi_stream_view()->lowest_note())
2427 << " highest note (streamview): " << int(midi_stream_view()->highest_note()) << endl;
2428 cerr << "lowest note (selection): " << int(lowest_note_in_selection) << " highest note(selection): "
2429 << int(highest_note_in_selection) << endl;
2430 cerr << "selection size: " << _selection.size() << endl;
2431 cerr << "Highest note in selection: " << (int) highest_note_in_selection << endl;
2434 // Make sure the note pitch does not exceed the MIDI standard range
2435 if (highest_note_in_selection + dnote > 127) {
2436 highest_note_difference = highest_note_in_selection - 127;
2439 start_note_diff_command (_("move notes"));
2441 for (Selection::iterator i = _selection.begin(); i != _selection.end() ; ++i) {
2443 framepos_t new_frames = source_beats_to_absolute_frames ((*i)->note()->time()) + dt;
2444 Evoral::MusicalTime new_time = absolute_frames_to_source_beats (new_frames);
2450 note_diff_add_change (*i, MidiModel::NoteDiffCommand::StartTime, new_time);
2452 uint8_t original_pitch = (*i)->note()->note();
2453 uint8_t new_pitch = original_pitch + dnote - highest_note_difference;
2455 // keep notes in standard midi range
2456 clamp_to_0_127(new_pitch);
2458 lowest_note_in_selection = std::min(lowest_note_in_selection, new_pitch);
2459 highest_note_in_selection = std::max(highest_note_in_selection, new_pitch);
2461 note_diff_add_change (*i, MidiModel::NoteDiffCommand::NoteNumber, new_pitch);
2466 // care about notes being moved beyond the upper/lower bounds on the canvas
2467 if (lowest_note_in_selection < midi_stream_view()->lowest_note() ||
2468 highest_note_in_selection > midi_stream_view()->highest_note()) {
2469 midi_stream_view()->set_note_range(MidiStreamView::ContentsRange);
2473 /** @param x Pixel relative to the region position.
2474 * @return Snapped frame relative to the region position.
2477 MidiRegionView::snap_pixel_to_frame(double x)
2479 PublicEditor& editor (trackview.editor());
2480 return snap_frame_to_frame (editor.pixel_to_frame (x));
2483 /** @param x Pixel relative to the region position.
2484 * @return Snapped pixel relative to the region position.
2487 MidiRegionView::snap_to_pixel(double x)
2489 return (double) trackview.editor().frame_to_pixel(snap_pixel_to_frame(x));
2493 MidiRegionView::get_position_pixels()
2495 framepos_t region_frame = get_position();
2496 return trackview.editor().frame_to_pixel(region_frame);
2500 MidiRegionView::get_end_position_pixels()
2502 framepos_t frame = get_position() + get_duration ();
2503 return trackview.editor().frame_to_pixel(frame);
2507 MidiRegionView::source_beats_to_absolute_frames(double beats) const
2509 /* the time converter will return the frame corresponding to `beats'
2510 relative to the start of the source. The start of the source
2511 is an implied position given by region->position - region->start
2513 const framepos_t source_start = _region->position() - _region->start();
2514 return source_start + _source_relative_time_converter.to (beats);
2518 MidiRegionView::absolute_frames_to_source_beats(framepos_t frames) const
2520 /* the `frames' argument needs to be converted into a frame count
2521 relative to the start of the source before being passed in to the
2524 const framepos_t source_start = _region->position() - _region->start();
2525 return _source_relative_time_converter.from (frames - source_start);
2529 MidiRegionView::region_beats_to_region_frames(double beats) const
2531 return _region_relative_time_converter.to(beats);
2535 MidiRegionView::region_frames_to_region_beats(framepos_t frames) const
2537 return _region_relative_time_converter.from(frames);
2541 MidiRegionView::begin_resizing (bool /*at_front*/)
2543 _resize_data.clear();
2545 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2546 CanvasNote *note = dynamic_cast<CanvasNote *> (*i);
2548 // only insert CanvasNotes into the map
2550 NoteResizeData *resize_data = new NoteResizeData();
2551 resize_data->canvas_note = note;
2553 // create a new SimpleRect from the note which will be the resize preview
2554 SimpleRect *resize_rect = new SimpleRect(
2555 *_note_group, note->x1(), note->y1(), note->x2(), note->y2());
2557 // calculate the colors: get the color settings
2558 uint32_t fill_color = UINT_RGBA_CHANGE_A(
2559 ARDOUR_UI::config()->canvasvar_MidiNoteSelected.get(),
2562 // make the resize preview notes more transparent and bright
2563 fill_color = UINT_INTERPOLATE(fill_color, 0xFFFFFF40, 0.5);
2565 // calculate color based on note velocity
2566 resize_rect->property_fill_color_rgba() = UINT_INTERPOLATE(
2567 CanvasNoteEvent::meter_style_fill_color(note->note()->velocity(), note->selected()),
2571 resize_rect->property_outline_color_rgba() = CanvasNoteEvent::calculate_outline(
2572 ARDOUR_UI::config()->canvasvar_MidiNoteSelected.get());
2574 resize_data->resize_rect = resize_rect;
2575 _resize_data.push_back(resize_data);
2580 /** Update resizing notes while user drags.
2581 * @param primary `primary' note for the drag; ie the one that is used as the reference in non-relative mode.
2582 * @param at_front which end of the note (true == note on, false == note off)
2583 * @param delta_x change in mouse position since the start of the drag
2584 * @param relative true if relative resizing is taking place, false if absolute resizing. This only makes
2585 * a difference when multiple notes are being resized; in relative mode, each note's length is changed by the
2586 * amount of the drag. In non-relative mode, all selected notes are set to have the same start or end point
2587 * as the \a primary note.
2590 MidiRegionView::update_resizing (ArdourCanvas::CanvasNoteEvent* primary, bool at_front, double delta_x, bool relative)
2592 bool cursor_set = false;
2594 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2595 SimpleRect* resize_rect = (*i)->resize_rect;
2596 CanvasNote* canvas_note = (*i)->canvas_note;
2601 current_x = canvas_note->x1() + delta_x;
2603 current_x = primary->x1() + delta_x;
2607 current_x = canvas_note->x2() + delta_x;
2609 current_x = primary->x2() + delta_x;
2614 resize_rect->property_x1() = snap_to_pixel(current_x);
2615 resize_rect->property_x2() = canvas_note->x2();
2617 resize_rect->property_x2() = snap_to_pixel(current_x);
2618 resize_rect->property_x1() = canvas_note->x1();
2624 beats = snap_pixel_to_frame (current_x);
2625 beats = region_frames_to_region_beats (beats);
2630 if (beats < canvas_note->note()->end_time()) {
2631 len = canvas_note->note()->time() - beats;
2632 len += canvas_note->note()->length();
2637 if (beats >= canvas_note->note()->time()) {
2638 len = beats - canvas_note->note()->time();
2645 snprintf (buf, sizeof (buf), "%.3g beats", len);
2646 show_verbose_cursor (buf, 0, 0);
2655 /** Finish resizing notes when the user releases the mouse button.
2656 * Parameters the same as for \a update_resizing().
2659 MidiRegionView::commit_resizing (ArdourCanvas::CanvasNoteEvent* primary, bool at_front, double delta_x, bool relative)
2661 start_note_diff_command (_("resize notes"));
2663 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2664 CanvasNote* canvas_note = (*i)->canvas_note;
2665 SimpleRect* resize_rect = (*i)->resize_rect;
2667 /* Get the new x position for this resize, which is in pixels relative
2668 * to the region position.
2675 current_x = canvas_note->x1() + delta_x;
2677 current_x = primary->x1() + delta_x;
2681 current_x = canvas_note->x2() + delta_x;
2683 current_x = primary->x2() + delta_x;
2687 /* Convert that to a frame within the source */
2688 current_x = snap_pixel_to_frame (current_x) + _region->start ();
2690 /* and then to beats */
2691 current_x = region_frames_to_region_beats (current_x);
2693 if (at_front && current_x < canvas_note->note()->end_time()) {
2694 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::StartTime, current_x);
2696 double len = canvas_note->note()->time() - current_x;
2697 len += canvas_note->note()->length();
2700 /* XXX convert to beats */
2701 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
2706 double len = current_x - canvas_note->note()->time();
2709 /* XXX convert to beats */
2710 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
2718 _resize_data.clear();
2723 MidiRegionView::abort_resizing ()
2725 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2726 delete (*i)->resize_rect;
2730 _resize_data.clear ();
2734 MidiRegionView::change_note_velocity(CanvasNoteEvent* event, int8_t velocity, bool relative)
2736 uint8_t new_velocity;
2739 new_velocity = event->note()->velocity() + velocity;
2740 clamp_to_0_127(new_velocity);
2742 new_velocity = velocity;
2745 event->set_selected (event->selected()); // change color
2747 note_diff_add_change (event, MidiModel::NoteDiffCommand::Velocity, new_velocity);
2751 MidiRegionView::change_note_note (CanvasNoteEvent* event, int8_t note, bool relative)
2756 new_note = event->note()->note() + note;
2761 clamp_to_0_127 (new_note);
2762 note_diff_add_change (event, MidiModel::NoteDiffCommand::NoteNumber, new_note);
2766 MidiRegionView::trim_note (CanvasNoteEvent* event, Evoral::MusicalTime front_delta, Evoral::MusicalTime end_delta)
2768 bool change_start = false;
2769 bool change_length = false;
2770 Evoral::MusicalTime new_start = 0;
2771 Evoral::MusicalTime new_length = 0;
2773 /* NOTE: the semantics of the two delta arguments are slightly subtle:
2775 front_delta: if positive - move the start of the note later in time (shortening it)
2776 if negative - move the start of the note earlier in time (lengthening it)
2778 end_delta: if positive - move the end of the note later in time (lengthening it)
2779 if negative - move the end of the note earlier in time (shortening it)
2783 if (front_delta < 0) {
2785 if (event->note()->time() < -front_delta) {
2788 new_start = event->note()->time() + front_delta; // moves earlier
2791 /* start moved toward zero, so move the end point out to where it used to be.
2792 Note that front_delta is negative, so this increases the length.
2795 new_length = event->note()->length() - front_delta;
2796 change_start = true;
2797 change_length = true;
2801 Evoral::MusicalTime new_pos = event->note()->time() + front_delta;
2803 if (new_pos < event->note()->end_time()) {
2804 new_start = event->note()->time() + front_delta;
2805 /* start moved toward the end, so move the end point back to where it used to be */
2806 new_length = event->note()->length() - front_delta;
2807 change_start = true;
2808 change_length = true;
2815 bool can_change = true;
2816 if (end_delta < 0) {
2817 if (event->note()->length() < -end_delta) {
2823 new_length = event->note()->length() + end_delta;
2824 change_length = true;
2829 note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_start);
2832 if (change_length) {
2833 note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, new_length);
2838 MidiRegionView::change_note_channel (CanvasNoteEvent* event, int8_t chn, bool relative)
2840 uint8_t new_channel;
2844 if (event->note()->channel() < -chn) {
2847 new_channel = event->note()->channel() + chn;
2850 new_channel = event->note()->channel() + chn;
2853 new_channel = (uint8_t) chn;
2856 note_diff_add_change (event, MidiModel::NoteDiffCommand::Channel, new_channel);
2860 MidiRegionView::change_note_time (CanvasNoteEvent* event, Evoral::MusicalTime delta, bool relative)
2862 Evoral::MusicalTime new_time;
2866 if (event->note()->time() < -delta) {
2869 new_time = event->note()->time() + delta;
2872 new_time = event->note()->time() + delta;
2878 note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_time);
2882 MidiRegionView::change_note_length (CanvasNoteEvent* event, Evoral::MusicalTime t)
2884 note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, t);
2888 MidiRegionView::change_velocities (bool up, bool fine, bool allow_smush, bool all_together)
2893 if (_selection.empty()) {
2908 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2909 if ((*i)->note()->velocity() < -delta || (*i)->note()->velocity() + delta > 127) {
2915 start_note_diff_command (_("change velocities"));
2917 for (Selection::iterator i = _selection.begin(); i != _selection.end();) {
2918 Selection::iterator next = i;
2922 if (i == _selection.begin()) {
2923 change_note_velocity (*i, delta, true);
2924 value = (*i)->note()->velocity() + delta;
2926 change_note_velocity (*i, value, false);
2930 change_note_velocity (*i, delta, true);
2939 if (!_selection.empty()) {
2941 snprintf (buf, sizeof (buf), "Vel %d",
2942 (int) (*_selection.begin())->note()->velocity());
2943 show_verbose_cursor (buf, 10, 10);
2949 MidiRegionView::transpose (bool up, bool fine, bool allow_smush)
2951 if (_selection.empty()) {
2968 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2970 if ((int8_t) (*i)->note()->note() + delta <= 0) {
2974 if ((int8_t) (*i)->note()->note() + delta > 127) {
2981 start_note_diff_command (_("transpose"));
2983 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2984 Selection::iterator next = i;
2986 change_note_note (*i, delta, true);
2994 MidiRegionView::change_note_lengths (bool fine, bool shorter, Evoral::MusicalTime delta, bool start, bool end)
3000 /* grab the current grid distance */
3002 delta = trackview.editor().get_grid_type_as_beats (success, _region->position());
3004 /* XXX cannot get grid type as beats ... should always be possible ... FIX ME */
3005 error << string_compose (_("programming error: %1"), "Grid type not available as beats - TO BE FIXED") << endmsg;
3015 start_note_diff_command (_("change note lengths"));
3017 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3018 Selection::iterator next = i;
3021 /* note the negation of the delta for start */
3023 trim_note (*i, (start ? -delta : 0), (end ? delta : 0));
3032 MidiRegionView::nudge_notes (bool forward)
3034 if (_selection.empty()) {
3038 /* pick a note as the point along the timeline to get the nudge distance.
3039 its not necessarily the earliest note, so we may want to pull the notes out
3040 into a vector and sort before using the first one.
3043 framepos_t ref_point = source_beats_to_absolute_frames ((*(_selection.begin()))->note()->time());
3045 framecnt_t distance;
3047 if (trackview.editor().snap_mode() == Editing::SnapOff) {
3049 /* grid is off - use nudge distance */
3051 distance = trackview.editor().get_nudge_distance (ref_point, unused);
3057 framepos_t next_pos = ref_point;
3060 if (max_framepos - 1 < next_pos) {
3064 if (next_pos == 0) {
3070 trackview.editor().snap_to (next_pos, (forward ? 1 : -1), false);
3071 distance = ref_point - next_pos;
3074 if (distance == 0) {
3078 Evoral::MusicalTime delta = region_frames_to_region_beats (fabs (distance));
3084 start_note_diff_command (_("nudge"));
3086 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3087 Selection::iterator next = i;
3089 change_note_time (*i, delta, true);
3097 MidiRegionView::change_channel(uint8_t channel)
3099 start_note_diff_command(_("change channel"));
3100 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3101 note_diff_add_change (*i, MidiModel::NoteDiffCommand::Channel, channel);
3109 MidiRegionView::note_entered(ArdourCanvas::CanvasNoteEvent* ev)
3111 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3113 pre_enter_cursor = editor->get_canvas_cursor ();
3115 if (_mouse_state == SelectTouchDragging) {
3116 note_selected (ev, true);
3119 show_verbose_cursor (ev->note ());
3123 MidiRegionView::note_left (ArdourCanvas::CanvasNoteEvent*)
3125 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3127 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3128 (*i)->hide_velocity ();
3131 editor->verbose_cursor()->hide ();
3133 if (pre_enter_cursor) {
3134 editor->set_canvas_cursor (pre_enter_cursor);
3135 pre_enter_cursor = 0;
3140 MidiRegionView::patch_entered (ArdourCanvas::CanvasPatchChange* p)
3143 /* XXX should get patch name if we can */
3144 s << _("Bank ") << (p->patch()->bank() + MIDI_BP_ZERO) << '\n'
3145 << _("Program ") << ((int) p->patch()->program()) + MIDI_BP_ZERO << '\n'
3146 << _("Channel ") << ((int) p->patch()->channel() + 1);
3147 show_verbose_cursor (s.str(), 10, 20);
3152 MidiRegionView::patch_left (ArdourCanvas::CanvasPatchChange *)
3154 trackview.editor().verbose_cursor()->hide ();
3155 /* focus will transfer back via the enter-notify event sent to this
3161 MidiRegionView::sysex_entered (ArdourCanvas::CanvasSysEx* p)
3165 show_verbose_cursor (s.str(), 10, 20);
3170 MidiRegionView::sysex_left (ArdourCanvas::CanvasSysEx *)
3172 trackview.editor().verbose_cursor()->hide ();
3173 /* focus will transfer back via the enter-notify event sent to this
3179 MidiRegionView::note_mouse_position (float x_fraction, float /*y_fraction*/, bool can_set_cursor)
3181 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3182 Editing::MouseMode mm = editor->current_mouse_mode();
3183 bool trimmable = (mm == MouseObject || mm == MouseTimeFX || mm == MouseDraw);
3185 if (trimmable && x_fraction > 0.0 && x_fraction < 0.2) {
3186 editor->set_canvas_cursor (editor->cursors()->left_side_trim);
3187 } else if (trimmable && x_fraction >= 0.8 && x_fraction < 1.0) {
3188 editor->set_canvas_cursor (editor->cursors()->right_side_trim);
3190 if (pre_enter_cursor && can_set_cursor) {
3191 editor->set_canvas_cursor (pre_enter_cursor);
3197 MidiRegionView::set_frame_color()
3201 TimeAxisViewItem::set_frame_color ();
3208 f = ARDOUR_UI::config()->canvasvar_SelectedFrameBase.get();
3209 } else if (high_enough_for_name) {
3210 f= ARDOUR_UI::config()->canvasvar_MidiFrameBase.get();
3215 if (!rect_visible) {
3216 f = UINT_RGBA_CHANGE_A (f, 0);
3219 frame->property_fill_color_rgba() = f;
3223 MidiRegionView::midi_channel_mode_changed(ChannelMode mode, uint16_t mask)
3225 if (mode == ForceChannel) {
3226 mask = 0xFFFF; // Show all notes as active (below)
3229 // Update notes for selection
3230 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3231 (*i)->on_channel_selection_change(mask);
3234 _last_channel_selection = mask;
3235 _last_channel_mode = mode;
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() (CanvasNoteEvent* a, CanvasNoteEvent* 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->channel_selector().get_selected_channels ();
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->channel_selector().get_selected_channels ();
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->w2i (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 NoEventCanvasNote (*this, *_note_group, g);
3532 _ghost_note->property_outline_color_rgba() = 0x000000aa;
3533 update_ghost_note (x, y);
3534 _ghost_note->show ();
3539 show_verbose_cursor (_ghost_note->note ());
3543 MidiRegionView::snap_changed ()
3549 create_ghost_note (_last_ghost_x, _last_ghost_y);
3553 MidiRegionView::drop_down_keys ()
3555 _mouse_state = None;
3559 MidiRegionView::maybe_select_by_position (GdkEventButton* ev, double /*x*/, double y)
3561 double note = midi_stream_view()->y_to_note(y);
3563 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3565 uint16_t chn_mask = mtv->channel_selector().get_selected_channels();
3567 if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
3568 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchGreaterThanOrEqual, (uint8_t) floor (note), chn_mask);
3569 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
3570 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchLessThanOrEqual, (uint8_t) floor (note), chn_mask);
3575 bool add_mrv_selection = false;
3577 if (_selection.empty()) {
3578 add_mrv_selection = true;
3581 for (Events::iterator i = e.begin(); i != e.end(); ++i) {
3582 if (_selection.insert (*i).second) {
3583 (*i)->set_selected (true);
3587 if (add_mrv_selection) {
3588 PublicEditor& editor (trackview.editor());
3589 editor.get_selection().add (this);
3594 MidiRegionView::color_handler ()
3596 RegionView::color_handler ();
3598 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3599 (*i)->set_selected ((*i)->selected()); // will change color
3602 /* XXX probably more to do here */
3606 MidiRegionView::enable_display (bool yn)
3608 RegionView::enable_display (yn);
3615 MidiRegionView::show_step_edit_cursor (Evoral::MusicalTime pos)
3617 if (_step_edit_cursor == 0) {
3618 ArdourCanvas::Group* const group = (ArdourCanvas::Group*)get_canvas_group();
3620 _step_edit_cursor = new ArdourCanvas::SimpleRect (*group);
3621 _step_edit_cursor->property_y1() = 0;
3622 _step_edit_cursor->property_y2() = midi_stream_view()->contents_height();
3623 _step_edit_cursor->property_fill_color_rgba() = RGBA_TO_UINT (45,0,0,90);
3624 _step_edit_cursor->property_outline_color_rgba() = RGBA_TO_UINT (85,0,0,90);
3627 move_step_edit_cursor (pos);
3628 _step_edit_cursor->show ();
3632 MidiRegionView::move_step_edit_cursor (Evoral::MusicalTime pos)
3634 _step_edit_cursor_position = pos;
3636 if (_step_edit_cursor) {
3637 double pixel = trackview.editor().frame_to_pixel (region_beats_to_region_frames (pos));
3638 _step_edit_cursor->property_x1() = pixel;
3639 set_step_edit_cursor_width (_step_edit_cursor_width);
3644 MidiRegionView::hide_step_edit_cursor ()
3646 if (_step_edit_cursor) {
3647 _step_edit_cursor->hide ();
3652 MidiRegionView::set_step_edit_cursor_width (Evoral::MusicalTime beats)
3654 _step_edit_cursor_width = beats;
3656 if (_step_edit_cursor) {
3657 _step_edit_cursor->property_x2() = _step_edit_cursor->property_x1() + trackview.editor().frame_to_pixel (region_beats_to_region_frames (beats));
3661 /** Called when a diskstream on our track has received some data. Update the view, if applicable.
3662 * @param w Source that the data will end up in.
3665 MidiRegionView::data_recorded (boost::weak_ptr<MidiSource> w)
3667 if (!_active_notes) {
3668 /* we aren't actively being recorded to */
3672 boost::shared_ptr<MidiSource> src = w.lock ();
3673 if (!src || src != midi_region()->midi_source()) {
3674 /* recorded data was not destined for our source */
3678 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (&trackview);
3680 boost::shared_ptr<MidiBuffer> buf = mtv->midi_track()->get_gui_feed_buffer ();
3682 BeatsFramesConverter converter (trackview.session()->tempo_map(), mtv->midi_track()->get_capture_start_frame (0));
3684 framepos_t back = max_framepos;
3686 for (MidiBuffer::iterator i = buf->begin(); i != buf->end(); ++i) {
3687 Evoral::MIDIEvent<MidiBuffer::TimeType> const ev (*i, false);
3689 if (ev.is_channel_event()) {
3690 if (_last_channel_mode == FilterChannels) {
3691 if (((uint16_t(1) << ev.channel()) & _last_channel_selection) == 0) {
3697 /* ev.time() is in session frames, so (ev.time() - converter.origin_b()) is
3698 frames from the start of the source, and so time_beats is in terms of the
3702 Evoral::MusicalTime const time_beats = converter.from (ev.time () - converter.origin_b ());
3704 if (ev.type() == MIDI_CMD_NOTE_ON) {
3705 boost::shared_ptr<NoteType> note (
3706 new NoteType (ev.channel(), time_beats, 0, ev.note(), ev.velocity()));
3708 add_note (note, true);
3710 /* fix up our note range */
3711 if (ev.note() < _current_range_min) {
3712 midi_stream_view()->apply_note_range (ev.note(), _current_range_max, true);
3713 } else if (ev.note() > _current_range_max) {
3714 midi_stream_view()->apply_note_range (_current_range_min, ev.note(), true);
3717 } else if (ev.type() == MIDI_CMD_NOTE_OFF) {
3718 resolve_note (ev.note (), time_beats);
3724 midi_stream_view()->check_record_layers (region(), back);
3728 MidiRegionView::trim_front_starting ()
3730 /* Reparent the note group to the region view's parent, so that it doesn't change
3731 when the region view is trimmed.
3733 _temporary_note_group = new ArdourCanvas::Group (*group->property_parent ());
3734 _temporary_note_group->move (group->property_x(), group->property_y());
3735 _note_group->reparent (*_temporary_note_group);
3739 MidiRegionView::trim_front_ending ()
3741 _note_group->reparent (*group);
3742 delete _temporary_note_group;
3743 _temporary_note_group = 0;
3745 if (_region->start() < 0) {
3746 /* Trim drag made start time -ve; fix this */
3747 midi_region()->fix_negative_start ();
3752 MidiRegionView::edit_patch_change (ArdourCanvas::CanvasPatchChange* pc)
3754 PatchChangeDialog d (&_source_relative_time_converter, trackview.session(), *pc->patch (), instrument_info(), Gtk::Stock::APPLY, true);
3756 d.set_position (Gtk::WIN_POS_MOUSE);
3758 int response = d.run();
3761 case Gtk::RESPONSE_ACCEPT:
3763 case Gtk::RESPONSE_REJECT:
3764 delete_patch_change (pc);
3770 change_patch_change (pc->patch(), d.patch ());
3774 MidiRegionView::delete_sysex (CanvasSysEx* sysex)
3776 MidiModel::SysExDiffCommand* c = _model->new_sysex_diff_command (_("delete sysex"));
3777 c->remove (sysex->sysex());
3778 _model->apply_command (*trackview.session(), c);
3785 MidiRegionView::show_verbose_cursor (boost::shared_ptr<NoteType> n) const
3787 using namespace MIDI::Name;
3791 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3793 boost::shared_ptr<MasterDeviceNames> device_names(mtv->get_device_names());
3795 MIDI::Name::PatchPrimaryKey patch_key;
3796 get_patch_key_at(n->time(), n->channel(), patch_key);
3797 name = device_names->note_name(mtv->gui_property(X_("midnam-custom-device-mode")),
3799 patch_key.bank_number,
3800 patch_key.program_number,
3806 snprintf (buf, sizeof (buf), "%d %s\nCh %d Vel %d",
3808 name.empty() ? Evoral::midi_note_name (n->note()).c_str() : name.c_str(),
3809 (int) n->channel() + 1,
3810 (int) n->velocity());
3812 show_verbose_cursor(buf, 10, 20);
3816 MidiRegionView::show_verbose_cursor (string const & text, double xoffset, double yoffset) const
3820 trackview.editor().get_pointer_position (wx, wy);
3825 /* Flip the cursor above the mouse pointer if it would overlap the bottom of the canvas */
3827 double x1, y1, x2, y2;
3828 trackview.editor().verbose_cursor()->canvas_item()->get_bounds (x1, y1, x2, y2);
3830 if ((wy + y2 - y1) > trackview.editor().canvas_height()) {
3831 wy -= (y2 - y1) + 2 * yoffset;
3834 trackview.editor().verbose_cursor()->set (text, wx, wy);
3835 trackview.editor().verbose_cursor()->show ();
3838 /** @param p A session framepos.
3839 * @param grid_frames Filled in with the number of frames that a grid interval is at p.
3840 * @return p snapped to the grid subdivision underneath it.
3843 MidiRegionView::snap_frame_to_grid_underneath (framepos_t p, framecnt_t& grid_frames) const
3845 PublicEditor& editor = trackview.editor ();
3848 Evoral::MusicalTime grid_beats = editor.get_grid_type_as_beats (success, p);
3854 grid_frames = region_beats_to_region_frames (grid_beats);
3856 /* Hack so that we always snap to the note that we are over, instead of snapping
3857 to the next one if we're more than halfway through the one we're over.
3859 if (editor.snap_mode() == SnapNormal && p >= grid_frames / 2) {
3860 p -= grid_frames / 2;
3863 return snap_frame_to_frame (p);
3866 /** Called when the selection has been cleared in any MidiRegionView.
3867 * @param rv MidiRegionView that the selection was cleared in.
3870 MidiRegionView::selection_cleared (MidiRegionView* rv)
3876 /* Clear our selection in sympathy; but don't signal the fact */
3877 clear_selection (false);
3881 MidiRegionView::note_button_release ()
3883 delete _note_player;