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 == 0 || (*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);
2967 if (!_selection.empty()) {
2969 snprintf (buf, sizeof (buf), "Vel %d",
2970 (int) (*_selection.begin())->note()->velocity());
2971 show_verbose_cursor (buf, 10, 10);
2977 MidiRegionView::transpose (bool up, bool fine, bool allow_smush)
2979 if (_selection.empty()) {
2996 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2998 if ((int8_t) (*i)->note()->note() + delta <= 0) {
3002 if ((int8_t) (*i)->note()->note() + delta > 127) {
3009 start_note_diff_command (_("transpose"));
3011 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3012 Selection::iterator next = i;
3014 change_note_note (*i, delta, true);
3022 MidiRegionView::change_note_lengths (bool fine, bool shorter, Evoral::MusicalTime delta, bool start, bool end)
3028 /* grab the current grid distance */
3030 delta = trackview.editor().get_grid_type_as_beats (success, _region->position());
3032 /* XXX cannot get grid type as beats ... should always be possible ... FIX ME */
3033 error << string_compose (_("programming error: %1"), "Grid type not available as beats - TO BE FIXED") << endmsg;
3043 start_note_diff_command (_("change note lengths"));
3045 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3046 Selection::iterator next = i;
3049 /* note the negation of the delta for start */
3051 trim_note (*i, (start ? -delta : 0), (end ? delta : 0));
3060 MidiRegionView::nudge_notes (bool forward)
3062 if (_selection.empty()) {
3066 /* pick a note as the point along the timeline to get the nudge distance.
3067 its not necessarily the earliest note, so we may want to pull the notes out
3068 into a vector and sort before using the first one.
3071 framepos_t ref_point = source_beats_to_absolute_frames ((*(_selection.begin()))->note()->time());
3073 framecnt_t distance;
3075 if (trackview.editor().snap_mode() == Editing::SnapOff) {
3077 /* grid is off - use nudge distance */
3079 distance = trackview.editor().get_nudge_distance (ref_point, unused);
3085 framepos_t next_pos = ref_point;
3088 if (max_framepos - 1 < next_pos) {
3092 if (next_pos == 0) {
3098 trackview.editor().snap_to (next_pos, (forward ? 1 : -1), false);
3099 distance = ref_point - next_pos;
3102 if (distance == 0) {
3106 Evoral::MusicalTime delta = region_frames_to_region_beats (fabs (distance));
3112 start_note_diff_command (_("nudge"));
3114 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3115 Selection::iterator next = i;
3117 change_note_time (*i, delta, true);
3125 MidiRegionView::change_channel(uint8_t channel)
3127 start_note_diff_command(_("change channel"));
3128 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3129 note_diff_add_change (*i, MidiModel::NoteDiffCommand::Channel, channel);
3137 MidiRegionView::note_entered(ArdourCanvas::CanvasNoteEvent* ev)
3139 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3141 pre_enter_cursor = editor->get_canvas_cursor ();
3143 if (_mouse_state == SelectTouchDragging) {
3144 note_selected (ev, true);
3147 show_verbose_cursor (ev->note ());
3151 MidiRegionView::note_left (ArdourCanvas::CanvasNoteEvent*)
3153 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3155 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3156 (*i)->hide_velocity ();
3159 editor->verbose_cursor()->hide ();
3161 if (pre_enter_cursor) {
3162 editor->set_canvas_cursor (pre_enter_cursor);
3163 pre_enter_cursor = 0;
3168 MidiRegionView::patch_entered (ArdourCanvas::CanvasPatchChange* p)
3171 /* XXX should get patch name if we can */
3172 s << _("Bank:") << (p->patch()->bank() + MIDI_BP_ZERO) << '\n'
3173 << _("Program:") << ((int) p->patch()->program()) + MIDI_BP_ZERO << '\n'
3174 << _("Channel:") << ((int) p->patch()->channel() + 1);
3175 show_verbose_cursor (s.str(), 10, 20);
3180 MidiRegionView::patch_left (ArdourCanvas::CanvasPatchChange *)
3182 trackview.editor().verbose_cursor()->hide ();
3183 /* focus will transfer back via the enter-notify event sent to this
3189 MidiRegionView::sysex_entered (ArdourCanvas::CanvasSysEx* p)
3193 show_verbose_cursor (s.str(), 10, 20);
3198 MidiRegionView::sysex_left (ArdourCanvas::CanvasSysEx *)
3200 trackview.editor().verbose_cursor()->hide ();
3201 /* focus will transfer back via the enter-notify event sent to this
3207 MidiRegionView::note_mouse_position (float x_fraction, float /*y_fraction*/, bool can_set_cursor)
3209 Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3210 Editing::MouseMode mm = editor->current_mouse_mode();
3211 bool trimmable = (mm == MouseObject || mm == MouseTimeFX || mm == MouseDraw);
3213 if (trimmable && x_fraction > 0.0 && x_fraction < 0.2) {
3214 editor->set_canvas_cursor (editor->cursors()->left_side_trim);
3215 } else if (trimmable && x_fraction >= 0.8 && x_fraction < 1.0) {
3216 editor->set_canvas_cursor (editor->cursors()->right_side_trim);
3218 if (pre_enter_cursor && can_set_cursor) {
3219 editor->set_canvas_cursor (pre_enter_cursor);
3225 MidiRegionView::set_frame_color()
3229 TimeAxisViewItem::set_frame_color ();
3236 f = ARDOUR_UI::config()->canvasvar_SelectedFrameBase.get();
3237 } else if (high_enough_for_name) {
3238 f= ARDOUR_UI::config()->canvasvar_MidiFrameBase.get();
3243 if (!rect_visible) {
3244 f = UINT_RGBA_CHANGE_A (f, 0);
3247 frame->property_fill_color_rgba() = f;
3251 MidiRegionView::midi_channel_mode_changed(ChannelMode mode, uint16_t mask)
3253 if (mode == ForceChannel) {
3254 mask = 0xFFFF; // Show all notes as active (below)
3257 // Update notes for selection
3258 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3259 (*i)->on_channel_selection_change(mask);
3262 _last_channel_selection = mask;
3263 _last_channel_mode = mode;
3265 _patch_changes.clear ();
3266 display_patch_changes ();
3270 MidiRegionView::instrument_settings_changed ()
3276 MidiRegionView::cut_copy_clear (Editing::CutCopyOp op)
3278 if (_selection.empty()) {
3282 PublicEditor& editor (trackview.editor());
3286 /* XXX what to do ? */
3290 editor.get_cut_buffer().add (selection_as_cut_buffer());
3298 start_note_diff_command();
3300 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3307 note_diff_remove_note (*i);
3317 MidiRegionView::selection_as_cut_buffer () const
3321 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3322 NoteType* n = (*i)->note().get();
3323 notes.insert (boost::shared_ptr<NoteType> (new NoteType (*n)));
3326 MidiCutBuffer* cb = new MidiCutBuffer (trackview.session());
3332 /** This method handles undo */
3334 MidiRegionView::paste (framepos_t pos, float times, const MidiCutBuffer& mcb)
3340 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("MIDI paste @ %1 times %2\n", pos, times));
3342 trackview.session()->begin_reversible_command (_("paste"));
3344 start_note_diff_command (_("paste"));
3346 Evoral::MusicalTime beat_delta;
3347 Evoral::MusicalTime paste_pos_beats;
3348 Evoral::MusicalTime duration;
3349 Evoral::MusicalTime end_point = 0;
3351 duration = (*mcb.notes().rbegin())->end_time() - (*mcb.notes().begin())->time();
3352 paste_pos_beats = absolute_frames_to_source_beats (pos);
3353 beat_delta = (*mcb.notes().begin())->time() - paste_pos_beats;
3354 paste_pos_beats = 0;
3356 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",
3357 (*mcb.notes().begin())->time(),
3358 (*mcb.notes().rbegin())->end_time(),
3359 duration, pos, _region->position(),
3360 paste_pos_beats, beat_delta));
3364 for (int n = 0; n < (int) times; ++n) {
3366 for (Notes::const_iterator i = mcb.notes().begin(); i != mcb.notes().end(); ++i) {
3368 boost::shared_ptr<NoteType> copied_note (new NoteType (*((*i).get())));
3369 copied_note->set_time (paste_pos_beats + copied_note->time() - beat_delta);
3371 /* make all newly added notes selected */
3373 note_diff_add_note (copied_note, true);
3374 end_point = copied_note->end_time();
3377 paste_pos_beats += duration;
3380 /* if we pasted past the current end of the region, extend the region */
3382 framepos_t end_frame = source_beats_to_absolute_frames (end_point);
3383 framepos_t region_end = _region->position() + _region->length() - 1;
3385 if (end_frame > region_end) {
3387 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste extended region from %1 to %2\n", region_end, end_frame));
3389 _region->clear_changes ();
3390 _region->set_length (end_frame - _region->position());
3391 trackview.session()->add_command (new StatefulDiffCommand (_region));
3396 trackview.session()->commit_reversible_command ();
3399 struct EventNoteTimeEarlyFirstComparator {
3400 bool operator() (CanvasNoteEvent* a, CanvasNoteEvent* b) {
3401 return a->note()->time() < b->note()->time();
3406 MidiRegionView::time_sort_events ()
3408 if (!_sort_needed) {
3412 EventNoteTimeEarlyFirstComparator cmp;
3415 _sort_needed = false;
3419 MidiRegionView::goto_next_note (bool add_to_selection)
3421 bool use_next = false;
3423 if (_events.back()->selected()) {
3427 time_sort_events ();
3429 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3430 uint16_t const channel_mask = mtv->channel_selector().get_selected_channels ();
3432 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3433 if ((*i)->selected()) {
3436 } else if (use_next) {
3437 if (channel_mask & (1 << (*i)->note()->channel())) {
3438 if (!add_to_selection) {
3441 note_selected (*i, true, false);
3448 /* use the first one */
3450 if (!_events.empty() && (channel_mask & (1 << _events.front()->note()->channel ()))) {
3451 unique_select (_events.front());
3456 MidiRegionView::goto_previous_note (bool add_to_selection)
3458 bool use_next = false;
3460 if (_events.front()->selected()) {
3464 time_sort_events ();
3466 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3467 uint16_t const channel_mask = mtv->channel_selector().get_selected_channels ();
3469 for (Events::reverse_iterator i = _events.rbegin(); i != _events.rend(); ++i) {
3470 if ((*i)->selected()) {
3473 } else if (use_next) {
3474 if (channel_mask & (1 << (*i)->note()->channel())) {
3475 if (!add_to_selection) {
3478 note_selected (*i, true, false);
3485 /* use the last one */
3487 if (!_events.empty() && (channel_mask & (1 << (*_events.rbegin())->note()->channel ()))) {
3488 unique_select (*(_events.rbegin()));
3493 MidiRegionView::selection_as_notelist (Notes& selected, bool allow_all_if_none_selected)
3495 bool had_selected = false;
3497 time_sort_events ();
3499 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3500 if ((*i)->selected()) {
3501 selected.insert ((*i)->note());
3502 had_selected = true;
3506 if (allow_all_if_none_selected && !had_selected) {
3507 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3508 selected.insert ((*i)->note());
3514 MidiRegionView::update_ghost_note (double x, double y)
3516 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3521 _note_group->w2i (x, y);
3523 PublicEditor& editor = trackview.editor ();
3525 framepos_t const unsnapped_frame = editor.pixel_to_frame (x);
3526 framecnt_t grid_frames;
3527 framepos_t const f = snap_frame_to_grid_underneath (unsnapped_frame, grid_frames);
3529 /* use region_frames... because we are converting a delta within the region
3533 double length = editor.get_grid_type_as_beats (success, unsnapped_frame);
3539 /* note that this sets the time of the ghost note in beats relative to
3540 the start of the source; that is how all note times are stored.
3542 _ghost_note->note()->set_time (absolute_frames_to_source_beats (f + _region->position ()));
3543 _ghost_note->note()->set_length (length);
3544 _ghost_note->note()->set_note (midi_stream_view()->y_to_note (y));
3545 _ghost_note->note()->set_channel (mtv->get_channel_for_add ());
3547 /* the ghost note does not appear in ghost regions, so pass false in here */
3548 update_note (_ghost_note, false);
3550 show_verbose_cursor (_ghost_note->note ());
3554 MidiRegionView::create_ghost_note (double x, double y)
3556 remove_ghost_note ();
3558 boost::shared_ptr<NoteType> g (new NoteType);
3559 _ghost_note = new NoEventCanvasNote (*this, *_note_group, g);
3560 _ghost_note->property_outline_color_rgba() = 0x000000aa;
3561 update_ghost_note (x, y);
3562 _ghost_note->show ();
3567 show_verbose_cursor (_ghost_note->note ());
3571 MidiRegionView::snap_changed ()
3577 create_ghost_note (_last_ghost_x, _last_ghost_y);
3581 MidiRegionView::drop_down_keys ()
3583 _mouse_state = None;
3587 MidiRegionView::maybe_select_by_position (GdkEventButton* ev, double /*x*/, double y)
3589 double note = midi_stream_view()->y_to_note(y);
3591 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3593 uint16_t chn_mask = mtv->channel_selector().get_selected_channels();
3595 if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
3596 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchGreaterThanOrEqual, (uint8_t) floor (note), chn_mask);
3597 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
3598 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchLessThanOrEqual, (uint8_t) floor (note), chn_mask);
3603 bool add_mrv_selection = false;
3605 if (_selection.empty()) {
3606 add_mrv_selection = true;
3609 for (Events::iterator i = e.begin(); i != e.end(); ++i) {
3610 if (_selection.insert (*i).second) {
3611 (*i)->set_selected (true);
3615 if (add_mrv_selection) {
3616 PublicEditor& editor (trackview.editor());
3617 editor.get_selection().add (this);
3622 MidiRegionView::color_handler ()
3624 RegionView::color_handler ();
3626 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3627 (*i)->set_selected ((*i)->selected()); // will change color
3630 /* XXX probably more to do here */
3634 MidiRegionView::enable_display (bool yn)
3636 RegionView::enable_display (yn);
3643 MidiRegionView::show_step_edit_cursor (Evoral::MusicalTime pos)
3645 if (_step_edit_cursor == 0) {
3646 ArdourCanvas::Group* const group = (ArdourCanvas::Group*)get_canvas_group();
3648 _step_edit_cursor = new ArdourCanvas::SimpleRect (*group);
3649 _step_edit_cursor->property_y1() = 0;
3650 _step_edit_cursor->property_y2() = midi_stream_view()->contents_height();
3651 _step_edit_cursor->property_fill_color_rgba() = RGBA_TO_UINT (45,0,0,90);
3652 _step_edit_cursor->property_outline_color_rgba() = RGBA_TO_UINT (85,0,0,90);
3655 move_step_edit_cursor (pos);
3656 _step_edit_cursor->show ();
3660 MidiRegionView::move_step_edit_cursor (Evoral::MusicalTime pos)
3662 _step_edit_cursor_position = pos;
3664 if (_step_edit_cursor) {
3665 double pixel = trackview.editor().frame_to_pixel (region_beats_to_region_frames (pos));
3666 _step_edit_cursor->property_x1() = pixel;
3667 set_step_edit_cursor_width (_step_edit_cursor_width);
3672 MidiRegionView::hide_step_edit_cursor ()
3674 if (_step_edit_cursor) {
3675 _step_edit_cursor->hide ();
3680 MidiRegionView::set_step_edit_cursor_width (Evoral::MusicalTime beats)
3682 _step_edit_cursor_width = beats;
3684 if (_step_edit_cursor) {
3685 _step_edit_cursor->property_x2() = _step_edit_cursor->property_x1() + trackview.editor().frame_to_pixel (region_beats_to_region_frames (beats));
3689 /** Called when a diskstream on our track has received some data. Update the view, if applicable.
3690 * @param w Source that the data will end up in.
3693 MidiRegionView::data_recorded (boost::weak_ptr<MidiSource> w)
3695 if (!_active_notes) {
3696 /* we aren't actively being recorded to */
3700 boost::shared_ptr<MidiSource> src = w.lock ();
3701 if (!src || src != midi_region()->midi_source()) {
3702 /* recorded data was not destined for our source */
3706 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (&trackview);
3708 boost::shared_ptr<MidiBuffer> buf = mtv->midi_track()->get_gui_feed_buffer ();
3710 BeatsFramesConverter converter (trackview.session()->tempo_map(), mtv->midi_track()->get_capture_start_frame (0));
3712 framepos_t back = max_framepos;
3714 for (MidiBuffer::iterator i = buf->begin(); i != buf->end(); ++i) {
3715 Evoral::MIDIEvent<MidiBuffer::TimeType> const ev (*i, false);
3716 assert (ev.buffer ());
3718 if(ev.is_channel_event()) {
3719 if (_last_channel_mode == FilterChannels) {
3720 if(((uint16_t(1) << ev.channel()) & _last_channel_selection) == 0) {
3726 /* ev.time() is in session frames, so (ev.time() - converter.origin_b()) is
3727 frames from the start of the source, and so time_beats is in terms of the
3731 Evoral::MusicalTime const time_beats = converter.from (ev.time () - converter.origin_b ());
3733 if (ev.type() == MIDI_CMD_NOTE_ON) {
3735 boost::shared_ptr<NoteType> note (
3736 new NoteType (ev.channel(), time_beats, 0, ev.note(), ev.velocity())
3739 add_note (note, true);
3741 /* fix up our note range */
3742 if (ev.note() < _current_range_min) {
3743 midi_stream_view()->apply_note_range (ev.note(), _current_range_max, true);
3744 } else if (ev.note() > _current_range_max) {
3745 midi_stream_view()->apply_note_range (_current_range_min, ev.note(), true);
3748 } else if (ev.type() == MIDI_CMD_NOTE_OFF) {
3749 resolve_note (ev.note (), time_beats);
3755 midi_stream_view()->check_record_layers (region(), back);
3759 MidiRegionView::trim_front_starting ()
3761 /* Reparent the note group to the region view's parent, so that it doesn't change
3762 when the region view is trimmed.
3764 _temporary_note_group = new ArdourCanvas::Group (*group->property_parent ());
3765 _temporary_note_group->move (group->property_x(), group->property_y());
3766 _note_group->reparent (*_temporary_note_group);
3770 MidiRegionView::trim_front_ending ()
3772 _note_group->reparent (*group);
3773 delete _temporary_note_group;
3774 _temporary_note_group = 0;
3776 if (_region->start() < 0) {
3777 /* Trim drag made start time -ve; fix this */
3778 midi_region()->fix_negative_start ();
3783 MidiRegionView::edit_patch_change (ArdourCanvas::CanvasPatchChange* pc)
3785 PatchChangeDialog d (&_source_relative_time_converter, trackview.session(), *pc->patch (), instrument_info(), Gtk::Stock::APPLY);
3787 d.set_position (Gtk::WIN_POS_MOUSE);
3789 if (d.run () != Gtk::RESPONSE_ACCEPT) {
3793 change_patch_change (pc->patch(), d.patch ());
3797 MidiRegionView::delete_sysex (CanvasSysEx* sysex)
3799 MidiModel::SysExDiffCommand* c = _model->new_sysex_diff_command (_("delete sysex"));
3800 c->remove (sysex->sysex());
3801 _model->apply_command (*trackview.session(), c);
3808 MidiRegionView::show_verbose_cursor (boost::shared_ptr<NoteType> n) const
3811 snprintf (buf, sizeof (buf), "%s (%d) Chn %d\nVel %d",
3812 Evoral::midi_note_name (n->note()).c_str(),
3814 (int) n->channel() + 1,
3815 (int) n->velocity());
3817 show_verbose_cursor (buf, 10, 20);
3821 MidiRegionView::show_verbose_cursor (string const & text, double xoffset, double yoffset) const
3825 trackview.editor().get_pointer_position (wx, wy);
3830 /* Flip the cursor above the mouse pointer if it would overlap the bottom of the canvas */
3832 double x1, y1, x2, y2;
3833 trackview.editor().verbose_cursor()->canvas_item()->get_bounds (x1, y1, x2, y2);
3835 if ((wy + y2 - y1) > trackview.editor().canvas_height()) {
3836 wy -= (y2 - y1) + 2 * yoffset;
3839 trackview.editor().verbose_cursor()->set (text, wx, wy);
3840 trackview.editor().verbose_cursor()->show ();
3843 /** @param p A session framepos.
3844 * @param grid_frames Filled in with the number of frames that a grid interval is at p.
3845 * @return p snapped to the grid subdivision underneath it.
3848 MidiRegionView::snap_frame_to_grid_underneath (framepos_t p, framecnt_t& grid_frames) const
3850 PublicEditor& editor = trackview.editor ();
3853 Evoral::MusicalTime grid_beats = editor.get_grid_type_as_beats (success, p);
3859 grid_frames = region_beats_to_region_frames (grid_beats);
3861 /* Hack so that we always snap to the note that we are over, instead of snapping
3862 to the next one if we're more than halfway through the one we're over.
3864 if (editor.snap_mode() == SnapNormal && p >= grid_frames / 2) {
3865 p -= grid_frames / 2;
3868 return snap_frame_to_frame (p);
3871 /** Called when the selection has been cleared in any MidiRegionView.
3872 * @param rv MidiRegionView that the selection was cleared in.
3875 MidiRegionView::selection_cleared (MidiRegionView* rv)
3881 /* Clear our selection in sympathy; but don't signal the fact */
3882 clear_selection (false);
3886 MidiRegionView::note_button_release ()
3888 delete _note_player;